/*
   ____    _ __           ____               __    ____
  / __/___(_) /  ___ ____/ __ \__ _____ ___ / /_  /  _/__  ____
 _\ \/ __/ / _ \/ -_) __/ /_/ / // / -_|_-</ __/ _/ // _ \/ __/
/___/\__/_/_.__/\__/_/  \___\_\_,_/\__/___/\__/ /___/_//_/\__(_)

Copyright on 2008 SciberQuest Inc.
*/

#include "pqSQBOVMetaReader.h"

#include "SQMacros.h"

#include "pqNamedWidgets.h"
#include "pqPropertyManager.h"
#include "pqProxy.h"

#include "vtkSMProxy.h"

#include <QValidator>
#include <QIntValidator>
#include <QLineEdit>
#include <QSpinBox>
#include <QString>
#include <QDebug>


#include <limits.h>
#include <iostream>
using std::cerr;
using std::endl;

//-----------------------------------------------------------------------------
pqSQBOVMetaReader::pqSQBOVMetaReader(pqProxy *l_proxy, QWidget *l_parent)
                  :
      pqAutoGeneratedObjectPanel(l_proxy,l_parent)
{
  #if defined pqSQBOVMetaReaderDEBUG
  cerr << ":::::::::::::::::::::::::::::::pqSQBOVMetaReader::pqSQBOVMetaReader" << endl;
  #endif

  // Get the widgets from the autogenerated layout.
  this->SubsetUI[0]=this->findChild<QSpinBox*>("ISubset_0");
  this->SubsetUI[1]=this->findChild<QSpinBox*>("ISubset_1");
  this->SubsetUI[2]=this->findChild<QSpinBox*>("JSubset_0");
  this->SubsetUI[3]=this->findChild<QSpinBox*>("JSubset_1");
  this->SubsetUI[4]=this->findChild<QSpinBox*>("KSubset_0");
  this->SubsetUI[5]=this->findChild<QSpinBox*>("KSubset_1");
  this->DecompDims[0]=this->findChild<QLineEdit*>("DecompDims_0");
  this->DecompDims[1]=this->findChild<QLineEdit*>("DecompDims_1");
  this->DecompDims[2]=this->findChild<QLineEdit*>("DecompDims_2");
  this->BlockSize=this->findChild<QLineEdit*>("BlockSize");
  this->NoBlocksToCache=this->findChild<QLineEdit*>("NBlocksToCache");
  this->BlockCacheSize=this->findChild<QLineEdit*>("BlockCacheSize");

  this->BlockSize->setReadOnly(true);
  this->BlockCacheSize->setReadOnly(true);

  // disconnect read only widgets
  vtkSMProxy *proxy=this->proxy();
  pqPropertyManager *propMgr=this->propertyManager();
  pqNamedWidgets::unlinkObject(this->BlockSize,proxy,"BlockSize",propMgr);
  pqNamedWidgets::unlinkObject(this->BlockCacheSize,proxy,"BlockCacheSize",propMgr);

  // initialize ui
  this->UpdateDecompDims();
  this->UpdateNoBlocksCached();
  this->UpdateNoBlocksCachedValidator();

  // Connect decomp dims to the subsetting ui
  for (int q=0; q<6; ++q)
    {
    connect(
        this->SubsetUI[q],
        SIGNAL(valueChanged(int)),
        this,
        SLOT(UpdateDecompDims(void)));
    }

  // connect decomp dims to block / cache size
  for (int q=0; q<3; ++q)
    {
    connect(
        this->DecompDims[q],
        SIGNAL(textEdited(const QString&)),
        this,
        SLOT(UpdateBlockSize(void)));

    connect(
        this->DecompDims[q],
        SIGNAL(textEdited(const QString&)),
        this,
        SLOT(UpdateNoBlocksCachedValidator(void)));

    connect(
        this->DecompDims[q],
        SIGNAL(textEdited(const QString&)),
        this,
        SLOT(UpdateNoBlocksCached(void)));
    }

  // connect cache size
  connect(
      this->NoBlocksToCache,
      SIGNAL(textEdited(const QString&)),
      this,
      SLOT(UpdateBlockCacheSize(void)));
}

//-----------------------------------------------------------------------------
pqSQBOVMetaReader::~pqSQBOVMetaReader()
{
  #if defined pqSQBOVMetaReaderDEBUG
  cerr << ":::::::::::::::::::::::::::::::pqSQBOVMetaReader::~pqSQBOVMetaReader" << endl;
  #endif
}

//-----------------------------------------------------------------------------
unsigned long int pqSQBOVMetaReader::GetLargeBlockSize()
{
  int nCellsTotal[3];
  this->Subset.Size(nCellsTotal);

  unsigned long int blockSize=1;

  for (int q=0; q<3; ++q)
    {
    unsigned int dim=this->DecompDims[q]->text().toUInt();
    blockSize*=(nCellsTotal[q]/dim + (nCellsTotal[q]%dim?1:0));
    }

  // array size to memory consumption
  blockSize*=sizeof(float);

  return blockSize;
}

//-----------------------------------------------------------------------------
unsigned long int pqSQBOVMetaReader::GetNoBlocks()
{
  unsigned long int nBlocks=1;
  for (int q=0; q<3; ++q)
    {
    nBlocks*=this->DecompDims[q]->text().toUInt();
    }

  return nBlocks;
}

//-----------------------------------------------------------------------------
QString pqSQBOVMetaReader::FormatMemoryUsage(unsigned long int n)
{
  QString s;

  if (n<1024)
    {
    s=QString("%1 B").arg(n);
    }
  else
  if (n<1048576)
    {
    double v=((double)n)/1024.0;
    s=QString("%1 kB").arg(v,0,'f',1);
    }
  else
  if (n<1073741824)
    {
    double v=((double)n)/1048576.0;
    s=QString("%1 MB").arg(v,0,'f',1);
    }
  else
    {
    double v=((double)n)/1073741824.0;
    s=QString("%1 GB").arg(v,0,'f',1);
    }

  return s;
}

//-----------------------------------------------------------------------------
void pqSQBOVMetaReader::UpdateDecompDims()
{
  this->Subset.Set(
        this->SubsetUI[0]->value(),
        this->SubsetUI[1]->value(),
        this->SubsetUI[2]->value(),
        this->SubsetUI[3]->value(),
        this->SubsetUI[4]->value(),
        this->SubsetUI[5]->value());

  int nCells[3];
  this->Subset.Size(nCells);

  // sanity - prevent log(0)
  if (nCells[0]<1 || nCells[1]<1 || nCells[2]<1)
    {
    return;
    }

  int nBlocks=1;

  for (int q=0; q<3; ++q)
    {
    this->DecompDims[q]->setValidator(0);
    this->DecompDims[q]->setValidator(new QIntValidator(1,nCells[q],this));

    if (nCells[q]<100)
      {
      this->DecompDims[q]->setText(QString("%1").arg(1));
      }
    else
      {
      int p=log10((float)nCells[q]);
      int b=pow(10.0,p-2);
      int m=nCells[q]/((int)pow(10.0,p));
      int dim=m*b;
      nBlocks*=dim;
      this->DecompDims[q]->setText(QString("%1").arg(dim));
      }
    }

  this->UpdateBlockSize();
}

//-----------------------------------------------------------------------------
void pqSQBOVMetaReader::UpdateBlockSize()
{
  unsigned long int blockSize=this->GetLargeBlockSize();

  QString memUse
    =QString("%1 Scalar / %2 Vector")
        .arg(this->FormatMemoryUsage(blockSize))
          .arg(this->FormatMemoryUsage(3*blockSize));

  this->BlockSize->setText(memUse);

  // warn about 2GB block size limit.
  if (blockSize<2147483648)
    {
    this->BlockSize->setStyleSheet("color:black; background-color:white;");
    }
  else
    {
    this->BlockSize->setStyleSheet("color:red; background-color:lightyellow;");
    }
}

//-----------------------------------------------------------------------------
void pqSQBOVMetaReader::UpdateBlockCacheSize()
{
  unsigned long int nBlocks=this->NoBlocksToCache->text().toUInt();
  unsigned long maxScalar=sizeof(float)*this->Subset.Size();
  unsigned long int blockSize=min(maxScalar,this->GetLargeBlockSize());
  unsigned long int cacheSize=blockSize*nBlocks;

  QString memUse
    =QString("%1 Scalar / %2 Vector")
        .arg(this->FormatMemoryUsage(cacheSize))
          .arg(this->FormatMemoryUsage(3*cacheSize));

  this->BlockCacheSize->setText(memUse);
}

//-----------------------------------------------------------------------------
void pqSQBOVMetaReader::UpdateNoBlocksCached()
{
  unsigned long int nBlocks=this->GetNoBlocks();
  unsigned long maxScalar=sizeof(float)*this->Subset.Size();
  unsigned long int blockSize=3*min(maxScalar,this->GetLargeBlockSize());
  unsigned long int cacheSize=blockSize*nBlocks;

  while (nBlocks>1 && cacheSize>4294967296)
    {
    --nBlocks;
    cacheSize=blockSize*nBlocks;
    }

  this->NoBlocksToCache->setText(QString("%1").arg(nBlocks));

  this->UpdateBlockCacheSize();
}

//-----------------------------------------------------------------------------
void pqSQBOVMetaReader::UpdateNoBlocksCachedValidator()
{
  unsigned long int nBlocks=this->GetNoBlocks();

  this->NoBlocksToCache->setValidator(0);
  this->NoBlocksToCache->setValidator(new QIntValidator(1,nBlocks,this));
}

/// Print the widgets type and names from the autogenerated panel.
//   QList<QWidget *> list=this->findChildren<QWidget *>();
//   for (int i = 0; i<list.size(); ++i) 
//     {
//     QWidget *w=list.at(i);
//     cerr
//       << w->metaObject()->className() << " "
//       << w->objectName().toStdString() << endl;
//     }

