#include <HidroCellHig_MGB_IPH.h>

#include <HidroFlowUtils.h>

#include <stdio.h>
#include <cmath>

#define M_PI       3.14159265358979323846
#define M_SQRT2    1.41421356237309504880

#include <TeRaster.h>

HidroCellHig::HidroCellHig(
  TeRaster* demRaster,
  TeRaster* blocksRaster,
  TeRaster* highSubwatershedRaster,  
  TeRaster* lowLDDRaster,
  TeRaster* lowAccumulatedRaster,
  TeRaster* lowSubwatershedRaster,
  TeRaster* lengthRaster,
  TeRaster* slopeRaster,
  std::string outFileName ) :
demRaster_(demRaster),
blocksRaster_(blocksRaster),
highSubwatershedRaster_(highSubwatershedRaster),
lowLDDRaster_(lowLDDRaster),
lowAccumulatedRaster_(lowAccumulatedRaster),
lowSubwatershedRaster_(lowSubwatershedRaster),
lengthRaster_(lengthRaster),
slopeRaster_(slopeRaster),
outFileName_(outFileName)
{
  /*if( demRaster->params().useDummy_ )
  {
    demDummy_ = (float)demRaster->params().dummy_[0];
  }
  else
  {
    demDummy_ = -9999;
  }*/

  // right
  lineIncrement_[1] = 0;
  columnIncrement_[1] = 1;

  // down right
  lineIncrement_[2] = 1;
  columnIncrement_[2] = 1;

  // down
  lineIncrement_[4] = 1;
  columnIncrement_[4] = 0;

  // down left
  lineIncrement_[8] = 1;
  columnIncrement_[8] = -1;

  // left
  lineIncrement_[16] = 0;
  columnIncrement_[16] = -1;

  // up left
  lineIncrement_[32] = -1;
  columnIncrement_[32] = -1;

  // up
  lineIncrement_[64] = -1;
  columnIncrement_[64] = 0;

  // up right
  lineIncrement_[128] = -1;
  columnIncrement_[128] = 1;

  A = demRaster_->params().projection()->datum().radius() / 1000.0;
  F = demRaster_->params().projection()->datum().flattening();
	
  E2 = 2 * F - F * F; //!QUADRADO DA EXCENTRICIDADE

  resx = lowLDDRaster_->params().resx_;
  resy = lowLDDRaster_->params().resy_;

  if( lowSubwatershedRaster_->params().useDummy_ )
  {
    subWatershedDummy_ = lowSubwatershedRaster_->params().dummy_[0];
  }
  else
  {
    subWatershedDummy_ = -9999;
  }
}

HidroCellHig::~HidroCellHig()
{
  if( outFile_ != NULL )
  {
    fclose( outFile_ );
  }
}

bool
HidroCellHig::execute()
{
  // guarda a hora inicial do processo
  Time::instance().start();
  timeStart_ = Time::instance().actualTimeString();

  // abre o arquivo de saida
  outFile_ = NULL;
  outFile_ = fopen( outFileName_.c_str(), "w" );

  if( outFile_ == NULL )
  {
    errorMessage_ = "Error - Can't create output file!";
    return error();
  }

  factor_ = (int) ((lowLDDRaster_->params().resx_ / demRaster_->params().resx_) + 0.5);  

  unsigned int upLines = lowSubwatershedRaster_->params().nlines_;
  unsigned int upColumns = lowSubwatershedRaster_->params().ncols_;

  // fila de prioridade das celulas
  priority_queue< Cell, deque<Cell>, greater<Cell> > cellQueue_;
  priority_queue< Cell, deque<Cell>, greater<Cell> > cellQueue2_;

  // inicializa a matriz de celulas do modelo
  cellMatrix_.Reset( upLines, upColumns, TePDIMatrix<unsigned int>::AutoMemPol, cellMatrix_.getMaxTmpFileSize(), MATRIX_MAX_PERCENT_USAGE );

  double accumulatedVal;
  for( unsigned int line=0; line<upLines; ++line)
  {
    for( unsigned int column=0; column<upColumns; ++column)
    {
      cellMatrix_[line][column] = 0;

      // verifica se esta na area da mascara
      bool touchSubwatershed = false;
      double subwatershedVal;
      lowSubwatershedRaster_->getElement( column, line, subwatershedVal );
      if( subwatershedVal != subWatershedDummy_ )
      {
        touchSubwatershed = true;
      }

      if( touchSubwatershed )
      {
        lowAccumulatedRaster_->getElement(column, line, accumulatedVal );
        Cell cell( line, column, accumulatedVal );
        cellQueue2_.push( cell );        
      }
    }
  }

  unsigned int cellId = 1;
  while( !cellQueue2_.empty() )
  {
    Cell cell = cellQueue2_.top();
    unsigned int line = cell.line_;
    unsigned int column = cell.column_;

    cellMatrix_[line][column] = cellId;
    
    cellQueue_.push( cell );
    
    cellQueue2_.pop();
    cellId++;
  }

  TeProgress::instance()->setMessage("PREPARA...");
  TeProgress::instance()->setTotalSteps( cellQueue_.size() );
  TeProgress::instance()->setProgress( 0 );
  
  unsigned int downstreamId;
  cellId = 1;
  
  while( !cellQueue_.empty() )
  {
    Cell cell = cellQueue_.top();

    unsigned int line = cell.line_;
    unsigned int column = cell.column_;

    cellMatrix_[line][column] = cellId;

    unsigned int blockBeginLine = line * factor_;
    unsigned int blockEndLine = line * factor_ + factor_;

    if( blockBeginLine == 0 )
      blockBeginLine = 1;

    if( blockEndLine > (unsigned int)demRaster_->params().nlines_-1 )
      blockEndLine = demRaster_->params().nlines_ - 1;

    unsigned int blockBeginColumn = column * factor_;
    unsigned int blockEndColumn = column * factor_ + factor_;

    if( blockBeginColumn == 0 )
      blockBeginColumn = 1;

    if( blockEndColumn > (unsigned int)demRaster_->params().ncols_-1 )
      blockEndColumn = demRaster_->params().ncols_ - 1;
  
    // encotra a lat e o long do centro da celula do modelo
    float lat_, long_;
    TeCoord2D centerPixelCoord = lowSubwatershedRaster_->index2Coord( TeCoord2D( line,  column ) );
    lat_ = centerPixelCoord.x_;
    long_ = centerPixelCoord.y_;

    double subwatershedVal;
    lowSubwatershedRaster_->getElement( column, line, subwatershedVal );

    double accumulatedVal;
    lowAccumulatedRaster_->getElement(column, line, accumulatedVal );

    float areacell = area( line );

    // imprime no arquivo
    fprintf( outFile_, "%15d %15.4f %15.4f %5d %15.8f %15.8f",
      cellId, lat_, long_,
      (int)subwatershedVal,
      accumulatedVal, 
      areacell ); 

    //maior e menor elevao
    altimetria( blockBeginLine, blockEndLine, blockBeginColumn, blockEndColumn );

    double lengthVal;
    lengthRaster_->getElement( column, line, lengthVal );

    double slopeVal;
    slopeRaster_->getElement(column, line, slopeVal );

    fprintf( outFile_, " %15.8f %15.8f", lengthVal, slopeVal );

    // celula de jusante    
    if( cellQueue_.size() == 1 )
    {
      downstreamId = cellId + 1;
    }
    else
    {
      double lddVal;
      lowLDDRaster_->getElement( column, line, lddVal );
      int aux = (int)lddVal;
      int ll = line + lineIncrement_[aux];
      int cc = column + columnIncrement_[aux];
      downstreamId = cellMatrix_[ll][cc];
    }    

    fprintf( outFile_, " %5d", downstreamId );

    //percentual dos blocos
    blocks( blockBeginLine, blockEndLine, blockBeginColumn, blockEndColumn );

    fprintf( outFile_,"\n");
    fflush( outFile_ );

    // refresh progress bar    
    TeProgress::instance()->setProgress( cellId-1 );
    
    // check for cancel
    if( TeProgress::instance()->wasCancelled() )
    {
      return cancel();
    }
    
    cellQueue_.pop();    
    cellId++;
  }  

  if( outFile_ != NULL )
  {
    fclose( outFile_ );
  }

  // Finish progress bar
  TeProgress::instance()->reset();

  // save processing time
  timeEnd_ = Time::instance().actualTimeString();
  timeTotal_ = Time::instance().partialString();

  return true;
}

void
HidroCellHig::altimetria( unsigned int lineBegin, unsigned int lineEnd,
    unsigned int columnBegin, unsigned int columnEnd )
{
  double max = 0.0;
  double min = 12000.0;
  double demVal;

  for( unsigned int line=lineBegin; line<lineEnd; line++ )
  {
    for( unsigned int column=columnBegin; column<columnEnd; column++ )
    {
      if( demRaster_->getElement( column, line, demVal ) )
      {
        if( demVal > max )
          max = demVal;

        if( demVal < min )
          min = demVal;
      }
    }
  }

  fprintf( outFile_, " %15.4f %15.4f",
      max, min );
}

float
HidroCellHig::area( unsigned int line )
{ 
  // A rea varia somente na lina / y / latitude  
  TeCoord2D index( 0, line );
  TeCoord2D coord = lowLDDRaster_->index2Coord( index );

  double YLAT = coord.y();        
  
  double RN =  A / sqrt( ( 1 - E2 * sin(YLAT * M_PI/180)*sin(YLAT * M_PI/180) ) ); //!RAIO DE CURVATURA DA TERRA NA LATITUDE

  double AREACEL = ((M_PI * RN*RN) / 180) * 
                    fabs( sin( (YLAT-resy/2) * M_PI/180) - sin( (YLAT+resy/2) * M_PI/180 ) ) *
                    fabs( resx );
  return AREACEL;  
}

void
HidroCellHig::blocks( unsigned int lineBegin, unsigned int lineEnd,
    unsigned int columnBegin, unsigned int columnEnd )
{
  std::vector<int> blocksCounter;  
  unsigned int blocksNumber = (unsigned int)blocksRaster_->params().vmax_[0];
  
  for( unsigned int i=0; i<blocksNumber; i++ )
  {
    blocksCounter.push_back(0);
  }

  double subVal;
  double blockVal;

  int blocksTotal = 0;
  int block;

  for( unsigned int line=lineBegin; line<lineEnd; line++ )
  {
    for( unsigned int column=columnBegin; column<columnEnd; column++ )
    {
      if( highSubwatershedRaster_->getElement( column, line, subVal ) )
      {        
        if( blocksRaster_->getElement( column, line, blockVal ) )
        {
          block = (int)blockVal-1;
          blocksCounter[block] = blocksCounter[block] + 1;
          blocksTotal++;
        }          
      }      
    }
  }

  double blocksPercent;  
  for( unsigned int i=0; i<blocksCounter.size(); i++ )
  {
    blocksPercent = (double)blocksCounter[i] / (double)blocksTotal;
    fprintf( outFile_, " %15.4f", blocksPercent );
  }
}
