package hep.io.root;

import hep.io.root.core.*;
import hep.io.root.daemon.DaemonInputStream;
import hep.io.root.interfaces.*;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;


/**
 * A class for reading root files.
 * @author Tony Johnson (tonyj@slac.stanford.edu)
 * @version $Id: RootFileReader.java,v 1.19 2004/05/30 06:40:37 tonyj Exp $
 */
public class RootFileReader implements TFile
{
   private static boolean welcome = false;
   private static boolean debug = System.getProperty("debugRoot") != null;
   private ClassLoader classLoader;
   private java.util.Date fDatimeC;
   private java.util.Date fDatimeM;
   private RootClass fileClass;
   private RootClassFactory factory;
   private RootInput in;
   private String name;
   private String title;
   private TDirectory dir;
   private TKey streamerInfo;
   private int fNbytesKeys;
   private int fNbytesName;
   private int fSeekDir;
   private int fSeekKeys;
   private int fSeekParent;
   private int fVersion;
 
   /** 
    * Open a file specified by URL for reading.
    * The URL may point to
    * <ul>
    * <li>a file, 
    * <li>to a rootd daemon (root: protocol)
    * <li>to any other data (not yet implemented)
    * </ul>
    * source. In the last case the file will be buffered in memory prior to 
    * reading, so this method should only be used for small files.
    * <p>
    * The options argument is used to pass options to the file reader. Supported
    * option include:
    * <ul>
    * <li>scheme -- The authorization scheme, currently supported UsrPwd, Anonymous
    * <li>user -- The user name to use
    * <li>password -- The password to use
    * </ul>
    */
   public RootFileReader(URL url, Map options) throws IOException
   {
      if (url.getProtocol().equals("file")) init(new File(url.getFile()));
      else
      {
         URLConnection connection = url.openConnection();
         
         if (options != null)
         {
            Object user = options.get("user");
            if (user != null) connection.setRequestProperty("user", user.toString());

            Object pass = options.get("password");
            if (pass != null) connection.setRequestProperty("password", pass.toString());         

            Object mode = options.get("scheme");
            if (mode != null) connection.setRequestProperty("scheme", mode.toString()); 
            
            Object bufferSize = options.get("bufferSize");
            if (bufferSize != null) connection.setRequestProperty("bufferSize", bufferSize.toString()); 
         }
         
         InputStream source = connection.getInputStream();
         if (source instanceof DaemonInputStream) init(new RootDaemonInputStream((DaemonInputStream) source,this));
         else
         {
            throw new IOException("Unsupported protocol: "+url.getProtocol() );
//            if ((source.readByte() != 'r') || (source.readByte() != 'o') || (source.readByte() != 'o') || (source.readByte() != 't'))
//               throw new IOException("Not a root file");
//            ByteArrayOutputStream out = new ByteArrayOutputStream();
//            byte[] buffer = new byte[8192];
//            for (;;)
//            {
//               int l = source.read(buffer);
//               if (l<0) break;
//               out.write(buffer,0,l);
//            }
//            source.close();
//            out.close();
//            init(new RootInputStream(new RootByteArrayInputStream(out.toByteArray(),0),this);
         }
      }
   }
   /**
    * Open a file specified by URL for reading with the default options.
    */
   public RootFileReader(URL url) throws IOException
   {
      this(url,null);
   }

   /**
    * Open a root file for reading.
    * The DefaultClassFactory will be used for creating classes
    * @param file The name of the file to open
    * @throws IOException If the file cannot be opened
    */
   public RootFileReader(String file) throws IOException
   {
      init(new File(file));
   }

   /**
    * Open a root file for reading
    * @param file The file to open
    */
   public RootFileReader(File file) throws IOException
   {
      init(file);
   }
   private void init(File file) throws IOException
   {
      RootRandomAccessFile raf = new RootRandomAccessFile(file, this);
      RootInput in = raf;
      if (System.getProperty("useNIO") != null) in = new FastInputStream(this, raf);
      init(in);
   }
   private void init(RootInput in) throws IOException
   {
      try
      {
         this.in = in;
         if (!welcome) welcome();
         factory = new DefaultClassFactory(this);
         fileClass = factory.create("TFile");

         fVersion = in.readInt();
         if (fVersion < 30006)
            throw new IOException("hep.io.root package cannot read files created by Root before release 3.00/6 (" + fVersion + ")");
         if (debug)
            System.out.println("version=" + fVersion);

         int fBEGIN = in.readInt();
         int fEND = in.readInt();
         int fSeekFree = in.readInt();
         int fNbytesFree = in.readInt();
         int nfree = in.readInt();
         fNbytesName = in.readInt();

         int fUnits = in.readByte();
         int fCompress = in.readInt();
         int fSeekInfo = in.readInt();
         int fNBytesInfo = in.readInt();

         in.setPosition(fBEGIN);

         int Nbytes = in.readInt();
         int Version = in.readShort();
         int ObjLen = in.readInt();
         fDatimeC = fDatimeM = ((TDatime) in.readObject("TDatime")).getDate();

         int KeyLen = in.readShort();
         int Cycle = in.readShort();
         int SeekKey = in.readInt();
         int SeekPdir = in.readInt();

         String className = in.readObject("TString").toString();
         name = in.readObject("TString").toString();
         title = in.readObject("TString").toString();

         in.setPosition(fBEGIN + fNbytesName); // This should get us to the directory
         dir = (TDirectory) in.readObject("TDirectory");

         // Ok, now read the StreamerInfo key, if present
         if (fSeekInfo != 0)
         {
            in.setPosition(fSeekInfo);
            streamerInfo = (TKey) in.readObject("TKey");
            this.factory = new FileClassFactory(streamerInfo, factory, this);
         }
      }
      catch (RootClassNotFound x)
      {
         x.printStackTrace();
         throw new IOException("Root Class Not Found: " + x.getClassName());
      }
   }

   public int getBits()
   {
      return 0;
   }

   public RootClassFactory getClassFactory()
   {
      return factory;
   }

   /**
    * Set the classloader to use for checking for interfaces and loading proxies.
    */
   public void setClassLoader(ClassLoader loader)
   {
      classLoader = loader;
   }

   public ClassLoader getClassLoader()
   {
      return classLoader;
   }

   public java.util.Date getDatimeC()
   {
      return fDatimeC;
   }

   public java.util.Date getDatimeM()
   {
      return fDatimeM;
   }

   public RootClassFactory getFactory()
   {
      return factory;
   }

   public TKey getKey(int index)
   {
      return dir.getKey(index);
   }

   public TKey getKey(String name)
   {
      return dir.getKey(name);
   }

   public TKey getKey(String name, int cycle)
   {
      return dir.getKey(name, cycle);
   }

   public TKey getKeyForTitle(String name)
   {
      return dir.getKeyForTitle(name);
   }

   public String getName()
   {
      return name;
   }

   public int getNbytesKeys()
   {
      return fNbytesKeys;
   }

   public int getNbytesName()
   {
      return fNbytesName;
   }

   /**
    * Get the class of this object
    * @return The RootClass for this object
    */
   public RootClass getRootClass()
   {
      return fileClass;
   }

   public int getSeekDir()
   {
      return fSeekDir;
   }

   public int getSeekKeys()
   {
      return fSeekKeys;
   }

   public int getSeekParent()
   {
      return fSeekParent;
   }

   public String getTitle()
   {
      return title;
   }

   public int getUniqueID()
   {
      return 0;
   }

   public int getVersion()
   {
      return fVersion;
   }

   public void close() throws IOException
   {
      in.close();
   }

   /**
    * Get the object associated with a particular key
    */
   public Object get(String name) throws IOException, RootClassNotFound
   {
      TKey key = getKey(name);
      return key.getObject();
   }

   public int nKeys()
   {
      return dir.nKeys();
   }

   /**
    * Get the StreamerInfo
    */
   public List streamerInfo() throws IOException
   {
      try
      {
         return (List) streamerInfo.getObject();
      }
      catch (RootClassNotFound x)
      {
         throw new IOException("Root Class Not Found during IO: " + x);
      }
   }

   public TKey streamerInfoKey()
   {
      return streamerInfo;
   }

   private static void welcome()
   {
      System.out.println("Root IO for Java, part of the FreeHEP library: http://java.freehep.org");
      System.out.println("Please report all bugs/problems to tonyj@slac.stanford.edu");
      System.out.println("Version $Id: RootFileReader.java,v 1.19 2004/05/30 06:40:37 tonyj Exp $");
      welcome = true;
   }
}
