/***************************************************************************
**
**  This file is part of QGpCompatibility.
**
**  QGpCompatibility is free software: you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation, either version 3 of the License, or
**  (at your option) any later version.
**
**  QGpCompatibility is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with Foobar.  If not, see <http://www.gnu.org/licenses/>
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2003-05-22
**  Copyright: 2003-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QGpCoreTools.h>
#include "CompatAutocorrReport.h"
#include "CompatAutocorrData.h"
#include "CompatEllipticityData.h"

namespace QGpCompatibility {

CompatAutocorrReport::CompatAutocorrReport(bool isWrite, QString reportFile, QString naInFile, int nModels) :
    CompatInversionReport(isWrite, reportFile, naInFile, nModels)
{
  TRACE;
  _currentAutocorr=0;
}

CompatAutocorrReport::~CompatAutocorrReport()
{
  TRACE;
  delete _currentAutocorr;
}

bool CompatAutocorrReport::isA(const char * className)
{
  TRACE;
  return strcmp(className, "CompatAutocorrReport" )==0;
}

bool CompatAutocorrReport::initCurrentAutocorr()
{
  TRACE;
  ASSERT( !_currentAutocorr);
  _currentAutocorr=new CompatAutocorrCurves();
  if(startReadingBlock( _blockGoal, AutocorrGoal) ) {
    if(_version >= 2) {
      uint rayleighModesCount;
      _s >> rayleighModesCount;
      _currentAutocorr->setRayleighModesCount(rayleighModesCount);
    }
    _currentAutocorr->reportToRadius(_s);
    if(startReadingBlock( _blockOmegas, Omegas) )
      _currentAutocorr->reportToOmega(_s);
    else {
      Message::critical(MSG_ID,"Inversion report", "Omegas at index 1", Message::cancel());
      return false;
    }
  } else Message::critical(MSG_ID,"Inversion report", "Radius at index 2", Message::cancel());
  if(_version < 2)
    _currentAutocorr->setRayleighModesCount(_currentAutocorr->modesCount());
  return true;
}

void CompatAutocorrReport::addAutocorrGoal(CompatAutocorrData * ad)
{
  TRACE;
  // tests if omegas are entered
  if( !startReadingBlock(_blockOmegas, Omegas) ) {
    fprintf(stderr, "You must call addOmegas before calling addDispersionGoal\n" );
    return ;
  }
  startWritingBlock(AutocorrGoal);
  if(ad) {
    _s << ad->rayleighModesCount();
    ad->dataToReport(_s);
  }
  endWritingBlock();
}

void CompatAutocorrReport::modifyAutocorrGoal(CompatAutocorrData * ad)
{
  TRACE;
  startWritingBlock(_blockGoal, AutocorrGoal);
  if(ad) {
    _s << ad->rayleighModesCount();
    ad->dataToReport(_s);
  }
  endWritingBlock();
}

void CompatAutocorrReport::addModel(const Seismic1DModel * model,
                                  float * naModel,
                                  const CompatDispersion * disp,
                                  const CompatAutocorrCurves * ac,
                                  double cost, int modelID)
{
  TRACE;
  // Write new model at the end of file
  if(startWritingBlock( ModelDispersion) ) {
    if(modelID < 0) {
      _s << _currentID;
      _currentID++;
    } else {
      if(modelID >= _currentID) _currentID=modelID + 1;
      _s << modelID;
    }
    model->toStream(_s);
    _s << cost;
    writeNAModel(naModel);
    disp->valuesToReport(_s);
    disp->refinesToReport(_s);
    ac->valuesToReport(_s);
  } else fprintf(stderr, "Maximum number of models is reached, model not added\n" );
  endWritingBlock();
  setMaxOmegasCount(disp->refinesCount(), 0);
}

void CompatAutocorrReport::addModel(const Seismic1DModel * model,
                                  float * naModel,
                                  const CompatDispersion * disp,
                                  const CompatEllipticity * ell,
                                  const CompatAutocorrCurves * ac,
                                  double cost, int modelID)
{
  TRACE;
  // Write new model at the end of file
  if(startWritingBlock( ModelDispersion) ) {
    if(modelID < 0) {
      _s << _currentID;
      _currentID++;
    } else {
      if(modelID >= _currentID) _currentID=modelID + 1;
      _s << modelID;
    }
    model->toStream(_s);
    _s << cost;
    writeNAModel(naModel);
    disp->valuesToReport(_s);
    disp->refinesToReport(_s);
    ell->valuesToReport(_s);
    ell->refinesToReport(_s);
    ac->valuesToReport(_s);
  } else fprintf(stderr, "Maximum number of models is reached, model not added\n" );
  endWritingBlock();
  setMaxOmegasCount(disp->refinesCount(), ell->refinesCount());
}

bool CompatAutocorrReport::loadAutocorr(int modelIndex)
{
  TRACE;
  if( !_currentModel) initCurrentModel();
  if( !_currentNAModel) _currentNAModel=new float [ _nd ];
  if( !_currentDispersion) initCurrentDispersion();
  if( !_currentAutocorr && !initCurrentAutocorr()) return false;
  if(startReadingBlock( modelIndex + _headerBlocksCount, ModelDispersion) ) {
    _s >> _currentID;
    _currentModel->fromStream(_s);
    _s >> _currentCost;
    if(_version >= 4) _s.readRawData(( char * ) _currentNAModel, _nd * sizeof(float) );
    _currentDispersion->reportToValues(_s);
    if(_version > 0) _currentDispersion->reportToRefines(_s);
    if(_currentEllipticity) {
      _currentEllipticity->reportToValues(_s);
      if(_version > 0) _currentEllipticity->reportToRefines(_s);
    }
    _currentAutocorr->reportToValues(_s);
  } else _currentCost=-1;
  return true;
}

bool CompatAutocorrReport::loadAutocorrOnly(int modelIndex)
{
  TRACE;
  if( !_currentModel) initCurrentModel();
  if( !_currentNAModel) _currentNAModel=new float [ _nd ];
  if( !_currentDispersion) initCurrentDispersion();
  if( !_currentAutocorr && !initCurrentAutocorr()) return false;
  if(startReadingBlock( modelIndex + _headerBlocksCount, ModelDispersion) ) {
    // ID, Model, cost and basic dispersion offset
    int nRefines;
    qint64 blockOffset=_s.device() ->pos();
    int offset=(_currentDispersion->modesCount() * _currentDispersion->omegasCount() +
                   4 * _currentModel->layerCount()) * sizeof(double) + 2 * sizeof(int);
    if(_version >= 4) offset += _nd * sizeof(float);
    if(_version >= 1) {
      _s.device() ->seek(blockOffset + offset);
      _s >> nRefines;
      offset += sizeof(int) + nRefines * 2 * sizeof(double);
    }
    if(_currentEllipticity) {
      offset += _currentDispersion->modesCount() * _currentDispersion->omegasCount() *
                sizeof(double);
      if(_version >= 1) {
        _s.device() ->seek(blockOffset + offset);
        _s >> nRefines;
        offset += sizeof(int) + nRefines * 2 * sizeof(double);
      }
    }
    _s.device() ->seek(blockOffset + offset);
    _currentAutocorr->reportToValues(_s);
  }
  return true;
}

bool CompatAutocorrReport::loadAutocorr(CompatAutocorrCurves * ac, int modelIndex)
{
  TRACE;
  if( !_currentModel) initCurrentModel();
  if( !_currentNAModel) _currentNAModel=new float [ _nd ];
  if( !_currentDispersion) initCurrentDispersion();
  if(startReadingBlock( _blockGoal, AutocorrGoal) ) {
    ac->reportToRadius(_s);
    if(startReadingBlock( _blockOmegas, Omegas) ) {
      ac->reportToOmega(_s);
      if(startReadingBlock( modelIndex + _headerBlocksCount, ModelDispersion) ) {
        _s >> _currentID;
        _currentModel->fromStream(_s);
        _s >> _currentCost;
        if(_version >= 4) _s.readRawData(( char * ) _currentNAModel, _nd * sizeof(float) );
        _currentDispersion->reportToValues(_s);
        if(_version > 0) _currentDispersion->reportToRefines(_s);
        if(_currentEllipticity) {
          _currentEllipticity->reportToValues(_s);
          if(_version > 0) _currentEllipticity->reportToRefines(_s);
        }
        ac->reportToValues(_s);
      }
    } else {
      Message::critical(MSG_ID,"Inversion report", "Omegas at index 1", Message::cancel());
      return false;
    }
  } else {
    Message::critical(MSG_ID,"Inversion report", "Radius at index 2", Message::cancel());
    return false;
  }
  return true;
}

CompatAutocorrData * CompatAutocorrReport::loadAutocorrGoal()
{
  TRACE;
  if( !_currentAutocorr) initCurrentAutocorr();
  CompatAutocorrData * ad=new CompatAutocorrData(_currentAutocorr);
  if(startReadingBlock( _blockGoal, AutocorrGoal) ) {
    if(_version >= 2) {
      uint rayleighModesCount;
      _s >> rayleighModesCount;
      ad->setRayleighModesCount(rayleighModesCount);
    }
    ad->reportToData(_s);
    ad->reportToDataWeight(_s, _version >= 3);
  } else fprintf(stderr, "Unable to read block AutocorrGoal in current file\n" );
  return ad;
}

} // namespace QGpCompatibility
