//=============================================================================
//  MuseScore
//  Music Composition & Notation
//
//  Copyright (C) 2020 MuseScore BVBA and others
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License version 2.
//
//  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 General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "workspacefile.h"

#include <QBuffer>

#include "log.h"
#include "stringutils.h"

#include "thirdparty/qzip/qzipreader_p.h"
#include "thirdparty/qzip/qzipwriter_p.h"

#include "global/xmlreader.h"
#include "global/xmlwriter.h"

using namespace mu::workspace;
using namespace mu::system;

static const QString WS_CONTAINTER_XML_PATH("META-INF/container.xml");

static constexpr std::string_view WS_CONTAINER_TAG("container");
static constexpr std::string_view WS_ROOTFILES_TAG("rootfiles");
static constexpr std::string_view WS_ROOTFILE_TAG("rootfile");
static constexpr std::string_view WS_FULLPATH_ATTRIBUTE("full-path");

WorkspaceFile::WorkspaceFile(const io::path& filePath)
    : m_filePath(filePath)
{}

QByteArray WorkspaceFile::readRootFile()
{
    RetVal<QByteArray> data = fileSystem()->readFile(m_filePath);
    if (!data.ret) {
        LOGE() << data.ret.toString();
        return QByteArray();
    }

    QBuffer buf(&data.val);
    MQZipReader zip(&buf);

    std::string rootFile;
    MetaInfo meta;

    if (meta.read(zip)) {
        rootFile = meta.rootFile();
    } else {
        QVector<MQZipReader::FileInfo> fis = zip.fileInfoList();
        if (!fis.isEmpty()) {
            rootFile = fis.first().filePath.toStdString();
        }
    }

    if (rootFile.empty()) {
        LOGE() << "not found root file: " << m_filePath;
        return QByteArray();
    }

    QByteArray fileData = zip.fileData(QString::fromStdString(rootFile));
    if (fileData.isEmpty()) {
        LOGE() << "failed read root file: " << m_filePath;
        return QByteArray();
    }

    return fileData;
}

bool WorkspaceFile::writeRootFile(const std::string& name, const QByteArray& data)
{
    MQZipWriter zip(m_filePath.toQString());

    MetaInfo meta;
    meta.setRootFile(name);
    meta.write(zip);

    zip.addFile(QString::fromStdString(name), data);

    bool ret = zip.status() == MQZipWriter::NoError;
    if (!ret) {
        LOGE() << "Error while writing workspace, zip status: " << zip.status();
    }

    return ret;
}

void WorkspaceFile::MetaInfo::setRootFile(const std::string& name)
{
    m_rootFile = name;
}

std::string WorkspaceFile::MetaInfo::rootFile() const
{
    return m_rootFile;
}

void WorkspaceFile::MetaInfo::write(MQZipWriter& zip)
{
    QByteArray data;
    writeContainer(&data);
    zip.addFile(WS_CONTAINTER_XML_PATH, data);
}

bool WorkspaceFile::MetaInfo::read(const MQZipReader& zip)
{
    QByteArray container = zip.fileData(WS_CONTAINTER_XML_PATH);
    if (container.isEmpty()) {
        LOGE() << "not found" << WS_CONTAINTER_XML_PATH;
        return false;
    }

    readContainer(container);
    if (m_rootFile.empty()) {
        return false;
    }

    return true;
}

void WorkspaceFile::MetaInfo::readContainer(const QByteArray& data)
{
    XmlReader reader(data);

    while (reader.readNextStartElement()) {
        if (WS_CONTAINER_TAG != reader.tagName()) {
            reader.skipCurrentElement();
            continue;
        }

        while (reader.readNextStartElement()) {
            if (WS_ROOTFILES_TAG != reader.tagName()) {
                reader.skipCurrentElement();
                continue;
            }

            while (reader.readNextStartElement()) {
                if (WS_ROOTFILE_TAG != reader.tagName()) {
                    reader.skipCurrentElement();
                    continue;
                }

                m_rootFile = reader.attribute(WS_FULLPATH_ATTRIBUTE);
                return;
            }
        }
    }
}

void WorkspaceFile::MetaInfo::writeContainer(QByteArray* data) const
{
    IF_ASSERT_FAILED(data) {
        return;
    }

    QBuffer buffer(data);
    buffer.open(IODevice::WriteOnly);

    XmlWriter writer(&buffer);
    writer.writeStartDocument();
    writer.writeStartElement(WS_CONTAINER_TAG);
    writer.writeStartElement(WS_ROOTFILES_TAG);

    writer.writeStartElement(WS_ROOTFILE_TAG);
    writer.writeAttribute(WS_FULLPATH_ATTRIBUTE, m_rootFile);
    writer.writeEndElement();

    writer.writeEndElement();
    writer.writeEndElement();
    writer.writeEndDocument();

    buffer.close();
}
