package hep.io.root.util;

import hep.io.root.interfaces.*;
import jas.hist.*;

class RootHistogramAdapter
{
   static DataSource create(TH1 rootHisto)
   {
      if      (rootHisto instanceof TH1F) return new Root1FHistogramAdapter((TH1F) rootHisto);
      else if (rootHisto instanceof TH1D) return new Root1DHistogramAdapter((TH1D) rootHisto);
      else if (rootHisto instanceof TH2F) return new Root2FHistogramAdapter((TH2F) rootHisto);
      else if (rootHisto instanceof TH2D) return new Root2DHistogramAdapter((TH2D) rootHisto);
      else return null;
   }
   
   private static class Root1DHistogramAdapter extends Root1HistogramAdapter
   {
      private TH1D th1d;
      Root1DHistogramAdapter(TH1D rootHisto)
      {
         super(rootHisto);
         this.th1d = rootHisto;
      }
      public double[][] rebin(int bins, double p2, double p3, boolean p4, boolean p5)
      {
         double[] data = new double[bins];
         double[] array = th1d.getArray();
         for (int i=0; i<bins; i++) data[i] = array[i+1];
         double[] darray = rootHisto.getSumw2();
         if (darray != null && darray.length>0)
         {
            double[] error = new double[bins];
            for (int i=0; i<bins; i++) error[i] = Math.sqrt(darray[i+1]);
            return new double[][]
            { data, error };
         }
         else
         {
            return new double[][]
            {data};
         }
      }
   }
   private static class Root1FHistogramAdapter extends Root1HistogramAdapter
   {
      private TH1F th1f;
      Root1FHistogramAdapter(TH1F rootHisto)
      {
         super(rootHisto);
         this.th1f = rootHisto;
      }
      public double[][] rebin(int bins, double p2, double p3, boolean p4, boolean p5)
      {
         double[] data = new double[bins];
         float[] array = th1f.getArray();
         for (int i=0; i<bins; i++) data[i] = array[i+1];
         double[] darray = rootHisto.getSumw2();
         if (darray != null && darray.length>0)
         {
            double[] error = new double[bins];
            for (int i=0; i<bins; i++) error[i] = Math.sqrt(darray[i+1]);
            return new double[][]
            { data, error };
         }
         else
         {
            return new double[][]
            {data};
         }
      }
   }
   private static abstract class Root1HistogramAdapter implements Rebinnable1DHistogramData, HasStatistics, Statistics
   {
      protected TH1 rootHisto;
      Root1HistogramAdapter(TH1 rootHisto)
      {
         this.rootHisto = rootHisto;
      }
      public double getMin()
      {
         return rootHisto.getXaxis().getXmin();
      }
      public double getMax()
      {
         return rootHisto.getXaxis().getXmax();
      }
      public int getBins()
      {
         return rootHisto.getXaxis().getNbins();
      }
      public boolean isRebinnable()
      {
         return false;
      }
      public int getAxisType()
      {
         return DOUBLE;
      }
      public String[] getAxisLabels()
      {
         return null;
      }
      public String getTitle()
      {
         return rootHisto.getTitle();
      }
      public Statistics getStatistics()
      {
         return this;
      }
      public String[] getStatisticNames()
      {
         return new String[]
         {"Entries", "Mean", "RMS"};
      }
      private double mean()
      {
         return rootHisto.getTsumwx()/rootHisto.getTsumw();
      }
      public double rms()
      {
         double sumw = rootHisto.getTsumw();
         double sumwx = rootHisto.getTsumwx();
         double sumwx2 = rootHisto.getTsumwx2();
         return Math.sqrt(sumwx2/sumw - sumwx*sumwx/sumw/sumw);
      }
      public double getStatistic(String name)
      {
         if      (name.equals("Entries")) return rootHisto.getEntries();
         else if (name.equals("Mean")) return mean();
         else if (name.equals("RMS")) return rms();
         else return 0;
      }
   }
   private static class Root2DHistogramAdapter extends Root2HistogramAdapter
   {
      private TH2D th2d;
      Root2DHistogramAdapter(TH2D rootHisto)
      {
         super(rootHisto);
         this.th2d = rootHisto;
      }
      public double[][][] rebin(int xbins, double p2, double p3,
      int ybins, double p6, double p7,
      boolean p4, boolean p5, boolean p8)
      {
         double[][] data = new double[xbins][ybins];
         double[] array = th2d.getArray();
         for (int i=0; i<xbins; i++)
            for (int j=0; j<ybins; j++)
               data[i][j] = array[1+i+(j+1)*(xbins+2)];
         double[] darray = rootHisto.getSumw2();
         if (darray != null && darray.length > 0)
         {
            double[][] error = new double[xbins][ybins];
            for (int i=0; i<xbins; i++)
               for (int j=0; j<ybins; j++)
                  error[i][j] = darray[1+j+(i+1)*(ybins+2)];
            return new double[][][]
            { data, error };
         }
         else
         {
            return new double[][][]
            { data };
         }
      }
   }
   private static class Root2FHistogramAdapter extends Root2HistogramAdapter
   {
      private TH2F th2f;
      Root2FHistogramAdapter(TH2F rootHisto)
      {
         super(rootHisto);
         this.th2f = rootHisto;
      }
      public double[][][] rebin(int xbins, double p2, double p3,
      int ybins, double p6, double p7,
      boolean p4, boolean p5, boolean p8)
      {
         double[][] data = new double[xbins][ybins];
         float[] array = th2f.getArray();
         for (int i=0; i<xbins; i++)
            for (int j=0; j<ybins; j++)
               data[i][j] = array[1+i+(j+1)*(xbins+2)];
         double[] darray = rootHisto.getSumw2();
         if (darray != null && darray.length > 0)
         {
            double[][] error = new double[xbins][ybins];
            for (int i=0; i<xbins; i++)
               for (int j=0; j<ybins; j++)
                  error[i][j] = darray[1+j+(i+1)*(ybins+2)];
            return new double[][][]
            { data, error };
         }
         else
         {
            return new double[][][]
            { data };
         }
      }
   }
   private static abstract class Root2HistogramAdapter implements Rebinnable2DHistogramData, HasStatistics, Statistics
   {
      protected TH2 rootHisto;
      
      Root2HistogramAdapter(TH2 rootHisto)
      {
         this.rootHisto = rootHisto;
      }
      public double getXMin()
      {
         return rootHisto.getXaxis().getXmin();
      }
      public double getXMax()
      {
         return rootHisto.getXaxis().getXmax();
      }
      public int getXBins()
      {
         return rootHisto.getXaxis().getNbins();
      }
      public double getYMin()
      {
         return rootHisto.getYaxis().getXmin();
      }
      public double getYMax()
      {
         return rootHisto.getYaxis().getXmax();
      }
      public int getYBins()
      {
         return rootHisto.getYaxis().getNbins();
      }
      public boolean isRebinnable()
      {
         return false;
      }
      public int getXAxisType()
      {
         return DOUBLE;
      }
      public String[] getXAxisLabels()
      {
         return null;
      }
      public int getYAxisType()
      {
         return DOUBLE;
      }
      public String[] getYAxisLabels()
      {
         return null;
      }
      public String getTitle()
      {
         return rootHisto.getTitle();
      }
      public Statistics getStatistics()
      {
         return this;
      }
      public String[] getStatisticNames()
      {
         return new String[]
         {"Entries"};
      }
      public double getStatistic(String p1)
      {
         return rootHisto.getEntries();
      }
   }
}
