/* OdLib_Io - Copyright (c) 2007-2020 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.mapeadores.opendocument.io;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.AbstractList;
import java.util.Collection;
import java.util.List;
import java.util.RandomAccess;
import java.util.function.Consumer;
import net.mapeadores.opendocument.elements.ElementMaps;
import net.mapeadores.opendocument.io.odtable.StyleManager;
import net.mapeadores.opendocument.io.odtable.TableSettings;
import net.mapeadores.util.exceptions.NestedIOException;
import net.mapeadores.util.mimetype.MimeTypeConstants;
import net.mapeadores.util.xml.AppendableXMLWriter;
import net.mapeadores.util.xml.XMLUtils;
import net.mapeadores.util.xml.XMLWriter;


/**
 *
 * @author Vincent Calame
 */
public final class OdUtils {

    public final static short OD_TEXT = 1;
    public final static short OD_SPREADSHEET = 2;
    public final static short OD_DATABASE = 3;
    public final static short OD_CHART = 4;
    public final static short OD_FORMULA = 5;
    public final static short OD_DRAWING = 6;
    public final static short OD_IMAGE = 7;
    public final static short OD_MASTER = 8;
    public final static short OD_PRESENTATION = 9;
    public final static OdSource EMPTY_ODSOURCE = new EmptyOdSource();

    private OdUtils() {
    }

    public static String getMimeType(short odType) {
        switch (odType) {
            case OD_TEXT:
                return MimeTypeConstants.ODT;
            case OD_SPREADSHEET:
                return MimeTypeConstants.ODS;
            case OD_DATABASE:
                return "application/vnd.oasis.opendocument.database";
            case OD_CHART:
                return "application/vnd.oasis.opendocument.chart";
            case OD_FORMULA:
                return "application/vnd.oasis.opendocument.formula";
            case OD_DRAWING:
                return "application/vnd.oasis.opendocument.graphics";
            case OD_IMAGE:
                return "application/vnd.oasis.opendocument.image";
            case OD_MASTER:
                return "application/vnd.oasis.opendocument.text-master";
            case OD_PRESENTATION:
                return "application/vnd.oasis.opendocument.presentation";
            default:
                throw new IllegalArgumentException("unknown odType = " + odType);
        }
    }

    public static String getExtension(short odType) {
        switch (odType) {
            case OD_TEXT:
                return "odt";
            case OD_SPREADSHEET:
                return "ods";
            case OD_DATABASE:
                return "odb";
            case OD_CHART:
                return "odc";
            case OD_FORMULA:
                return "odf";
            case OD_DRAWING:
                return "odg";
            case OD_IMAGE:
                return "odi";
            case OD_MASTER:
                return "odm";
            case OD_PRESENTATION:
                return "odp";
            default:
                throw new IllegalArgumentException("unknown odType = " + odType);
        }
    }

    public static boolean isOdExtension(String extension) {
        try {
            getOdType(extension);
            return true;
        } catch (IllegalArgumentException iae) {
            return false;
        }
    }

    public static short getOdType(String extension) {
        if (extension.equals("odt")) {
            return OD_TEXT;
        } else if (extension.equals("ods")) {
            return OD_SPREADSHEET;
        } else if (extension.equals("odb")) {
            return OD_DATABASE;
        } else if (extension.equals("odc")) {
            return OD_CHART;
        } else if (extension.equals("odf")) {
            return OD_FORMULA;
        } else if (extension.equals("odg")) {
            return OD_DRAWING;
        } else if (extension.equals("odi")) {
            return OD_IMAGE;
        } else if (extension.equals("odm")) {
            return OD_MASTER;
        } else if (extension.equals("odp")) {
            return OD_PRESENTATION;
        } else {
            throw new IllegalArgumentException("wrong extension = " + extension);
        }
    }

    public static List<Pictures.Entry> wrap(Pictures.Entry[] array) {
        return new PictureList(array);
    }

    public static void writeSpreadSheetDocumentContent(OutputStream outputStream, Consumer<Writer> spreadSheetProducer, StyleManager styleManager) throws IOException {
        BufferedWriter buf = new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8"));
        AppendableXMLWriter xmlWriter = XMLUtils.toXMLWriter(buf);
        xmlWriter.appendXMLDeclaration();
        OdXML.openDocumentContent(xmlWriter);
        OdXML.openAutomaticStyles(xmlWriter);
        styleManager.insertAutomaticStyles(xmlWriter);
        OdXML.closeAutomaticStyles(xmlWriter);
        OdXML.openBody(xmlWriter);
        OdXML.openSpreadsheet(xmlWriter);
        try {
            spreadSheetProducer.accept(buf);
        } catch (NestedIOException ioe) {
            throw ioe.getIOException();
        }
        OdXML.closeSpreadsheet(xmlWriter);
        OdXML.closeBody(xmlWriter);
        OdXML.closeDocumentContent(xmlWriter);
        buf.flush();
    }

    public static OdSource getSettingsOdSource(Collection<TableSettings> settings) {
        return new SettingsOdSource(settings);
    }

    public static void writeSettings(OutputStream outputStream, Collection<TableSettings> settings) throws IOException {
        BufferedWriter buf = new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8"));
        AppendableXMLWriter xmlWriter = XMLUtils.toXMLWriter(buf);
        xmlWriter.appendXMLDeclaration();
        OdXML.openDocumentSettings(xmlWriter);
        OdXML.openSettings(xmlWriter);
        OdXML.openConfigItemSet(xmlWriter, "ooo:view-settings");
        OdXML.openConfigItemMapIndexed(xmlWriter, "Views");
        OdXML.openConfigItemMapEntry(xmlWriter, null);
        OdXML.addConfigItem(xmlWriter, "ViewId", "string", "view1");
        OdXML.openConfigItemMapNamed(xmlWriter, "Tables");
        for (TableSettings tableSettings : settings) {
            writeTableSettings(xmlWriter, tableSettings);
        }
        OdXML.closeConfigItemMapNamed(xmlWriter);
        OdXML.closeConfigItemMapEntry(xmlWriter);
        OdXML.closeConfigItemMapIndexed(xmlWriter);
        OdXML.closeConfigItemSet(xmlWriter);
        OdXML.closeSettings(xmlWriter);
        OdXML.closeDocumentSettings(xmlWriter);
        buf.flush();
    }


    public static OdSource toStyleOdSource(ElementMaps elementMaps, boolean isSpreadSheet) {
        return new CharSequenceOdSource(toStyleXml(elementMaps, isSpreadSheet), "UTF-8");
    }

    public static String toStyleXml(ElementMaps elementMaps, boolean isSpreadSheet) {
        StringBuilder buf = new StringBuilder();
        AppendableXMLWriter xmlWriter = XMLUtils.toXMLWriter(buf);
        try {
            xmlWriter.appendXMLDeclaration();
            StylesXMLPart stylesPart = new StylesXMLPart(xmlWriter);
            stylesPart.openStyleDocument();
            stylesPart.openStyles();
            if (elementMaps != null) {
                stylesPart.insertStyles(elementMaps, isSpreadSheet);
            }
            stylesPart.closeStyles();
            stylesPart.closeStyleDocument();
        } catch (IOException ioe) {

        }
        return buf.toString();
    }

    private static void writeTableSettings(XMLWriter xmlWriter, TableSettings tableSettings) throws IOException {
        OdXML.openConfigItemMapEntry(xmlWriter, tableSettings.tableName());
        int fixedColumns = tableSettings.fixedColumns();
        int fixedRows = tableSettings.fixedRows();
        if (fixedColumns > 0) {
            OdXML.addConfigItem(xmlWriter, "HorizontalSplitMode", "short", "2");
            OdXML.addConfigItem(xmlWriter, "HorizontalSplitPosition", "int", String.valueOf(fixedColumns));
            OdXML.addConfigItem(xmlWriter, "PositionLeft", "int", "0");
            OdXML.addConfigItem(xmlWriter, "PositionRight", "int", String.valueOf(fixedColumns));
        }
        if (fixedRows > 0) {
            OdXML.addConfigItem(xmlWriter, "VerticalSplitMode", "short", "2");
            OdXML.addConfigItem(xmlWriter, "VerticalSplitPosition", "int", String.valueOf(fixedRows));
            OdXML.addConfigItem(xmlWriter, "PositionTop", "int", "0");
            OdXML.addConfigItem(xmlWriter, "PositionBottom", "int", String.valueOf(fixedRows));
        }
        OdXML.closeConfigItemMapEntry(xmlWriter);
    }


    private static class EmptyOdSource implements OdSource {

        private EmptyOdSource() {

        }

        @Override
        public void writeStream(OutputStream outputStream) throws IOException {

        }

    }


    private static class PictureList extends AbstractList<Pictures.Entry> implements RandomAccess {

        private final Pictures.Entry[] array;

        private PictureList(Pictures.Entry[] array) {
            this.array = array;
        }

        @Override
        public int size() {
            return array.length;
        }

        @Override
        public Pictures.Entry get(int index) {
            return array[index];
        }

    }


    private static class SettingsOdSource implements OdSource {

        private final Collection<TableSettings> settings;

        private SettingsOdSource(Collection<TableSettings> settings) {
            this.settings = settings;
        }

        @Override
        public void writeStream(OutputStream outputStream) throws IOException {
            OdUtils.writeSettings(outputStream, settings);
        }

    }

}
