/* Copyright (C) 2012-2020 Petersburg Nuclear Physics Institute named by B.P.Konstantinov of National Research Centre "Kurchatov Institute", Gatchina
   SPDX-License-Identifier: GPL-3.0-only
   Authors: Mikhail Ryzhinskiy, Evgeny Kryshen [committer] */

/** CbmMuchModuleGemRectangular.cxx
 *@author  M.Ryzhinskiy <m.ryzhinskiy@gsi.de>
 *@version 1.0
 *@since   11.02.08
 **
 ** This class holds the transport geometry parameters
 ** of one side of MuCh module.
 **/
#include "CbmMuchModuleGemRectangular.h"

#include "CbmMuchPadRectangular.h"     // for CbmMuchPadRectangular
#include "CbmMuchSectorRectangular.h"  // for CbmMuchSectorRectangular

#include <TMathBase.h>  // for Abs

#include <algorithm>  // for find
#include <limits>     // for numeric_limits
#include <vector>     // for vector, vector<>::iterator

using std::vector;

// -----   Default constructor   -------------------------------------------
CbmMuchModuleGemRectangular::CbmMuchModuleGemRectangular()
  : CbmMuchModuleGem()
  , fUseModuleDesign(kFALSE)
  , fGridNx(0)
  , fGridNy(0)
  , fGridDx(0.)
  , fGridDy(0.)
  , fGrid()
{
  fDetectorType = 1;
}
// -------------------------------------------------------------------------


// -----   Standard constructor   ------------------------------------------
CbmMuchModuleGemRectangular::CbmMuchModuleGemRectangular(Int_t iStation, Int_t iLayer, Bool_t iSide, Int_t iModule,
                                                         TVector3 position, TVector3 size, Double_t cutRadius)
  : CbmMuchModuleGem(iStation, iLayer, iSide, iModule, position, size, cutRadius)
  , fUseModuleDesign(kFALSE)
  , fGridNx(0)
  , fGridNy(0)
  , fGridDx(0.)
  , fGridDy(0.)
  , fGrid()
{
  fDetectorType = 1;
}
// -------------------------------------------------------------------------


// -----   Public method GetSector   ---------------------------------------
CbmMuchSectorRectangular* CbmMuchModuleGemRectangular::GetSector(Int_t ix, Int_t iy)
{
  if (ix < 0 || ix >= fGridNx) return nullptr;
  if (iy < 0 || iy >= fGridNy) return nullptr;
  Long64_t iSector = fGrid[ix][iy];
  if (iSector == -1) return nullptr;
  return (CbmMuchSectorRectangular*) fSectors[iSector];
}
// -------------------------------------------------------------------------


// -----   Public method GetSector   ---------------------------------------
CbmMuchSectorRectangular* CbmMuchModuleGemRectangular::GetSector(Double_t x, Double_t y)
{
  return GetSector(GetGridIndexX(x), GetGridIndexY(y));
}
// -------------------------------------------------------------------------

// -------------------------------------------------------------------------
Int_t CbmMuchModuleGemRectangular::GetGridIndexX(Double_t x)
{
  Double_t mx0 = fPosition[0];
  Double_t mlx = fSize[0];
  Double_t msx = mx0 > 0 ? +1 : -1;
  Double_t mx1 = mx0 - msx * mlx / 2;
  return Int_t((x - mx1) / msx / fGridDx);
}
// -------------------------------------------------------------------------

// -------------------------------------------------------------------------
Int_t CbmMuchModuleGemRectangular::GetGridIndexY(Double_t y)
{
  Double_t my0 = fPosition[1];
  Double_t mly = fSize[1];
  Double_t msy = my0 > 0 ? +1 : -1;
  Double_t my1 = my0 - msy * mly / 2;
  return Int_t((y - my1) / msy / fGridDy);
}
// -------------------------------------------------------------------------

// -------------------------------------------------------------------------
CbmMuchPadRectangular* CbmMuchModuleGemRectangular::GetPad(Double_t x, Double_t y)
{
  CbmMuchSectorRectangular* sector = GetSector(x, y);
  if (!sector) return nullptr;
  return sector->GetPad(x, y);
}
// -------------------------------------------------------------------------


// -----   Public method InitGrid  -----------------------------------------
Bool_t CbmMuchModuleGemRectangular::InitGrid(Bool_t useModuleDesign)
{
  Int_t nSectors = GetNSectors();
  if (!nSectors) return kFALSE;
  fUseModuleDesign = useModuleDesign;

  Double_t mx0 = fPosition[0];
  Double_t my0 = fPosition[1];
  Double_t mlx = fSize[0];
  Double_t mly = fSize[1];
  Double_t msx = mx0 > 0 ? +1 : -1;
  Double_t msy = my0 > 0 ? +1 : -1;
  Double_t mx1 = mx0 - msx * mlx / 2;
  Double_t my1 = my0 - msy * mly / 2;

  // Determine grid dimensions
  fGridDx = std::numeric_limits<Double_t>::max();
  fGridDy = std::numeric_limits<Double_t>::max();
  for (Int_t iSector = 0; iSector < nSectors; iSector++) {
    CbmMuchSectorRectangular* s = (CbmMuchSectorRectangular*) fSectors[iSector];
    if (s->IsIncomplete()) continue;
    Double_t dx = s->GetSize()[0];
    Double_t dy = s->GetSize()[1];
    if (dx < fGridDx) fGridDx = dx;
    if (dy < fGridDy) fGridDy = dy;
  }

  Int_t nCell = fUseModuleDesign ? 1 : 2;  // Number of additional columns/rows in the grid
  fGridNx     = Int_t((mlx + 1e-5) / fGridDx) + nCell;
  fGridNy     = Int_t((mly + 1e-5) / fGridDy) + nCell;

  // Fill grid
  fGrid.resize(fGridNx);
  for (Int_t ix = 0; ix < fGridNx; ix++) {
    fGrid[ix].resize(fGridNy);
    for (Int_t iy = 0; iy < fGridNy; iy++) {
      Double_t x    = mx1 + msx * fGridDx * (ix + 1e-3);
      Double_t y    = my1 + msy * fGridDy * (iy + 1e-3);
      fGrid[ix][iy] = -1;
      for (int iSector = 0; iSector < nSectors; iSector++) {
        CbmMuchSectorRectangular* sec = (CbmMuchSectorRectangular*) fSectors[iSector];
        if (sec->Inside(x, y)) {
          fGrid[ix][iy] = sec->GetSectorIndex();
          break;
        }
      }
    }
  }
  return kTRUE;
}
// -------------------------------------------------------------------------

// ------ Public method InitNeighbours  ------------------------------------
void CbmMuchModuleGemRectangular::InitNeighbourSectors()
{
  vector<CbmMuchSectorRectangular*> neighbours;
  vector<CbmMuchSectorRectangular*>::iterator it;
  for (Int_t iSector = 0; iSector < GetNSectors(); iSector++) {
    CbmMuchSectorRectangular* sector = (CbmMuchSectorRectangular*) fSectors[iSector];
    Double_t x1                      = sector->GetX1() - 1e-3;
    Double_t x2                      = sector->GetX2() + 1e-3;
    Double_t y1                      = sector->GetY1() - 1e-3;
    Double_t y2                      = sector->GetY2() + 1e-3;
    Double_t ix1                     = GetGridIndexX(x1);
    Double_t ix2                     = GetGridIndexX(x2);
    Double_t iy1                     = GetGridIndexY(y1);
    Double_t iy2                     = GetGridIndexY(y2);
    Double_t ixmin                   = (ix1 < ix2) ? ix1 : ix2;
    Double_t ixmax                   = (ix1 < ix2) ? ix2 : ix1;
    Double_t iymin                   = (iy1 < iy2) ? iy1 : iy2;
    Double_t iymax                   = (iy1 < iy2) ? iy2 : iy1;
    if (ixmin < 0) ixmin = 0;
    if (ixmax >= fGridNx) ixmax = fGridNx - 1;
    if (iymin < 0) iymin = 0;
    if (iymax >= fGridNy) iymax = fGridNy - 1;

    for (Int_t ix = ixmin; ix <= ixmax; ix++) {
      for (Int_t iy = iymin; iy <= iymax; iy++) {
        Int_t iSec = fGrid[ix][iy];
        if (iSec < 0) continue;
        if (iSec == iSector) continue;
        CbmMuchSectorRectangular* sec = (CbmMuchSectorRectangular*) fSectors[iSec];
        it                            = find(neighbours.begin(), neighbours.end(), sec);
        if (it == neighbours.end()) neighbours.push_back(sec);
      }
    }
    sector->SetNeighbours(neighbours);
    neighbours.clear();
  }
}
// -------------------------------------------------------------------------

// ------ Public method InitNeighbourPads ----------------------------------
void CbmMuchModuleGemRectangular::InitNeighbourPads()
{
  vector<CbmMuchPad*>::iterator it;
  vector<CbmMuchPad*> neighbours;

  // Loop over all sectors within the module
  for (Int_t iSector = 0; iSector < GetNSectors(); iSector++) {
    CbmMuchSectorRectangular* sector = (CbmMuchSectorRectangular*) fSectors[iSector];
    if (!sector) continue;
    Double_t mindx                                     = sector->GetPadDx();
    Double_t mindy                                     = sector->GetPadDy();
    vector<CbmMuchSectorRectangular*> neighbourSectors = sector->GetNeighbours();
    for (UInt_t iSec = 0; iSec < neighbourSectors.size(); iSec++) {
      CbmMuchSectorRectangular* sec = neighbourSectors[iSec];
      Double_t dx                   = sec->GetPadDx();
      Double_t dy                   = sec->GetPadDy();
      if (dx < mindx) mindx = dx;
      if (dy < mindy) mindy = dy;
    }

    for (Int_t iChannel = 0; iChannel < sector->GetNChannels(); iChannel++) {
      CbmMuchPadRectangular* pad = (CbmMuchPadRectangular*) sector->GetPadByChannelIndex(iChannel);
      Double_t x1                = pad->GetX1();
      Double_t x2                = pad->GetX2();
      Double_t y1                = pad->GetY1();
      Double_t y2                = pad->GetY2();
      for (Double_t x = x1 - mindx / 2; x < x2 + mindx / 2 + 1e-3; x += mindx) {
        for (Double_t y = y1 - mindy / 2; y < y2 + mindy / 2 + 1e-3; y += mindy) {
          CbmMuchPad* p = GetPad(x, y);
          if (!p) continue;
          if (p == pad) continue;
          it = find(neighbours.begin(), neighbours.end(), p);
          if (it == neighbours.end()) neighbours.push_back(p);
        }
      }
      pad->SetNeighbours(neighbours);
      neighbours.clear();
    }
  }
}
// -------------------------------------------------------------------------

// -------------------------------------------------------------------------
Bool_t CbmMuchModuleGemRectangular::InitModule()
{
  Bool_t useModuleDesign = TMath::Abs(fPosition[0]) > 1e-5 || TMath::Abs(fPosition[1]) > 1e-5;
  if (!InitGrid(useModuleDesign)) return kFALSE;
  InitNeighbourSectors();
  for (Int_t iSector = 0; iSector < GetNSectors(); iSector++) {
    CbmMuchSectorRectangular* sector = (CbmMuchSectorRectangular*) fSectors[iSector];
    if (!sector) continue;
    sector->AddPads();
  }
  InitNeighbourPads();
  return kTRUE;
}
// -------------------------------------------------------------------------

ClassImp(CbmMuchModuleGemRectangular)