package hep.aida.ref.root;

import hep.aida.ITuple;

import hep.aida.ref.tuple.AbstractTuple;

import hep.io.root.RootClassNotFound;
import hep.io.root.interfaces.*;

import hep.tuple.Cursor;

import hep.tuple.interfaces.FTuple;
import hep.tuple.interfaces.FTupleColumn;
import hep.tuple.interfaces.FTupleCursor;

import java.io.IOException;
import java.util.List;

import org.freehep.util.Value;


/**
 * A Tuple based on a Root Ttree().
 * No support for subFolders yet.
 * @author tonyj
 */
class TTreeTuple extends AbstractTuple implements FTuple
{
   private Cursor cursor;
   private TKey key;
   private Value theValue;
   private TLeafColumn[] columns;
   private int nCol;

   TTreeTuple(TKey key, String name)
   {
      super(name);
      this.setTitle(key.getTitle());
      this.key = key;
      nCol = tree().getLeaves().size();
      cursor = new Cursor(0, rows(), true);
      columns = new TLeafColumn[nCol];
      theValue = new Value();
      
      TLeafFolderColumn subTuple = null;
      TLeaf lastDim = null;
      
      int n = 0;
      for (int i = 0; i < nCol; i++)
      {
         TLeaf leaf = (TLeaf) tree().getLeaves().get(i);
         int nDim = leaf.getArrayDim();
         if (nDim == 0)
         {
            if (leaf instanceof TLeafI)
               columns[n++] = new TLeafIColumn((TLeafI) leaf);
            else if (leaf instanceof TLeafF)
               columns[n++] = new TLeafFColumn((TLeafF) leaf);
            else if (leaf instanceof TLeafD)
               columns[n++] = new TLeafDColumn((TLeafD) leaf);
            else if (leaf instanceof TLeafB)
               columns[n++] = new TLeafBColumn((TLeafB) leaf);
            else if (leaf instanceof TLeafC)
               columns[n++] = new TLeafCColumn((TLeafC) leaf);
            else
            {
               System.out.println("Ignored column " + leaf.getName() + " of type " + leaf.getClass());
            }
         }
         else
         {
            // If this is a variable dimension attempt to group items together
            TLeafI dim = (TLeafI) leaf.getLeafCount();
            if (dim == null) columns[n++] = new TLeafObjectColumn(leaf);
            else if (dim == lastDim)
            {
               subTuple.addColumn(leaf);
            }
            else
            {
               subTuple = new TLeafFolderColumn(dim);
               columns[n++] = subTuple;
               subTuple.addColumn(leaf);
               lastDim = dim;
            }
         }
      }
      nCol = n;
   }

   public boolean getBoolean(int col) throws ClassCastException
   {
      columns[col].getValue(cursor.row(), theValue);
      return theValue.getBoolean();
   }

   public byte getByte(int col) throws ClassCastException
   {
      columns[col].getValue(cursor.row(), theValue);
      return theValue.getByte();
   }

   public char getChar(int col) throws ClassCastException
   {
      columns[col].getValue(cursor.row(), theValue);
      return theValue.getChar();
   }

   public double getDouble(int col) throws ClassCastException
   {
      columns[col].getValue(cursor.row(), theValue);
      return theValue.getDouble();
   }

   public float getFloat(int col) throws ClassCastException
   {
      columns[col].getValue(cursor.row(), theValue);
      return theValue.getFloat();
   }

   public boolean isInMemory()
   {
      return false;
   }

   public int getInt(int col) throws ClassCastException
   {
      columns[col].getValue(cursor.row(), theValue);
      return theValue.getInt();
   }

   public long getLong(int col) throws ClassCastException
   {
      columns[col].getValue(cursor.row(), theValue);
      return theValue.getLong();
   }

   public Object getObject(int col) throws ClassCastException
   {
      columns[col].getValue(cursor.row(), theValue);
      return theValue.getObject();
   }

   public void setRow(int row) throws IllegalArgumentException
   {
      cursor.setRow(row);
   }

   public short getShort(int col) throws ClassCastException
   {
      columns[col].getValue(cursor.row(), theValue);
      return theValue.getShort();
   }

   public String getString(int col) throws ClassCastException
   {
      columns[col].getValue(cursor.row(), theValue);
      return theValue.getString();
   }

   public ITuple getTuple(int col)
   {
      columns[col].getValue(cursor.row(), theValue);
      return (ITuple) theValue.getObject();
   }
   public FTuple tuple(int col)
   {
      columns[col].getValue(cursor.row(), theValue);
      return (FTuple) theValue.getObject();
   }
   
   public void close() {}

   public FTupleColumn column(int index)
   {
      return columns[index];
   }

   public FTupleColumn columnByName(String name)
   {
      int n = columnIndexByName(name);
      if (n < 0)
         return null;
      return column(n);
   }

      public int columnIndexByName(String name)
      {
         for (int i = 0; i < columns.length; i++)
         {
            if (columns[i].name().equals(name))
               return i;
         }
         return -1;
      }

   public double columnMax(int index) throws IllegalArgumentException
   {
      columns[index].maxValue(theValue);
      return theValue.getDouble();
   }

   public double columnMean(int index) throws IllegalArgumentException
   {
      columns[index].meanValue(theValue);
      return theValue.getDouble();
   }

   public double columnMin(int index) throws IllegalArgumentException
   {
      columns[index].minValue(theValue);
      return theValue.getDouble();
   }

   public String columnName(int index) throws IllegalArgumentException
   {
      return columns[index].name();
   }
    public String[] columnNames() throws java.lang.IllegalArgumentException {
        String[] names = new String[columns.length];
        for ( int i = 0; i < names.length; i++ )
            names[i] = columns[i].name();
        return names;
    }
    

   public double columnRms(int index) throws IllegalArgumentException
   {
      columns[index].rmsValue(theValue);
      return theValue.getDouble();
   }

   public Class columnType(int index) throws IllegalArgumentException
   {
      return columns[index].type();
   }

   public Class[] columnTypes() throws java.lang.IllegalArgumentException {
        Class[] types = new Class[columns.length];
        for ( int i = 0; i < types.length; i++ )
            types[i] = columns[i].type();
        return types;
    }

   public void columnValue(int index, FTupleCursor cursor, Value value)
   {
      columns[index].getValue(cursor.row(), value);
   }

   public int columns()
   {
      return nCol;
   }

   public FTupleCursor cursor() throws IllegalStateException
   {
      return new Cursor(0, rows(), true);
   }

   public int findColumn(String name) throws IllegalArgumentException
   {
      int n = columnIndexByName(name);
      if (n < 0)
         throw new IllegalArgumentException("Unknown column " + name);
      return n;
   }

   public boolean next()
   {
      return cursor.next();
   }

   public int rows()
   {
      return (int) tree().getEntries();
   }

   public void skip(int n) throws IllegalArgumentException
   {
      cursor.skip(n);
   }

   public void start()
   {
      cursor.start();
   }

   public boolean supportsMultipleCursors()
   {
      return true;
   }

   public boolean supportsRandomAccess()
   {
      return true;
   }

   private TTree tree()
   {
      try
      {
         return (TTree) key.getObject();
      }
      catch (RootClassNotFound x)
      {
         throw new RuntimeException("Root Class Not Found " + x.getClassName());
      }
      catch (IOException x)
      {
         throw new RuntimeException("IOException reading root file");
      }
   }
}
