package hep.io.mcfio;

import hep.io.xdr.*;

import java.io.*;


/**
 * A class for writing MCFIO files.
 *
 * @author Tony Johnson (tonyj@slac.stanford.edu)
 * @version $Id: MCFIOWriter.java,v 1.3 2003/09/16 23:11:54 tonyj Exp $
 */
public class MCFIOWriter implements MCFIOConstants
{
   protected static boolean compatibilityMode = true;
   private FileHeader fileHeader;
   private WriteEventTable eventTable;
   private XDRRandomAccessFile xdr;

   /**
    * Open an MCFIO file for writing.
    * @param title The title of the file
    * @param comment The comment associate with the file
    * @param numevts The number of events expected to be written
    * @param blockIds The user defined blocks that will be included in this file
    * @param blockNames Names for the blocks defined by blockIds
    */
   public MCFIOWriter(String file, String title, String comment, int numevts, int[] blockIds, String[] blockNames) throws IOException
   {
      if (compatibilityMode)
      {
         if (title.length() > MCF_XDR_F_TITLE_LENGTH)
            throw new MCFIOException("Title too long");
         if (comment.length() > MCF_XDR_F_TITLE_LENGTH)
            throw new MCFIOException("Comment too long");
      }
      xdr = new BufferedRandomAccessFile(file);

      // Leave space for the fileHeader and eventTable
      fileHeader = new FileHeader(getPosition(), title, comment, numevts, blockIds, blockNames);
      xdr.seek(fileHeader.getLength());
      fileHeader.setFirstTable(getPosition());
      eventTable = new WriteEventTable(getPosition());
      xdr.seek(getPosition() + eventTable.getLength());
   }

   /**
    * Set compatibility mode. In the old C implementation
    * there were various limits, such as the length of
    * some strings and the maximum number of particles that
    * could be written to stdhep events. There is no reason
    * to have any of these limits in the Java implementation,
    * but if we ignore them then the resulting file might not
    * be readable by the old C implementation. By default this
    * implementation enforces the same limits as the C implementation,
    * but setting compatibilyMode to false will turn off this
    * limitation.
    */
   public void setCompatibilityMode(boolean mode)
   {
      compatibilityMode = mode;
   }

   public void close() throws IOException
   {
      eventTable.commit(xdr);
      fileHeader.commit(xdr);
      xdr.close();
   }

   /**
    * Write an event
    */
   public void write(MCFIOEvent event) throws IOException
   {
      if (eventTable.isFull())
         eventTable = eventTable.newTable();
      eventTable.add(event);
   }

   private int getPosition() throws IOException
   {
      // note: Java supports file positions specified as longs,
      // but MCFIO only supports ints.
      return (int) xdr.getFilePointer();
   }

   private class WriteEventTable extends EventTable
   {
      WriteEventTable(int pos) throws IOException
      {
         super(pos);
      }

      void add(MCFIOEvent event) throws IOException
      {
         super.add(event, getPosition());

         // Write the event header
         int nBlocks = event.getNBlocks();
         int size = 4 * (12 + (nBlocks * 2));
         xdr.writeInt(EVENTHEADER);
         xdr.writeInt(size);
         xdr.writeString("1.00");

         xdr.writeInt(event.getEventNumber());
         xdr.writeInt(event.getStoreNumber());
         xdr.writeInt(event.getRunNumber());
         xdr.writeInt(event.getTrigMask());
         xdr.writeInt(nBlocks);
         xdr.writeInt(nBlocks);

         int[] ptrs = new int[nBlocks];
         int[] ids = new int[nBlocks];
         int pos = getPosition() + (4 * (2 + (nBlocks * 2)));
         for (int i = 0; i < nBlocks; i++)
         {
            ids[i] = event.getBlockID(i);
            ptrs[i] = pos;
            pos += event.getBlock(i).getLength();
         }
         xdr.writeIntArray(ids);
         xdr.writeIntArray(ptrs);

         // Write the user blocks
         for (int i = 0; i < nBlocks; i++)
         {
            event.getBlock(i).write(xdr);
         }
      }

      void commit(XDRRandomAccessFile xdr) throws IOException
      {
         fileHeader.incrementNumEvents(numevts());
         super.commit(xdr);
      }

      WriteEventTable newTable() throws IOException
      {
         // Commit the current table to disk
         int currentPos = getPosition();
         setNextTable(currentPos);
         commit(xdr);

         // leave space for the next event table
         xdr.seek(currentPos + getLength());
         return new WriteEventTable(currentPos);
      }
   }
}
