/*
    libodsstream is a library to read and write ODS documents as streams
    Copyright (C) 2013  Olivier Langella <Olivier.Langella@moulon.inra.fr>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "odsdocwriter.h"
#include "odsexception.h"
#include "writer/structure/manifestxml.h"
#include "writer/structure/metaxml.h"
#include "writer/structure/stylesxml.h"

#include <quazipfile.h>
#include <quacrc32.h>
#include <QDebug>
#include <QFileInfo>

OdsDocWriter::OdsDocWriter(const QString &filename)
{
  _p_device_delete = true;
  QFile *p_file    = new QFile(filename);
  try
    {
      openDevice(p_file);
    }
  catch(const OdsException &ods_error)
    {
      throw OdsException(
        QObject::tr("error opening ODS file. check file permission for %1")
          .arg(QFileInfo(filename).absoluteFilePath()));
    }
}

OdsDocWriter::OdsDocWriter(QIODevice *device)
{
  openDevice(device);
}

void
OdsDocWriter::openDevice(QIODevice *device)
{
  //_p_device_delete = false;
  _p_device    = device;
  _p_quaZip    = new QuaZip(device);
  bool open_ok = _p_quaZip->open(QuaZip::mdCreate);

  if(open_ok == false)
    {
      throw OdsException(QObject::tr("error opening ODS file."));
    }
  // QuaZipFile::open(): file open mode 2 incompatible with ZIP open mode 0
  // QuaZip::close(): ZIP is not open


  //_p_quaZip->setComment("application/vnd.oasis.opendocument.spreadsheet");
  QByteArray *messageIn = new QByteArray();
  *messageIn            = "application/vnd.oasis.opendocument.spreadsheet";

  QuaZipFile outFile(_p_quaZip);
  QuaCrc32 crc32;
  quint32 crc(crc32.calculate(*messageIn));
  QuaZipNewInfo info("mimetype");
  info.uncompressedSize = messageIn->length();
  outFile.open(QIODevice::WriteOnly, info, NULL, crc, 0, 0, true);
  outFile.write(*messageIn);
  outFile.close();

  delete(messageIn);

  ManifestXml manifest(_p_quaZip);
  MetaXml metaxml(_p_quaZip);
  _p_content = new ContentXml(_p_quaZip, &_settings_xml);
}


OdsDocWriter::~OdsDocWriter()
{
  // this->close();
  if(_p_content != nullptr)
    {
      this->close();
    }
}

/**
 * ends the document creation. It is required to obtain a correct output
 *
 */
void
OdsDocWriter::close()
{
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  if(_p_content != nullptr)
    {
      _p_content->close();
      delete _p_content;
    }
  StylesXml stylesXml(_p_quaZip);
  _settings_xml.write(_p_quaZip);
  _p_quaZip->close();
  delete _p_quaZip;

  if(_p_device != nullptr)
    {
      //_p_device->flush();
      _p_device->close();
    }
  _p_content = nullptr;
  _p_quaZip  = nullptr;
  // zipOutputStream = null;

  if(_p_device != nullptr)
    {
      if(_p_device_delete)
        {
          qDebug() << "_p_device_delete";
          delete _p_device;
        }
    }
}


/**
 * adds a new datasheet to the document
 *
 * @param sheetName
 * @throws XMLStreamException
 */
void
OdsDocWriter::writeSheet(const QString &sheetName)
{
  _p_content->writeSheet(sheetName);
}

/**
 * adds a new line to the document
 *
 * @throws XMLStreamException
 */
void
OdsDocWriter::writeLine()
{
  _p_content->writeLine();
}


void
OdsDocWriter::clearAnnotation()
{
  _next_annotation = "";
}
/**
 * adds a new text cell to the document
 *
 * @param text
 * @throws XMLStreamException
 */
void
OdsDocWriter::writeCell(const QString &text)
{
  _p_content->writeCell(text, _next_annotation);
  clearAnnotation();
  //     if (text == null) {
  //         this.writeEmptyCell();
  //         return;
  //     }
  //     this.content.writeCell(text);
}

void
OdsDocWriter::writeCell(const char *text)
{
  QString textQt(text);
  _p_content->writeCell(textQt, _next_annotation);
  clearAnnotation();
  //     if (text == null) {
  //         this.writeEmptyCell();
  //         return;
  //     }
  //     this.content.writeCell(text);
}

/**
 * writes an empty cell
 *
 * @throws XMLStreamException
 */
void
OdsDocWriter::writeEmptyCell()
{
  _p_content->writeEmptyCell(_next_annotation);
  clearAnnotation();
  // this.content.writeCell("");
}

/**
 * adds a new cell containing an integer
 *
 * @param value
 * @throws XMLStreamException
 */
void
OdsDocWriter::writeCell(std::size_t value)
{
  _p_content->writeCell(value, _next_annotation);
  clearAnnotation();
  // this.content.writeCell(value);
}


/**
 * adds a new cell containing an integer
 *
 * @param value
 * @throws XMLStreamException
 */
void
OdsDocWriter::writeCell(int value)
{
  _p_content->writeCell(value, _next_annotation);
  clearAnnotation();
  // this.content.writeCell(value);
}

/**
 * adds a new cell containing a float
 *
 * @param value
 * @throws XMLStreamException
 */
void
OdsDocWriter::writeCell(float value)
{
  _p_content->writeCell(value, _next_annotation);
  clearAnnotation();
  // this.content.writeCell(value);
}

/**
 * adds a new cell containing a double
 *
 * @param value
 * @throws XMLStreamException
 */
void
OdsDocWriter::writeCell(double value)
{
  _p_content->writeCell(value, _next_annotation);
  clearAnnotation();
  // this.content.writeCell(value);
}


/**
 * adds a new cell containing a percentage
 *
 * @param value
 * @throws XMLStreamException
 */
void
OdsDocWriter::writeCellPercentage(double value)
{
  _p_content->writeCellPercentage(value, _next_annotation);
  clearAnnotation();
}

/**
 * adds a new cell containing a boolean
 *
 * @param value
 * @throws XMLStreamException
 */
void
OdsDocWriter::writeCell(bool value)
{
  _p_content->writeCell(value, _next_annotation);
  clearAnnotation();
  // this.content.writeCell(value);
}

/**
 * adds a new cell containing a date
 *
 * @param date
 * @throws XMLStreamException
 * @throws DatatypeConfigurationException
 */
void
OdsDocWriter::writeCell(const QDate &date)
{
  _p_content->writeCell(date, _next_annotation);
  clearAnnotation();
  //     if (date == null) {
  //         this.writeEmptyCell();
  //         return;
  //     }
  //     this.content.writeCell(date);
}

/**
 * adds a new cell containing a timestamp
 *
 * @param date
 * @throws XMLStreamException
 * @throws DatatypeConfigurationException
 */
void
OdsDocWriter::writeCell(const QDateTime &date)
{
  _p_content->writeCell(date, _next_annotation);
  clearAnnotation();
  //     if (date == null) {
  //         this.writeEmptyCell();
  //         return;
  //     }
  //     this.content.writeCell(new Date(date.getTime()));
}

/**
 * NOT WORKING, adds a new cell containing a duration
 *
 * @param duration
 * @throws XMLStreamException
 * @throws DatatypeConfigurationException
 */
// void OdsDocWriter::writeCell(Duration duration)  {
//     if (duration == null) {
//         this.writeEmptyCell();
//         return;
//     }
//     this.content.writeCell(duration);
// }

/**
 * adds a new cell containing a text inside an href link
 *
 * @param theUri
 * @param text
 * @throws XMLStreamException
 */
void
OdsDocWriter::writeCell(const QUrl &theUri, const QString &text)
{
  _p_content->writeCell(theUri, text, _next_annotation);
  clearAnnotation();
}


/**
 * get a table cell style reference with a style definition
 *
 * @param style OdsTableCellStyle
 * @return OdsTableCellStyleRef pointer on a style reference
 */
OdsTableCellStyleRef
OdsDocWriter::getTableCellStyleRef(const OdsTableCellStyle &style)
{

  return _p_content->getTableCellStyleRef(style);
}

void
OdsDocWriter::setTableCellStyleRef(OdsTableCellStyleRef style_ref)
{
  _p_content->setTableCellStyleRef(style_ref);
}


void
OdsDocWriter::setCellAnnotation(const QString &annotation)
{
  _next_annotation = annotation;
}

void
OdsDocWriter::addColorScale(const OdsColorScale &ods_color_scale)
{
  _p_content->addColorScale(ods_color_scale);
}


QString
OdsDocWriter::getOdsCellCoordinate()
{
  return _p_content->getCellCoordinate();
}

void
OdsDocWriter::setCurrentOdsTableSettings(const OdsTableSettings &settings_in)
{
  _settings_xml.setCurrentOdsTableSettings(settings_in);
}
