/***************************************************************************
**
**  This file is part of DinverCore.
**
**  DinverCore 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.
**
**  DinverCore 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: 2005-12-20
**  Copyright: 2005-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QGpCoreTools.h>
#include "ReportWriter.h"
#include "ReportReader.h"
#include "Parameter.h"
#include "RealSpace.h"

namespace DinverCore {

/*!
  \class ReportWriter ReportWriter.h

  See documentation for ReportReader for format specification
*/

bool ReportWriter::initReport(const QString& fileName, const QString& title, Action action)
{
  TRACE;
  QFileInfo fi(fileName);
  if(fi.exists()) {
    if(ReportReader::isReportFormat(fileName)) {
      switch(action) {
      case Overwrite:
        remove(fileName);
        break;
      case Append:
        break;
      case Ask:
        switch(Message::warning(MSG_ID, title,
                                tr("Report file %1 already exists.").arg(fileName),
                                Message::cancel(), tr("Append"), tr("Overwrite"), true)) {
        case Message::Answer0:
          return false;
        case Message::Answer2:
          remove(fileName);
          break;
        default: // automatically append
          break;
        }
        break;
      }
    } else {
      switch(action) {
      case Overwrite:
        remove(fileName);
        break;
      case Append:
        App::log(tr("Cannot append to file %1 because report file has a bad format.\n").arg(fileName) );
        return false;
      case Ask:
        switch(Message::warning(MSG_ID, title,
                                tr("Report file %1 already exists, but it has not a report format.").arg(fileName),
                                Message::cancel(), tr("Overwrite"), QString(), true)) {
        case Message::Answer0:
          return false;
        default:
          remove(fileName);
          break;
        }
        break;
      }
    }
  }
  return true;
}

bool ReportWriter::open()
{
  TRACE;
  QFileInfo fi(_f.fileName());
  if(fi.exists()) {
    ReportReader r(_f.fileName());
    if(r.open()) {
      r.synchronize();
      _blockOffset=r.lastBlockOffset();
      if(_blockOffset>0) _blockOffset-=8;
      r.close();
      App::log(tr("Adding models to an existing inversion report...\n") );
      if(_f.open(QIODevice::ReadWrite)) {
        _s.setDevice(&_f);
        _f.seek(_f.size());
        return true;
      } else {
        App::log(tr("Cannot open file for writing (appending)\n") );
        return false;
      }
    } else {
      App::log(tr("Report already exist but unreadable, cannot append models\n") );
      return false;
    }
  } else {
    App::log(tr("Adding models to a new inversion report...\n") );
    if(_f.open(QIODevice::WriteOnly)) {
      _s.setDevice(&_f);
      _s.writeRawData(REPORT_FILE_TAG, 8);
      _s << (int) REPORT_CURRENT_VERSION;
      return true;
    } else {
      App::log(tr("Cannot open file for writing\n") );
      return false;
    }
  }
}

// TODO: remove after validation of gridded inversion
void ReportWriter::addModel(double misfit, uint paramChecksum, int nd, Parameter ** model)
{
  TRACE;
  // Write offset in previous block if necessary
  if(_blockOffset>0) {
    setCurrentOffset(_s, _blockOffset);
  }
  // Construct header of the new model block
  _s.writeRawData(REPORT_BLOCK_TAG, 4);
  _blockOffset=_f.pos();
  qint64 emptyOffset=0;
  _s << emptyOffset; // room for next block offset
  // Write mifit and model
  _s << misfit;
  // Write the parameterization checksum
  _s << nd;
  _s << paramChecksum;
  for(int i=0;i<nd;i++) {
    _s << model[i]->realValue();
  }
}

void ReportWriter::addModel(double misfit, uint paramChecksum, const RealSpace& parameterSpace)
{
  TRACE;
  // Write offset in previous block if necessary
  if(_blockOffset>0) {
    setCurrentOffset(_s, _blockOffset);
  }
  // Construct header of the new model block
  _s.writeRawData(REPORT_BLOCK_TAG, 4);
  _blockOffset=_f.pos();
  qint64 emptyOffset=0;
  _s << emptyOffset; // room for next block offset
  // Write mifit and model
  _s << misfit;
  // Write the parameterization checksum
  int nd=parameterSpace.allParameterCount();
  _s << nd;
  _s << paramChecksum;
  for(int i=0;i<nd;i++) {
    _s << parameterSpace.parameter(i)->realValue();
  }
}

/*!
  Write the current stream position in an offset table
*/
void ReportWriter::setCurrentOffset(QDataStream& s, qint64 tableOffset)
{
  TRACE;
  qint64 offset=s.device()->pos();
  s.device()->seek(tableOffset);
  s << offset;
  s.device()->seek(offset);
}

/*!
  Write a null entry in an offset table
*/
void ReportWriter::setEmptyOffset(QDataStream& s, qint64 tableOffset)
{
  TRACE;
  qint64 offset=s.device()->pos();
  s.device()->seek(tableOffset);
  tableOffset=0;
  s << tableOffset;
  s.device()->seek(offset);
}

/*!
  Remove an exist report file. Handy to force overwriting an existing report.
*/
void ReportWriter::remove(const QString& fileName)
{
  QFileInfo fi(fileName);
  QDir d(fi.absolutePath());
  d.remove(fi.fileName());
}

} // namespace DinverCore
