////////////////////////////////////////////////////////////////////////
// Class:       ImageDivider
// Module Type: ImageDivider
// File:        ImageDivider_module.cc
//
// Generated at Thu Mar 31 09:36:00 2016 by Taritree Wongjirad using artmod
// from cetpkgsupport v1_10_01.
////////////////////////////////////////////////////////////////////////

#include "art/Framework/Core/EDAnalyzer.h"
#include "art/Framework/Core/ModuleMacros.h"
#include "art/Framework/Principal/Event.h"
#include "art/Framework/Principal/Handle.h"
#include "art/Framework/Principal/Run.h"
#include "art/Framework/Principal/SubRun.h"
#include "art/Utilities/InputTag.h"
#include "fhiclcpp/ParameterSet.h"
#include "messagefacility/MessageLogger/MessageLogger.h"
#include "art/Framework/Services/Optional/TFileService.h"

#include "Geometry/Geometry.h" // LArCore
#include "Geometry/OpDetGeo.h" // LArCore
#include <cmath>
#include "TCanvas.h"
#include "TH2D.h"
#include "TBox.h"
#include "TLine.h"
#include "TROOT.h"
#include "TTree.h"

class ImageDivider;

class ImageDivider : public art::EDAnalyzer {
public:
  explicit ImageDivider(fhicl::ParameterSet const & p);
  // The destructor generated by the compiler is fine for classes
  // without bare pointers or other resource use.

  // Plugins should not be copied or assigned.
  ImageDivider(ImageDivider const &) = delete;
  ImageDivider(ImageDivider &&) = delete;
  ImageDivider & operator = (ImageDivider const &) = delete;
  ImageDivider & operator = (ImageDivider &&) = delete;

  // Required functions.
  void analyze(art::Event const & e) override;
  void cross( double out[], double a[], double b[] );
  double cross2D( double a[], double b[] );

private:

  // Declare member data here.
  TCanvas* test;
  TH2D* bg;
  TBox* tpc;
  bool fTestDraw;

  // output tree variables
  void setupTrees();

  // Output information on the wire coordinates
  TTree* m_WireInfoTree;
  int m_plane;
  int m_wireID;
  float m_wirestart[3];
  float m_wireend[3];
  void storeWireData(); // it does what you think it does

  // Output PMT position information
  TTree* m_PMTInfoTree;
  int m_femch;
  float m_pos[3];
  void storePMTData(); // it does what you think it does
  
  // Output information on the event divisions
  TTree* m_RegionInfoTree;
  int m_regionid;
  float m_plane0wirebounds[2];
  float m_plane1wirebounds[2];
  float m_plane2wirebounds[2];
  float m_tickbounds[2];
  float m_xbounds[2];
  float m_zbounds[2];
  float m_ybounds[2];
  int m_plane0_nwires;
  int m_plane1_nwires;
  int m_plane2_nwires;
};


ImageDivider::ImageDivider(fhicl::ParameterSet const & p)
  :
  EDAnalyzer(p)  // ,
 // More initializers here.
{
  // define output trees and branches
  setupTrees();

  // we output a copy of the wire definitions
  storeWireData();  
  storePMTData();  

  // visualization for debugging

  fTestDraw = false;
  
  test = new TCanvas("test","test", 1200, 600);
  bg = new TH2D("bg", "bg", 100, -10, 1050, 100, -120, 120 );
  tpc = new TBox( 0, -115, 1030, 117 );
  tpc->SetLineColor(kBlack);
  tpc->SetFillColor(0);
  tpc->SetLineWidth(1);
  
  if ( fTestDraw ) {
    test->Draw();
    bg->Draw();
    tpc->Draw();
  }

  
}

void ImageDivider::cross( double out[], double a[], double b[] ) {
  out[0] = a[1]*b[2] - a[2]*b[1];
  out[1] = a[2]*b[0] - a[0]*b[2];
  out[2] = a[0]*b[1] - a[1]*b[0];
}

double ImageDivider::cross2D( double a[], double b[] ) {
  return a[0]*b[1] - a[1]*b[0];
}

void ImageDivider::analyze(art::Event const & e)
{
  // Implementation of required member function here.
  art::ServiceHandle<geo::Geometry> geom;

  // We divide the image into certain number of segments in z and t (i.e. x)
  // Then we make an RGB image using only those wires that cross into that box
  // we expect to make about 50 images per event...
  
  int imgwidth=216;   // number of collection wires in a region (3^2*2^5)
  double pitch = 0.3; // cm
  double imgwidth_cm = imgwidth*pitch; // 76.8 cm across
  double drift_v = 0.108; // cm per usec

  // [ Time axis setup ]
  // we downsample the time bins as the sampling time is shorter than the shaping time (we really should shape shorter)
  // we also aim to roughly match the distance of time pixel with the width of a wire pixel
  // at 0.108 cm per usec, 1 wire pitch is 2.75 usec, this is 5-6 ticks
  int tickstart = 400;
  int tickend   = 5800; // 5000 ticks (400+4600+400) middle 4600 is for the 2.3 ms drift...
  int tickblock = 5; // 2.5 microseconds (electronics shaping time is 2 microseconds
  int ndsticks = (tickend-tickstart)/tickblock; // number of downsampled ticks
  int nt_divs = ndsticks/imgwidth; // number of time divisions
  if ( ndsticks%imgwidth!=0 )
    nt_divs+=1;
  
  // [ Z-direction setup ]
  // The blocks in the z-direction set the size of the region
  int ncolwires = geom->Nwires(2); // number of collection plane wires
  int nwire_divs = ncolwires/imgwidth; // number of collection plane block
  if ( ncolwires%imgwidth!=0 )
    nwire_divs+=1;
  float z0 = 0; // origin
  float zmax = 1100.0;

  // [ Y-direction (233 cm across about) ]
  // I got these numbers from the geometry service
  //int ny     = 233.0/(pitch*imgwidth)+1;
  int ny = 4;
  double ywidth_cm = 59.0;
  double y0 = -118.0; // origin 
  double ymin = -117.0;
  double ymax = 117.0;

  // if we wanted overlap we would do this: but too many regions, probably
  //int nw = nwire_divs + (nwire_divs-1); // number of w-axis boxes
  //int nt = nt_divs + (nt_divs-1);       // number of t-axis boxes
  // simple divisions
  int nw = nwire_divs; // number of wire regions
  int nt = nt_divs;    // number of time regions
  
  std::cout << "Total number of boxes: (" << nw << " nw) X (" << ny << " ny) X (" << nt << " nt) = " << nw*ny*nt << std::endl;

  int largest_nwires =0; // we set the size of the image after the fact here
  int nregions = 0; // counter
  for ( int iw=0; iw<nw; iw++) {
    for (int iy=0; iy<ny; iy++) {

      // we we wanted overlapping regions...
      //int worigin = iw*(imgwidth/2);
      //int yorigin = iy*(imgwidth/2);

      // define the four line segments that sets the boundary
      // do this by 4 rays.
      // start in lower right corner of box
      // direction vectors move counter-clockwise
      double seg0[4][2]; // set origins
      seg0[0][0] = y0 + iy*ywidth_cm;
      seg0[0][1] = z0 + iw*imgwidth_cm;
      seg0[1][0] = y0 + iy*ywidth_cm;
      seg0[1][1] = z0 + (iw+1)*imgwidth_cm;
      seg0[2][0] = y0 + (iy+1)*ywidth_cm;
      seg0[2][1] = z0 + (iw+1)*imgwidth_cm;
      seg0[3][0] = y0 + (iy+1)*ywidth_cm;
      seg0[3][1] = z0 + iw*imgwidth_cm;
      
      double seglen[4] = { imgwidth_cm, ywidth_cm, imgwidth_cm, ywidth_cm };

      if ( seg0[1][1]>zmax ) {
	seg0[1][1] = seg0[2][1] = zmax;
      }
      if ( seg0[2][0]>ymax ) {
	seg0[2][0] = seg0[3][0] = ymax;
      }

      if ( seg0[0][0]<ymin ) {
	seg0[1][0] = seg0[0][0] = ymin;
      }

      double dir[4][2] = { {0.0, 1.0},
			   {1.0, 0.0},
			   {0.0, -1.0},
			   {-1.0,0.0} }; // set dir vectors

      // FOR DEBUG
      // std::cout << "Testing box" << std::endl;
      // std::cout << "(3:" << seg0[3][1] << "," << seg0[3][0] << ")"
      // 		<< "--(2:" << seg0[2][1] << "," << seg0[2][0] << ")" << std::endl;
      // std::cout << "|            |" << std::endl;
      // std::cout << "(0:" << seg0[0][1] << "," << seg0[0][0] << ")"
      // 		<< "--(1:" << seg0[1][1] << "," << seg0[1][0] << ")" << std::endl;
      
      // std::cout << "(3:" << dir[3][1] << "," << dir[3][0] << ")"
      // 		<< "--(2:" << dir[2][1] << "," << dir[2][0] << ")" << std::endl;
      // std::cout << "|            |" << std::endl;
      // std::cout << "(0:" << dir[0][1] << "," << dir[0][0] << ")"
      // 		<< "--(1:" << dir[1][1] << "," << dir[1][0] << ")" << std::endl;

      // for debug we can visualizate these regions
      std::vector< TLine* > drawnlines;
      if ( fTestDraw ) {
	test->Clear();
	bg->Draw();
	tpc->Draw();
	test->Update();
      }

      // length of segments is always imgwidth
      

      // now the fun begins: we test the intersection of wires with these line segments

      std::vector<int> intersecting_plane_wires[3];
      int nwires = 0;
      for ( geo::WireID const& wID : geom->IterateWireIDs() ) {
	geo::WireGeo const& Wire = geom->Wire( wID );
	double start[3] = {0}; 
	double end[3] = {0};
	Wire.GetStart( start );
	Wire.GetEnd( end );
	// these return x,y,z (z is beam axis, x is drift axis, y is normal to x,y

	// std::cout << "[Wire ID " << wID << "]";
	// std::cout << " start (" << start[2] << ", " << start[1] << ")  ";
	// std::cout << " end (" << end[2] << ", " << end[1] << ")";
	// std::cout << std::endl;
	
	// when we do the intersection tests we move to R2 with (y,z)
	// calculate wire direction
	double wiredir[2] = {0};
	double wirelen = 0.0;
	for (int v=0; v<2; v++) {
	  wiredir[v] = end[v+1]-start[v+1]; // we skip x
	  wirelen += wiredir[v]*wiredir[v];
	}
	wirelen = sqrt(wirelen);
	for (int v=0; v<2; v++)
	  wiredir[v] /= wirelen;

	// loop over each segment
	for (unsigned int s=0; s<4; s++) {

	  // calculate useful cross products
	  double qp[2] = {0};
	  double segdir[2] = {0};
	  double rxs[3] = {0};
	  double qpxr[3] = {0};
	  double qpxs[3] = {0};
	  for (unsigned int v=0; v<2; v++) {
	    qp[v] = start[v+1]-seg0[s][v];
	    segdir[v] = dir[s][v];
	  }
	  rxs[2] =  cross2D( segdir, wiredir );
	  qpxr[2] = cross2D( qp, segdir);
	  qpxs[2] = cross2D( qp, wiredir);

	  //std::cout << "rxs=" << rxs[2] << " qpxr=" << qpxr[2]  << " qpxs=" << qpxs[2] << std::endl;

	  // test if colinear (we don't expect this happen)
	  bool qpxr_zero = true;
	  bool rxs_zero = true;
	  for (int v=0; v<3; v++) {
	    if ( fabs(rxs[v])>1.0e-8 )
	      rxs_zero = false;
	    if ( fabs(qpxr[v])>1.0e-8 )
	      qpxr_zero = false;
	  }

	  if ( qpxr_zero && rxs_zero ) {
	    // colinear
	    std::cout << " seg " << s << " is colinear" << std::endl;
	    continue;
	  }

	  // test if non-intersecting
	  if ( rxs_zero && !qpxr_zero ) {
	    //std::cout << " seg " << s << " is non-intersecting" << std::endl;
	    continue;
	  }

	  // rays intersect
	  double u,t;
	  if ( rxs[2]!=0 ) {
	    u = qpxr[2]/rxs[2];
	    t = qpxs[2]/rxs[2];
	  }
	  else {
	    u = -1;
	    t = -1;
	  }

	  //std::cout << "u=" << u << " t=" << t << std::endl;

	  //if ( u>=0 && u<=wirelen && t>=0 && t<=imgwidth_cm ) {
	  if ( u>=0 && u<=wirelen && t>=0 && t<=seglen[s] ) {
	    //std::cout << " seg " << s << " intersects" << std::endl;
	    if ( fTestDraw ) {
	      TLine* l = new TLine( start[2], start[1], end[2], end[1] );
	      l->Draw();
	      drawnlines.push_back( l ); // we keep these, so we can destroy them later
	    }
	    nwires++;
	    intersecting_plane_wires[ wID.Plane ].push_back( wID.Wire );
	    break;
	  }

	  //std::cout << " seg " << s << " neither intersects nor is parallel" << std::endl;
	  
	  
	}//loop over box line segments
	
	//std::cin.get();

      }

      // we finished this region
      std::cout << "Region number of wires. Plane0=" << intersecting_plane_wires[0].size()
		<< " Plane1=" << intersecting_plane_wires[1].size() 
		<< " Plane2=" << intersecting_plane_wires[2].size()
		<< std::endl;
      if ( (int)intersecting_plane_wires[0].size()>largest_nwires )
	largest_nwires = intersecting_plane_wires[0].size();
      if ( (int)intersecting_plane_wires[1].size()>largest_nwires )
	largest_nwires = intersecting_plane_wires[1].size();
      if ( (int)intersecting_plane_wires[2].size()>largest_nwires )
	largest_nwires = intersecting_plane_wires[2].size();
      //for (int it; it<nt; it++) {
      //int torigin = it*tickblock*(imgwidth/2);
      //}
      //break;

      // store this definition
      m_regionid = nregions;
      int minplane[3] = { 100000, 100000, 100000 };
      int maxplane[3] ={ 0, 0, 0 };
      int max_wires = 0;
      for (int iplane=0; iplane<3; iplane++) {
	for (int iwire=0; iwire<(int)intersecting_plane_wires[iplane].size(); iwire++) {

	  if ( minplane[iplane]>intersecting_plane_wires[iplane].at( iwire ) )
	    minplane[iplane] = intersecting_plane_wires[iplane].at( iwire );
	  if ( maxplane[iplane]<intersecting_plane_wires[iplane].at( iwire ) )
	    maxplane[iplane] = intersecting_plane_wires[iplane].at( iwire );
	}
	if ( max_wires<(int)intersecting_plane_wires[iplane].size() )
	  max_wires = (int)intersecting_plane_wires[iplane].size();
      }

      m_plane0wirebounds[0] = minplane[0];
      m_plane0wirebounds[1] = maxplane[0];
      m_plane0_nwires = m_plane0wirebounds[1]-m_plane0wirebounds[0] + 1;

      m_plane1wirebounds[0] = minplane[1];
      m_plane1wirebounds[1] = maxplane[1];
      m_plane1_nwires = m_plane1wirebounds[1]-m_plane1wirebounds[0] + 1;

      m_plane2wirebounds[0] = minplane[2];
      m_plane2wirebounds[1] = maxplane[2];
      m_plane2_nwires = m_plane2wirebounds[1]-m_plane2wirebounds[0] + 1;

      m_zbounds[0] = seg0[0][1];
      m_zbounds[1] = seg0[1][1];
      m_ybounds[0] = seg0[0][0];
      m_ybounds[1] = seg0[2][0];

      for (int it=0; it<nt; it++) {
	m_regionid = nregions;
	m_tickbounds[0] = it*imgwidth;
	m_tickbounds[1] = (it+1)*imgwidth;
	m_xbounds[0] = it*imgwidth*tickblock*0.5*drift_v;
	m_xbounds[1] = (it+1)*imgwidth*tickblock*0.5*drift_v;
	m_RegionInfoTree->Fill();
	nregions++;
      }
      
      if (fTestDraw) {

	TBox* thisseg = new TBox( seg0[0][1], seg0[0][0], seg0[2][1], seg0[2][0] );
	thisseg->SetLineColor(kRed);
	thisseg->SetLineWidth(2);
	thisseg->SetFillColor(0);
	thisseg->SetFillStyle(0);
	thisseg->Draw();

	test->Update();
	char testname[50];
	sprintf( testname, "wire_region_%03d.png", nregions );
	test->SaveAs( testname );
	
	delete thisseg;
	for ( int i=0; i<(int)drawnlines.size(); i++ ) {
	  delete drawnlines.at(i);
	  drawnlines.at(i) = NULL;
	}

      }

    }
    //break;
  }

  std::cout << "Number of regions defined: " << nregions << std::endl;
  std::cout << nw << "x" << ny << "x" << nt << std::endl;
  std::cout << "Largest number of wires needed inside of a region: " << largest_nwires << std::endl;
  std::cout << "Raw Memory per event: " << largest_nwires*largest_nwires*nw*ny*nt*2/1.0e6 << " MB" << std::endl;


}

void ImageDivider::setupTrees() {

  art::ServiceHandle<art::TFileService> ana_file;
  m_WireInfoTree     = ana_file->make<TTree>( "wireInfo", "Wire Geo Info" );  // image of the entire event
  m_WireInfoTree->Branch( "plane", &m_plane, "plane/I" );
  m_WireInfoTree->Branch( "wireID", &m_wireID, "wireID/I" );
  m_WireInfoTree->Branch( "start", m_wirestart, "start[3]/F" );
  m_WireInfoTree->Branch( "end", m_wireend, "end[3]/F" );

  m_PMTInfoTree     = ana_file->make<TTree>( "pmtInfo", "PMT Geo Info" );  // image of the entire event
  m_PMTInfoTree->Branch( "femch", &m_femch, "femch/I" );
  m_PMTInfoTree->Branch( "pos", m_pos, "pos[3]/F" );

  m_RegionInfoTree   = ana_file->make<TTree>( "regionInfo", "Bounding Box Tree and its label" ); // images of each interaction within events
  m_RegionInfoTree->Branch( "regionid", &m_regionid, "regionid/I" );
  m_RegionInfoTree->Branch( "plane0_wirebounds", m_plane0wirebounds, "plane0_wirebounds[2]/F" );
  m_RegionInfoTree->Branch( "plane1_wirebounds", m_plane1wirebounds, "plane1_wirebounds[2]/F" );
  m_RegionInfoTree->Branch( "plane2_wirebounds", m_plane2wirebounds, "plane2_wirebounds[2]/F" );
  m_RegionInfoTree->Branch( "plane0_nwires",     &m_plane0_nwires,    "plane0_nwires/I");
  m_RegionInfoTree->Branch( "plane1_nwires",     &m_plane1_nwires,    "plane1_nwires/I");
  m_RegionInfoTree->Branch( "plane2_nwires",     &m_plane2_nwires,    "plane2_nwires/I");
  m_RegionInfoTree->Branch( "zbounds",        m_zbounds,       "zbounds[2]/F" );
  m_RegionInfoTree->Branch( "ybounds",        m_ybounds,       "ybounds[2]/F" );
  m_RegionInfoTree->Branch( "xbounds",        m_xbounds,       "xbounds[2]/F" );

}

void ImageDivider::storeWireData() {

  // Implementation of required member function here.
  art::ServiceHandle<geo::Geometry> geom;
  //geo::WireID wid = 
  //geo::WireGeo* pWire = geom->WirePtr( 
  for ( geo::WireID const& wID : geom->IterateWireIDs() ) {
    geo::WireGeo const& Wire = geom->Wire( wID );
    m_wireID = (int)wID.Wire;
    m_plane  = (int)wID.Plane;
    double start[3];
    double end[3];
    Wire.GetStart( start );
    Wire.GetEnd( end );
    for (int i=0; i<3; i++) {
      m_wirestart[i] = (float)start[i];
      m_wireend[i]   = (float)end[i];
    }
    //std::cout << "[Wire ID " << wID << "]";
    //std::cout << " start (" << start[0] << ", " << start[1] << ", " << start[2] << ")  ";
    //std::cout << " end (" << end[0] << ", " << end[1] << ", " << end[2] << ")";
    //std::cout << std::endl;
    m_WireInfoTree->Fill();
  }

}

void ImageDivider::storePMTData() {

  // Implementation of required member function here.
  art::ServiceHandle<geo::Geometry> geom;
  
  for (int n=0; n<32; n++) {
    geo::OpDetGeo const& opdet = geom->OpDetGeoFromOpChannel( n );
    double xyz[3];
    opdet.GetCenter( xyz );
    for (int i=0; i<3; i++)
      m_pos[i] = xyz[i];
    m_femch = n;
    m_PMTInfoTree->Fill();
  }

}


DEFINE_ART_MODULE(ImageDivider)
