/*
    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 "structure/manifestxml.h"
#include "structure/metaxml.h"
#include "structure/stylesxml.h"

#include <quazip/quazipfile.h>
#include <quazip/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 (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);

}


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() << "OdsDocWriter::close";
    _p_content->close();
    delete _p_content;
    StylesXml stylesXml(_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(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;
}
