package hep.io.root.reps;

import hep.io.root.core.AbstractRootObject;
import hep.io.root.core.NameMangler;
import hep.io.root.core.RootInput;
import hep.io.root.interfaces.TBasket;
import hep.io.root.interfaces.TBranch;
import hep.io.root.interfaces.TLeaf;
import hep.io.root.interfaces.TObjArray;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;


/**
 * @author Tony Johnson (tonyj@slac.stanford.edu)
 * @version $Id: TBranchRep.java,v 1.6 2004/07/26 18:40:22 tonyj Exp $
 */
public abstract class TBranchRep extends AbstractRootObject implements hep.io.root.interfaces.TBranch
{
   private HashMap mangledMap;
   private HashMap map;
   private RootInput rin;
   private TBasket curBasket;
   private int curIndex;
   private int fWriteBasket;
   private int[] fBasketBytes;
   private int[] fBasketEntry;
   private int[] fBasketSeek;

   private int[] saveSpace(int[] in, int size)
   {
      if (size == 0) return null;
      if (size == in.length) return in;
      int[] result = new int[size];
      System.arraycopy(in,0,result,0,size);
      return result;
   }
   public TBranch getBranchForMangledName(String name)
   {
      if (map == null)
         buildMap();
      return (TBranch) mangledMap.get(name);
   }

   public TBranch getBranchForName(String name)
   {
      if (map == null)
         buildMap();
      return (TBranch) map.get(name);
   }

   public int getNEntries()
   {
      return (int) getEntries();
   }

   public RootInput setPosition(TLeaf leaf, int index) throws IOException
   {
      int i = findBasketForIndex(index);
      TBasket basket = getBasket(i);
      return basket.setPosition(index, i == 0 ? 0 : getBasketEntry()[i], leaf);
   }

   public void read(RootInput in) throws IOException
   {
      super.read(in);
      // Clean-up unnecessarily large arrays
      fBasketBytes = saveSpace(fBasketBytes,fWriteBasket);
      fBasketEntry = saveSpace(fBasketEntry,fWriteBasket+1);
      fBasketSeek = saveSpace(fBasketSeek,fWriteBasket);
      
      rin = in.getTop();

      // The leaves need to know which branch they are on
      TObjArray leaves = getLeaves();
      if (leaves != null)
      {
         for (int i = 0; i < leaves.size(); i++)
         {
            TLeaf leaf = (TLeaf) leaves.get(i);
            leaf.setBranch(this);
         }
      }
      curIndex = -1;
   }

   private TBasket getBasket(int index) throws IOException
   {
      try
      {
         TObjArray baskets = getBaskets();
         TBasket basket = (TBasket) baskets.get(index);
         if (basket != null) return basket;

         if (index == curIndex) return curBasket;
         // Ok read the TBasket
         rin.setPosition(getBasketSeek()[index]);

         basket = (TBasket) rin.readObject("TBasket");

         int len = getEntryOffsetLen();
         if (len > 0) basket.readEntryOffsets(len);
         
         curIndex = index;
         return curBasket = basket;
      }
      catch (IOException x)
      {
         curIndex = -1;
         throw x;
      }
   }

   private void buildMap()
   {
      NameMangler nameMangler = NameMangler.instance();
      map = new HashMap();
      mangledMap = new HashMap();

      TObjArray branches = getBranches();
      int size = branches.size();
      for (int i = 0; i < size; i++)
      {
         TBranch b = (TBranch) branches.get(i);
         String bName = b.getName();
         int pos = bName.indexOf('[');
         if (pos > 0)
            bName = bName.substring(0, pos);

         pos = bName.lastIndexOf('.');
         if (pos > 0)
            bName = bName.substring(pos + 1);
         map.put(bName, b);
         mangledMap.put(nameMangler.mangleMember(bName), b);
      }
   }

   private int findBasketForIndex(int index)
   {
      // Figure out which basket the entry is in
      if ((index < 0) || (index >= getNEntries()))
         throw new ArrayIndexOutOfBoundsException("index=" + index);

      int[] entries = getBasketEntry();
      if (entries == null) return 0;
      // TODO: we should do a binary search here
      int n = getWriteBasket();
      for (int i = 0; i < n; i++)
      {
         if ((index >= entries[i]) && (index < entries[i + 1])) return i;
      }
      return n;
      // binary search has problems if arrays contain trailing zeros
//      int result = Arrays.binarySearch(entries,index);
//      if (result < 0) result = - result - 2;
//      return result;      
   }
}
