#ifndef ClassAnasen_h
#define ClassAnasen_h

#include <cstdio>
#include <TMath.h>
#include <TVector3.h>

#include "TGeoManager.h"
#include "TGeoVolume.h"
#include "TGeoBBox.h"
#include "TCanvas.h"
#include "TPolyMarker3D.h"
#include "TPolyLine3D.h"
#include "TRandom.h"

#include "ClassSX3.h"
#include "ClassPW.h"

class ANASEN{
public:
  ANASEN();
  ~ANASEN();

  void SetUncertainties(double sx3W, double sx3L, double anode, double cathode){
    sigmaA = anode;
    sigmaC = cathode;
    sigmaW = sx3W;
    sigmaL = sx3L;
  }

  void DrawTrack(TVector3 pos, TVector3 direction, bool drawEstimatedTrack = false);
  void DrawDeducedTrack(TVector3 sx3Pos, int anodeID, int cathodeID);
  void DrawAnasen(int anodeID1 = -1, 
                  int anodeID2 = -1, 
                  int cathodeID1 = -1, 
                  int cathodeID2 = -1, 
                  int sx3ID = -1, 
                  bool DrawQQQ = false );



  PW * GetPW() {return pw;}
  SX3 * GetSX3() {return sx3;}

private:

  PW * pw;
  SX3 * sx3;

  double sigmaA, sigmaC; // pw
  double sigmaW, sigmaL; // sx3

  const float qqqR1 = 50;
  const float qqqR2 = 100;
  const float qqqZPos = 23 + 75 + 30; 

  void CalGeometry();

  TGeoManager *geom;
  TGeoVolume *worldBox;

  void Construct3DModel(int anodeID1 = -1, 
                        int anodeID2 = -1, 
                        int cathodeID1 = -1, 
                        int cathodeID2 = -1, 
                        int sx3ID = -1, 
                        bool DrawQQQ = true);

};

//!==============================================
inline ANASEN::ANASEN(){

  pw = new PW();
  sx3 = new SX3();

  CalGeometry();

  geom = nullptr;
  worldBox = nullptr;

}

inline ANASEN::~ANASEN(){

  delete geom;

  delete pw;
  delete sx3;

}

//!==============================================
inline void ANASEN::CalGeometry(){

  sx3->ConstructGeo();
  pw->ConstructGeo();

}

inline void ANASEN::Construct3DModel(int anodeID1, int anodeID2, int cathodeID1, int cathodeID2, int sx3ID, bool DrawQQQ ){

  if( geom ) delete geom;

  // Create ROOT manager and master volume
  geom = new TGeoManager("Detector", "ANASEN");

  //--- define some materials
  TGeoMaterial *matVacuum = new TGeoMaterial("Vacuum", 0,0,0);
  TGeoMaterial *matAl = new TGeoMaterial("Al", 26.98,13,2.7);
  //--- define some media
  TGeoMedium *Vacuum = new TGeoMedium("Vacuum",1, matVacuum);
  TGeoMedium *Al = new TGeoMedium("Root Material",2, matAl);

  //--- make the top container volume 
  Double_t worldx = 200.; //mm
  Double_t worldy = 200.; //mm
  Double_t worldz = 200.; //mm
  worldBox = geom->MakeBox("ROOT", Vacuum, worldx, worldy, worldz);
  geom->SetTopVolume(worldBox);

  //--- making axis
  TGeoVolume *axisX = geom->MakeTube("axisX", Al, 0, 0.1, 5.);
  axisX->SetLineColor(1);
  worldBox->AddNode(axisX, 1, new TGeoCombiTrans(5, 0, 0., new TGeoRotation("rotA", 90., 90., 0.)));
  TGeoVolume *axisY = geom->MakeTube("axisY", Al, 0, 0.1, 5.);
  axisY->SetLineColor(1);
  worldBox->AddNode(axisY, 1, new TGeoCombiTrans(0, 5, 0., new TGeoRotation("rotB", 0., 90., 0.)));
  TGeoVolume *axisZ = geom->MakeTube("axisZ", Al, 0, 0.1, 5.);
  axisZ->SetLineColor(1);
  worldBox->AddNode(axisZ, 1, new TGeoTranslation(0, 0,  5));

  //.......... convert to wire center dimensions
  TGeoVolume *pcA = geom->MakeTube("tub1", Al, 0, 0.01, pw->GetAnodeLength()/2);
  pcA->SetLineColor(4);  

  int startID = 0;
  int endID = pw->GetNumWire() - 1;

  if( anodeID1 >= 0 && anodeID2 >= 0 ){
    startID = anodeID1;
    endID = anodeID2;
    if( anodeID1 > anodeID2 ) {
      endID = pw->GetNumWire() + anodeID2;
    }
  }

  for( int i = startID; i <= endID; i++){
    TVector3 a = pw->GetAnodneMid(i);
    double wireTheta = pw->GetAnodeTheta(i) * TMath::RadToDeg();
    double wirePhi = pw->GetAnodePhi(i) * TMath::RadToDeg() + 90;

    worldBox->AddNode(pcA, i+1, new TGeoCombiTrans( a.X(), 
                                                    a.Y(), 
                                                    a.Z(), 
                                                    new TGeoRotation("rot1", wirePhi, wireTheta, 0.)));
  }

  TGeoVolume *pcC = geom->MakeTube("tub2", Al, 0, 0.01, pw->GetCathodeLength()/2);
  pcC->SetLineColor(6);

  startID = 0;
  endID = pw->GetNumWire() - 1;

  if( cathodeID1 >= 0 && cathodeID2 >= 0 ){
    startID = cathodeID1;
    endID = cathodeID2;
    if( cathodeID1 > cathodeID2 ) {
      endID = pw->GetNumWire() + cathodeID2;
    }
  }

  for( int i = startID; i <= endID; i++){
    TVector3 a = pw->GetCathodneMid(i);
    double wireTheta = pw->GetCathodeTheta(i) * TMath::RadToDeg();
    double wirePhi = pw->GetCathodePhi(i) * TMath::RadToDeg() + 90;

    worldBox->AddNode(pcC, i+1, new TGeoCombiTrans( a.X(), 
                                                    a.Y(), 
                                                    a.Z(), 
                                                    new TGeoRotation("rot1", wirePhi , wireTheta, 0.)));
  }

  TGeoVolume * sx3Det = geom->MakeBox("box", Al, 0.1, sx3->GetWidth()/2, sx3->GetLength()/2);
  sx3Det->SetLineColor(kGreen+3);

  for( int i = 0; i < sx3->GetNumDet(); i++){
    if( sx3ID != -1 && i != sx3ID ) continue;
    TVector3 aUp = sx3->GetUpMid(i); // center of the SX3 upstream
    TVector3 aDn = sx3->GetDnMid(i); // center of the SX3 Downstream
    double phi = sx3->GetDetPhi(i) * TMath::RadToDeg() + 90;

    worldBox->AddNode(sx3Det, 2*i+1., new TGeoCombiTrans( aUp.X(), 
                                                          aUp.Y(), 
                                                          aUp.Z(), 
                                                          new TGeoRotation("rot1", phi, 0., 0.)));
    worldBox->AddNode(sx3Det, 2*i+1., new TGeoCombiTrans( aDn.X(), 
                                                          aDn.Y(), 
                                                          aDn.Z(), 
                                                          new TGeoRotation("rot1", phi, 0., 0.)));
  }

  if( DrawQQQ ){
    TGeoVolume *qqq = geom->MakeTubs("qqq", Al, qqqR1, qqqR2, 0.5, 5, 85);
    qqq->SetLineColor(7);
    for( int i = 0; i < 4; i++){
      worldBox->AddNode(qqq, i+1, new TGeoCombiTrans( 0, 
                                                      0, 
                                                      qqqZPos, 
                                                      new TGeoRotation("rot1", 360/4 * (i), 0., 0.)));
    }
  }

}

//!============================================== Drawing functions
inline void ANASEN::DrawAnasen(int anodeID1, int anodeID2, int cathodeID1, int cathodeID2, int sx3ID, bool DrawQQQ ){

  Construct3DModel(anodeID1, anodeID2, cathodeID1, cathodeID2, sx3ID, DrawQQQ);

  geom->CloseGeometry();
  geom->SetVisLevel(4);
  worldBox->Draw("ogle");

}

inline  void ANASEN::DrawTrack(TVector3 pos, TVector3 direction, bool drawEstimatedTrack){

  pw->FindWireID(pos, direction);
  sx3->FindSX3Pos(pos, direction);

  std::pair<short, short> wireID = pw->GetNearestID();
  
  Construct3DModel(wireID.first, wireID.first, wireID.second, wireID.second, -1, false);

  double theta = direction.Theta() * TMath::RadToDeg();
  double phi = direction.Phi()  * TMath::RadToDeg();
  // printf("Theta, Phi = %.2f %.2f \n", theta, phi);
  // pos.Print();
  TGeoVolume * Track = geom->MakeTube("track", 0, 0, 0.1, 150.);
  Track->SetLineColor(kRed);
  worldBox->AddNode(Track, 1, new TGeoCombiTrans( pos.X(), pos.Y(), pos.Z(), new TGeoRotation("rotA", phi +  90, theta, 0.)));

  TGeoVolume * startPos = geom->MakeSphere("startPos", 0, 0, 3);
  startPos->SetLineColor(kBlack);
  worldBox->AddNode(startPos, 3, new TGeoCombiTrans( pos.X(), pos.Y(), pos.Z(), new TGeoRotation("rotA", 0, 0, 0.)));

  if( sx3->GetID() >= 0 ){
    //TVector3 hitPos = sx3->GetHitPos();
    TVector3 hitPos = sx3->GetHitPosWithSigma(sigmaW, sigmaL);

    TGeoVolume * hit = geom->MakeSphere("hitpos", 0, 0, 3);
    hit->SetLineColor(kRed);
    worldBox->AddNode(hit, 2, new TGeoCombiTrans( hitPos.X(), hitPos.Y(), hitPos.Z(), new TGeoRotation("rotA", 0, 0, 0.)));

    if( drawEstimatedTrack ){

      {//===== simple
        pw->CalTrack(hitPos, wireID.first, wireID.second, true);

        double thetaDeduce = pw->GetTrackTheta() * TMath::RadToDeg();
        double phiDeduce = pw->GetTrackPhi()  * TMath::RadToDeg();

        TGeoVolume * trackDeduce = geom->MakeTube("trackDeduce", 0, 0, 0.1, 100.);
        trackDeduce->SetLineColor(kOrange);
        worldBox->AddNode(trackDeduce, 1, new TGeoCombiTrans( hitPos.X(), hitPos.Y(), hitPos.Z(), new TGeoRotation("rotA", phiDeduce +  90, thetaDeduce, 0.)));
      }

      {//===== complicated 
        PWHitInfo hitInfo = pw->GetHitInfo();
        pw->CalTrack2(hitPos, hitInfo, sigmaA, sigmaC, true);

        double thetaDeduce = pw->GetTrackTheta() * TMath::RadToDeg();
        double phiDeduce = pw->GetTrackPhi()  * TMath::RadToDeg();

        TGeoVolume * trackDeduce2 = geom->MakeTube("trackDeduce2", 0, 0, 0.1, 100.);
        trackDeduce2->SetLineColor(kGreen);
        worldBox->AddNode(trackDeduce2, 1, new TGeoCombiTrans( hitPos.X(), hitPos.Y(), hitPos.Z(), new TGeoRotation("rotA", phiDeduce +  90, thetaDeduce, 0.)));
      }
    }

  }
  
  geom->CloseGeometry();
  geom->SetVisLevel(4);
  worldBox->Draw("ogle");

}

inline  void ANASEN::DrawDeducedTrack(TVector3 sx3Pos, int anodeID, int cathodeID){

  pw->CalTrack(sx3Pos, anodeID, cathodeID);

  Construct3DModel(anodeID, anodeID, cathodeID, cathodeID, -1, false);

  double theta = pw->GetTrackTheta() * TMath::RadToDeg();
  double phi = pw->GetTrackPhi()  * TMath::RadToDeg();

  TGeoVolume * Track = geom->MakeTube("axisX", 0, 0, 0.1, 100.);
  Track->SetLineColor(kRed);
  worldBox->AddNode(Track, 1, new TGeoCombiTrans( sx3Pos.X(), sx3Pos.Y(), sx3Pos.Z(), new TGeoRotation("rotA", phi +  90, theta, 0.)));

  TGeoVolume * hit = geom->MakeSphere("hitpos", 0, 0, 3);
  hit->SetLineColor(kRed);
  worldBox->AddNode(hit, 2, new TGeoCombiTrans( sx3Pos.X(), sx3Pos.Y(), sx3Pos.Z(), new TGeoRotation("rotA", 0, 0, 0.)));

  geom->CloseGeometry();
  geom->SetVisLevel(4);
  worldBox->Draw("ogle");

}


#endif 