/* This file is part of the KDE project
   SPDX-FileCopyrightText: 2008 Carlos Licea <carlos.licea@kdemail.net>
   SPDX-FileCopyrightText: 2008 Thorsten Zachmann <zachmann@kde.org>

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

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

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA  02110-1301  USA.
*/

// Qt's includes
#include <QColor>
#include <QTime>

const QString Filterkpr2odf::createPageStyle(const KoXmlElement &page)
{
    KoGenStyle style(KoGenStyle::DrawingPageAutoStyle, "drawing-page");
    style.setAutoStyleInStylesDotXml(m_sticky);

    bool useMasterBackground = false;
    if (page.nodeName() == "PAGE") {
        KoXmlElement backMaster = page.namedItem("BACKMASTER").toElement();
        if (!backMaster.isNull()) {
            style.addProperty("presentation:background-visible", backMaster.attribute("displayBackground", "1") == "1");
            style.addProperty("presentation:background-objects-visible", backMaster.attribute("displayMasterPageObject", "1") == "1");
            useMasterBackground =
                backMaster.attribute("useMasterBackground", "1") == "1"; // not set was the default and it means use the background from master
        } else {
            // if BACKMASTER is not found we assume it's true
            style.addProperty("presentation:background-visible", true);
            style.addProperty("presentation:background-objects-visible", true);
            style.addProperty("draw:fill", "none");
        }
    }

    if (!page.hasChildNodes()) { // we check if this is an empty page
        return m_styles.insert(style, "dp");
    }

    if (!useMasterBackground) {
        // it is not an empty page let's keep loading
        KoXmlElement backType = page.namedItem("BACKTYPE").toElement();
        if (backType.isNull() || backType.attribute("value") == "0") {
            // it's some form of color, plain or a gradient
            KoXmlElement bcType = page.namedItem("BCTYPE").toElement();
            if (bcType.isNull() || bcType.attribute("value") == "0") {
                // background is a plain color
                QString color = page.namedItem("BACKCOLOR1").toElement().attribute("color");
                // if the backcolor is not present it's implicitly white
                // unless a draw:fill is found, in which case even though a
                // draw:fill-color is not present it's black in KPresenter2.0
                style.addProperty("draw:fill", "solid");
                style.addProperty("draw:fill-color", page.namedItem("BACKCOLOR1").toElement().attribute("color", "#ffffff"));
            } else {
                // background is a gradient
                style.addProperty("draw:fill", "gradient");
                style.addProperty("draw:fill-gradient-name", createGradientStyle(page));
            }
        } else {
            // it's a picture instead
            QString pictureName = getPictureNameFromKey(page.namedItem("BACKPICTUREKEY").toElement());
            KoXmlElement backView = page.namedItem("BACKVIEW").toElement();
            style.addProperty("draw:fill", "bitmap");

            // The image is specified by a draw:fill-image style in draw:fill-image-name
            KoGenStyle drawFillImage(KoGenStyle::FillImageStyle);

            // default values
            drawFillImage.addAttribute("xlink:href", "Pictures/" + m_pictures[pictureName]);
            drawFillImage.addAttribute("xlink:type", "simple");
            drawFillImage.addAttribute("xlink:show", "embed");
            drawFillImage.addAttribute("xlink:actuate", "onLoad");
            QString repeat;
            if (backView.isNull()) {
                // the picture is just centered
                repeat = "no-repeat";
            } else if (backView.attribute("value") == "0") {
                // the picture is stretched
                repeat = "stretch";
            } else if (backView.attribute("value") == "2") {
                // picture is in mosaic
                repeat = "repeat";
            }
            style.addProperty("style:repeat", repeat);

            style.addProperty("draw:fill-image-name", m_styles.insert(drawFillImage, "picture"));
        }
    }

    // Add the duration of the page effect
    KoXmlElement pageDuration = page.namedItem("PGTIMER").toElement();
    if (!pageDuration.isNull()) {
        QTime time;
        time = time.addSecs(pageDuration.attribute("timer", "0").toInt());
        style.addProperty("presentation:duration", time.toString("'PT'hh'H'mm'M'ss'S'"));
        style.addProperty("presentation:transition-type", "automatic");
    }

    // Add the page effect
    // Enum: PageEffect
    KoXmlElement pageEffect = page.namedItem("PGEFFECT").toElement();
    if (!pageEffect.isNull()) {
        QString effectName("none"); // we default to none
        int effect = pageEffect.attribute("value", "0").toInt();
        switch (effect) {
        case -1: // PEF_RANDOM
            effectName = "random";
            break;
        case 0: // PEF_NONE
            effectName = "none";
            break;
        case 1: // PEF_CLOSE_HORZ
            effectName = "close-horizontal";
            break;
        case 2: // PEF_CLOSE_VERT
            effectName = "close-vertical";
            break;
        case 3: // PEF_CLOSE_ALL
            effectName = "close";
            break;
        case 4: // PEF_OPEN_HORZ
            effectName = "open-horizontal";
            break;
        case 5: // PEF_OPEN VERT
            effectName = "open-vertical";
            break;
        case 6: // PEF_OPEN_ALL
            effectName = "open";
            break;
        case 7: // PEF_INTERLOCKING_HORZ_1
            effectName = "interlocking-horizontal-left";
            break;
        case 8: // PEF_INTERLOCKING_HORZ_2
            effectName = "interlocking-horizontal-right";
            break;
        case 9: // PEF_INTERLOCKING_VERT_1
            effectName = "interlocking-vertical-up";
            break;
        case 10: // PEF_INTERLOCKING_VERT_2
            effectName = "interlocking-vertical-bottom";
            break;
        case 11: // PEF_SURROUND1
            effectName = "spiralin-left";
            break;
        case 12: // PEF_FLY1
            effectName = "fly-away";
            break;
        case 13: // PEF_BLINDS_HOR
            effectName = "horizontal-stripes";
            break;
        case 14: // PEF_BLINDS_VER
            effectName = "vertical-stripes";
            break;
        case 15: // PEF_BOX_IN
            effectName = "fade-to-center";
            break;
        case 16: // PEF_BOX_OUT
            effectName = "fade-from-center";
            break;
        case 17: // PEF_CHECKBOARD_ACROSS
            effectName = "horizontal-checkerboard";
            break;
        case 18: // PEF_CHECKBOARD_DOWN
            effectName = "vertical-checkerboard";
            break;
        case 19: // PEF_COVER_DOWN
            effectName = "fade-from-top";
            break;
        case 20: // PEF_UNCOVER_DOWN
            effectName = "uncover-to-bottom";
            break;
        case 21: // PEF_COVER_UP
            effectName = "fade-from-bottom";
            break;
        case 22: // PEF_UNCOVER_UP
            effectName = "uncover-to-top";
            break;
        case 23: // PEF_COVER_LEFT
            effectName = "fade-from-right";
            break;
        case 24: // PEF_UNCOVER_LEFT
            effectName = "uncover-to-left";
            break;
        case 25: // PEF_COVER_RIGHT
            effectName = "fade-from-left";
            break;
        case 26: // PEF_UNCOVER_RIGHT
            effectName = "uncover-to-right";
            break;
        case 27: // PEF_COVER_LEFT_UP
            effectName = "fade-from-lowerright";
            break;
        case 28: // PEF_UNCOVER_LEFT_UP
            effectName = "uncover-to-upperleft";
            break;
        case 29: // PEF_COVER_LEFT_DOWN
            effectName = "fade-from-upperrigh";
            break;
        case 30: // PEF_UNCOVER_LEFT_DOWN
            effectName = "uncover-to-lowerleft";
            break;
        case 31: // PEF_COVER_RIGHT_UP
            effectName = "fade-from-lowerleft";
            break;
        case 32: // PEF_UNCOVER_RIGHT_UP
            effectName = "uncover-to-upperright";
            break;
        case 33: // PEF_COVER_RIGHT_DOWN
            effectName = "fade-from-upperleft";
            break;
        case 34: // PEF_UNCOVER_RIGHT_DOWN
            effectName = "fade-from-lowerleft";
            break;
        case 35: // PEF_DISSOLVE
            effectName = "dissolve";
            break;
        case 36: // PEF_STRIPS_LEFT_UP
            effectName = "fade-from-lowerright";
            break;
        case 37: // PEF_STRIPS_LEFT_DOWN
            effectName = "fade-from-upperright";
            break;
        case 38: // PEF_STRIPS_RIGHT_UP
            effectName = "fade-from-lowerleft";
            break;
        case 39: // PEF_STRIPS_RIGHT_DOWN
            effectName = "fade-from-upperleft";
            break;
        case 40: // PEF_MELTING
            effectName = "melt";
            break;
        } // switch effect
        style.addProperty("presentation:transition-style", effectName);

        // Add the sound
        KoXmlElement soundEffect = page.namedItem("PGSOUNDEFFECT").toElement();
        if (!soundEffect.isNull() && soundEffect.attribute("soundEffect") != "0") {
            // As this is a "complex" tag we add it "manually"
            QBuffer buffer;
            buffer.open(QIODevice::WriteOnly);
            KoXmlWriter elementWriter(&buffer);
            //          FIXME:   elementWriter.startElement( "style:presentation-properties" );
            elementWriter.startElement("presentation:sound");
            elementWriter.addAttribute("xlink:href", "Sounds/" + m_sounds[soundEffect.attribute("soundFileName")]);
            elementWriter.addAttribute("xlink:type", "simple");
            elementWriter.addAttribute("xlink:show", "new");
            elementWriter.addAttribute("xlink:actuate", "onRequest");
            elementWriter.endElement(); // presentation:sound
            //             elementWriter.endElement();//style:presentation-properties

            QString elementContents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
            style.addChildElement("presentationSound", elementContents);
        }
    } // if pageEffect is null

    return m_styles.insert(style, "dp");
}

const QString Filterkpr2odf::createGradientStyle(const KoXmlElement &gradientElement)
{
    KoGenStyle style(KoGenStyle::GradientStyle);

    // KPresenter didn't allow to customize those attributes
    style.addAttribute("draw:start-intensity", "100%");
    style.addAttribute("draw:end-intensity", "100%");
    style.addAttribute("draw:border", "0%");
    // Check whether the gradient belongs to a page or to an object
    int type = 1; // we default to 1
    if (gradientElement.nodeName() == "PAGE" || gradientElement.nodeName() == "MASTERPAGE") {
        KoXmlElement backColor1 = gradientElement.namedItem("BACKCOLOR1").toElement();
        if (!backColor1.isNull()) {
            style.addAttribute("draw:start-color", backColor1.attribute("color"));
        } else {
            style.addAttribute("draw:end-color", "#ffffff");
        }

        KoXmlElement backColor2 = gradientElement.namedItem("BACKCOLOR2").toElement();
        if (!backColor2.isNull()) {
            style.addAttribute("draw:end-color", backColor2.attribute("color"));
        } else {
            style.addAttribute("draw:end-color", "#ffffff");
        }

        KoXmlElement bcType = gradientElement.namedItem("BCTYPE").toElement();
        if (!bcType.isNull()) {
            type = bcType.attribute("value").toInt();
        }

        KoXmlElement bGradient = gradientElement.namedItem("BGRADIENT").toElement();
        if (!bGradient.isNull()) {
            if (bGradient.attribute("unbalanced") == "0") {
                style.addAttribute("draw:cx", "50%");
                style.addAttribute("draw:cy", "50%");
            } else {
                int cx = bGradient.attribute("xfactor").toInt();
                int cy = bGradient.attribute("yfactor").toInt();
                style.addAttribute("draw:cx", QString("%1%").arg(cx / 4 + 50));
                style.addAttribute("draw:cy", QString("%1%").arg(cy / 4 + 50));
            }
        }
    } else {
        style.addAttribute("draw:start-color", gradientElement.attribute("color1"));
        style.addAttribute("draw:end-color", gradientElement.attribute("color2"));
        type = gradientElement.attribute("type").toInt();
        if (gradientElement.hasAttribute("unbalanced")) {
            if (gradientElement.attribute("unbalanced") == "0") {
                style.addAttribute("draw:cx", "50%");
                style.addAttribute("draw:cy", "50%");
            } else {
                int cx = gradientElement.attribute("xfactor").toInt();
                int cy = gradientElement.attribute("yfactor").toInt();
                style.addAttribute("draw:cx", QString("%1%").arg(cx / 4 + 50));
                style.addAttribute("draw:cy", QString("%1%").arg(cy / 4 + 50));
            }
        }
    }

    // export the type of the gradient
    QString typeString;
    QString angle;
    switch (type) {
    case 1:
        typeString = "linear";
        angle = '0';
        break;
    case 2:
        typeString = "linear";
        angle = "900";
        break;
    case 3:
        typeString = "linear";
        angle = "450";
        break;
    case 4:
        typeString = "linear";
        angle = "135";
        break;
    case 5:
        typeString = "radial";
        angle = '0';
        break;
    case 6:
        typeString = "square";
        angle = '0';
        break;
    case 7:
        typeString = "axial";
        angle = '0';
        break;
    } // switch type
    style.addAttribute("draw:style", typeString);
    style.addAttribute("draw:angle", angle);

    return m_styles.insert(style, "gr");
}

const QString Filterkpr2odf::createPageLayout()
{
    // Create the page-layout that is: paper, header and footer sizes
    KoXmlElement paper = m_mainDoc.namedItem("DOC").namedItem("PAPER").toElement();
    KoXmlElement paperBorders = paper.namedItem("PAPERBORDERS").toElement();

    // page-layout-properties
    KoGenStyle style(KoGenStyle::PageLayoutStyle);
    style.setAutoStyleInStylesDotXml(true);

    if (paperBorders.hasAttribute("ptTop")) {
        style.addPropertyPt("fo:margin-top", paperBorders.attribute("ptTop").toDouble());
    }
    if (paperBorders.hasAttribute("ptBottom")) {
        style.addPropertyPt("fo:margin-bottom", paperBorders.attribute("ptBottom").toDouble());
    }
    if (paperBorders.hasAttribute("ptLeft")) {
        style.addPropertyPt("fo:margin-left", paperBorders.attribute("ptLeft").toDouble());
    }
    if (paperBorders.hasAttribute("ptRight")) {
        style.addPropertyPt("fo:margin-right", paperBorders.attribute("ptRight").toDouble());
    }
    if (paper.hasAttribute("ptWidth")) {
        style.addPropertyPt("fo:page-width", paper.attribute("ptWidth").toDouble());
    }
    if (paper.hasAttribute("ptHeight")) {
        style.addPropertyPt("fo:page-height", paper.attribute("ptHeight").toDouble());
    }
    style.addProperty("style:print-orientation", "landscape");

    // NOTE: header-style and footer-style are not present because in KPresenter they are treated as text boxes

    return m_styles.insert(style, "pm");
}

const QString Filterkpr2odf::createMasterPageStyle(const KoXmlNode &objects, const KoXmlElement &masterBackground)
{
    // KoXmlElement header( m_mainDoc.namedItem( "DOC" ).namedItem( "HEADER" ).toElement() );
    // KoXmlElement footer( m_mainDoc.namedItem( "DOC" ).namedItem( "FOOTER" ).toElement() );

    // set that we work on master
    m_sticky = true;

    KoGenStyle style(KoGenStyle::MasterPageStyle);
    style.addAttribute("style:page-layout-name", createPageLayout());

    style.addAttribute("draw:style-name", createPageStyle(masterBackground));

    QBuffer buffer;
    buffer.open(QIODevice::WriteOnly);
    KoXmlWriter xmlWriter(&buffer);

    convertObjects(&xmlWriter, objects);

    m_sticky = false;

    QString contentElement = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
    style.addChildElement("master", contentElement);

    return m_styles.insert(style, "Default");
}
// TODO: load the tags still missing
const QString Filterkpr2odf::createGraphicStyle(const KoXmlElement &element)
{
    // A graphic style is wiely used by a broad type of objects, hence can have many different properties
    KoGenStyle style(KoGenStyle::GraphicAutoStyle, "graphic");
    style.setAutoStyleInStylesDotXml(m_sticky);

    KoXmlElement textObject(element.namedItem("TEXTOBJ").toElement());
    if (!textObject.isNull()) {
        if (textObject.hasAttribute("verticalAlign")) {
            QString textAligment = textObject.attribute("verticalAlign");
            if (textAligment == "center") {
                // all the other values are valid except center that is called middle in ODF
                textAligment = "middle";
            }
            style.addProperty("draw:textarea-vertical-align", textAligment);
        }
        if (textObject.hasAttribute("bleftpt")) {
            style.addPropertyPt("fo:padding-left", textObject.attribute("bleftpt").toDouble());
        }
        if (textObject.hasAttribute("bbottompt")) {
            style.addPropertyPt("fo:padding-bottom", textObject.attribute("bbottompt").toDouble());
        }
        if (textObject.hasAttribute("btoppt")) {
            style.addPropertyPt("fo:padding-top", textObject.attribute("btoppt").toDouble());
        }
        if (textObject.hasAttribute("brightpt")) {
            style.addPropertyPt("fo:padding-right", textObject.attribute("brightpt").toDouble());
        }
    }

    KoXmlElement pen = element.namedItem("PEN").toElement();
    if (!pen.isNull()) {
        style.addPropertyPt("svg:stroke-width", pen.attribute("width").toDouble());
        style.addProperty("svg:stroke-color", pen.attribute("color"));

        QString stroke;
        int strokeStyle = pen.attribute("style").toInt();
        if (strokeStyle == 1) {
            stroke = "solid";
        } else if (strokeStyle >= 2 && strokeStyle <= 5) {
            stroke = "dash";
            style.addProperty("draw:stroke-dash", createStrokeDashStyle(strokeStyle));
        } else {
            stroke = "none";
        }
        style.addProperty("draw:stroke", stroke);
    } else {
        // even if the pen is not present for some objects it has to have a black stroke
        if (element.attribute("type") == "1" || element.attribute("type") == "2" || element.attribute("type") == "3" || element.attribute("type") == "5"
            || element.attribute("type") == "8" || element.attribute("type") == "11" || element.attribute("type") == "12" || element.attribute("type") == "13"
            || element.attribute("type") == "14" || element.attribute("type") == "15" || element.attribute("type") == "16") {
            style.addProperty("draw:stroke", "solid");
            style.addProperty("svg:stroke-width", "1px");
            style.addProperty("svg:stroke-color", "#000000");
        }
    }

    // We now define what's the object filled with, we "default" to a brush if both attributes are present
    KoXmlElement brush = element.namedItem("BRUSH").toElement();
    KoXmlElement fillType = element.namedItem("FILLTYPE").toElement();

    int fillTypeValue = 0;
    if (!fillType.isNull()) {
        fillTypeValue = fillType.attribute("value").toInt();
    }

    KoXmlElement gradient = element.namedItem("GRADIENT").toElement();
    KoXmlElement filename = element.namedItem("FILENAME").toElement();
    bool isConnection = filename.attribute("value").startsWith("Connections");
    QString fill;
    if (!brush.isNull() && !isConnection && fillTypeValue == 0) {
        QString fillColor(brush.attribute("color"));

        int brushStyle = brush.attribute("style").toInt();
        if (brushStyle == 1) {
            fill = "solid";
            style.addProperty("draw:fill-color", fillColor);
        } else if (brushStyle >= 9 && brushStyle <= 14) {
            fill = "hatch";
            style.addProperty("draw:fill-hatch-name", createHatchStyle(brushStyle, fillColor));
        } else if (brushStyle >= 2 && brushStyle <= 8) {
            // in KPR files transparency was used in ODF opacity is used instead calculated as 100-transparency
            int opacity = 100;
            if (brushStyle == 2)
                opacity = 6;
            else if (brushStyle == 3)
                opacity = 12;
            else if (brushStyle == 4)
                opacity = 37;
            else if (brushStyle == 5)
                opacity = 50;
            else if (brushStyle == 6)
                opacity = 63;
            else if (brushStyle == 7)
                opacity = 88;
            else if (brushStyle == 8)
                opacity = 94;
            // Opacity is a gradient, in this case an uniform one
            style.addProperty("draw:opacity", createOpacityGradientStyle(opacity));
        }
    } else if (!gradient.isNull() && fillTypeValue == 1) {
        fill = "gradient";
        style.addProperty("draw:fill-gradient-name", createGradientStyle(gradient));
    } else {
        fill = "none";
    }

    if (!fill.isNull()) { // don't save ""
        style.addProperty("draw:fill", fill);
    }

    KoXmlElement lineBegin = element.namedItem("LINEBEGIN").toElement();
    if (!lineBegin.isNull()) {
        style.addProperty("draw:marker-start-width", "0.25cm");

        int markerStyle = lineBegin.attribute("value").toInt();
        style.addProperty("draw:marker-start", createMarkerStyle(markerStyle));
    }

    KoXmlElement lineEnd = element.namedItem("LINEEND").toElement();
    if (!lineEnd.isNull()) {
        style.addProperty("draw:marker-end-width", "0.25cm");

        int markerStyle = lineEnd.attribute("value").toInt();
        style.addProperty("draw:marker-end", createMarkerStyle(markerStyle));
    }

    KoXmlElement shadow = element.namedItem("SHADOW").toElement();
    if (!shadow.isNull()) {
        style.addProperty("draw:shadow", "visible");
        style.addProperty("draw:shadow-color", shadow.attribute("color"));

        QString shadowOffsetX;
        QString shadowOffsetY;
        int direction = shadow.attribute("direction").toInt();
        QString distance = QString("%1pt").arg(shadow.attribute("distance").toDouble());
        // Enum: ShadowDirection
        switch (direction) {
        case 1: // Left Up
            shadowOffsetX = '-' + distance;
            shadowOffsetY = '-' + distance;
            break;
        case 2: // Up
            shadowOffsetX = "0pt";
            shadowOffsetY = '-' + distance;
            break;
        case 3: // Right Up
            shadowOffsetX = distance;
            shadowOffsetY = '-' + distance;
            break;
        case 4: // Right
            shadowOffsetX = distance;
            shadowOffsetY = "0pt";
            break;
        case 5: // Right Bottom
            shadowOffsetX = distance;
            shadowOffsetY = distance;
            break;
        case 6: // Bottom
            shadowOffsetX = "0pt";
            shadowOffsetY = distance;
            break;
        case 7: // Left Bottom
            shadowOffsetX = '-' + distance;
            shadowOffsetY = distance;
            break;
        case 8: // Left
            shadowOffsetX = '-' + distance;
            shadowOffsetY = "0pt";
            break;
        }
        style.addProperty("draw:shadow-offset-x", shadowOffsetX);
        style.addProperty("draw:shadow-offset-y", shadowOffsetY);
    }

    // If what we're loading is the style for an image and some form of mirror, luminance, greyscale... is applied to it we must load it in the GrpahicStyle
    KoXmlElement pictureSettings = element.namedItem("PICTURESETTINGS").toElement();
    if (!pictureSettings.isNull()) {
        int mirrorType = pictureSettings.attribute("mirrorType", "0").toInt();
        QString mirror;
        switch (mirrorType) {
        case 0: // normal
            break;
        case 1:
            mirror = "horizontal";
            break;
        case 2:
            mirror = "vertical";
            break;
        case 3:
            mirror = "vertical horizontal";
            break;
        }
        if (!mirror.isNull()) {
            style.addProperty("style:mirror", mirror);
        }

        bool grayscal = pictureSettings.attribute("grayscal", "0") == "1";
        if (grayscal) {
            style.addProperty("draw:color-mode", "greyscale");
        }

        int bright = pictureSettings.attribute("bright", "0").toInt();
        if (bright != 0) {
            style.addProperty("draw:luminance", QString("%1%").arg(bright));
        }

        bool swapRGB = pictureSettings.attribute("bright", "0") == "1";
        if (swapRGB) {
            style.addProperty("draw:color-inversion", "true");
        }

        // NOTE: depth is not portable
    }

    //     style.addAttribute( "style:parent-style-name", "standard" ); TODO: add the standard Graphic style

    return m_styles.insert(style, "gr");
}

const QString Filterkpr2odf::createOpacityGradientStyle(int opacity)
{
    // Opacity wasn't a gradient in KPR so we go from and to the same value
    KoGenStyle style(KoGenStyle::OpacityStyle);
    QString opacityString = QString("%1%").arg(opacity);
    style.addAttribute("draw:start", opacityString);
    style.addAttribute("draw:end", opacityString);
    return m_styles.insert(style, "op");
}

const QString Filterkpr2odf::createMarkerStyle(int markerType)
{
    KoGenStyle style(KoGenStyle::MarkerStyle);

    QString displayName;
    QString viewBox;
    QString d;

    // Enum: LineEnd
    switch (markerType) {
    case 0: // Normal
        // nothing
        break;
    case 1:
        displayName = "Arrow";
        viewBox = "0 0 20 30";
        d = "m10 0-10 30h20z";
        break;
    case 2:
        displayName = "Square";
        viewBox = "0 0 10 10";
        d = "m0 0h10v10h-10z";
        break;
    case 3:
        displayName = "Circle";
        viewBox = "0 0 1131 1131";
        d = "m462 1118-102-29-102-51-93-72-72-93-51-102-29-102-13-105 13-102 29-106 51-102 72-89 93-72 102-50 102-34 106-9 101 9 106 34 98 50 93 72 72 89 51 "
            "102 29 106 13 102-13 105-29 102-51 102-72 93-93 72-98 51-106 29-101 13z";
        break;
    case 4:
        displayName = "Line Arrow";
        viewBox = "0 0 1122 2243";
        d = "m0 2108v17 17l12 42 30 34 38 21 43 4 29-8 30-21 25-26 13-34 343-1532 339 1520 13 42 29 34 39 21 42 4 42-12 34-30 21-42v-39-12l-4 "
            "4-440-1998-9-42-25-39-38-25-43-8-42 8-38 25-26 39-8 42z";
        break;
    case 5:
        displayName = "Dimension Lines";
        viewBox = "0 0 836 110";
        d = "m0 0h278 278 280v36 36 38h-278-278-280v-36-36z";
        break;
    case 6:
        displayName = "Double Arrow";
        viewBox = "0 0 1131 1918"; // FIXME: same as Double line arrow, not sure if it's ok, nothing in KPresenter1.6
        d = "m737 1131h394l-564-1131-567 1131h398l-398 787h1131z";
        break;
    case 7:
        displayName = "Double Line Arrow";
        viewBox = "0 0 1131 1918";
        d = "m0 11h312 312h122z";
        break;
    } // switch markerType

    style.addAttribute("draw:display-name", displayName);
    style.addAttribute("draw:viewBox", viewBox);
    style.addAttribute("draw:d", d);

    return m_styles.insert(style, "mks");
}

const QString Filterkpr2odf::createStrokeDashStyle(int strokeStyle)
{
    KoGenStyle style(KoGenStyle::StrokeDashStyle);

    //"Containment" strings, filled according to the type of the strokeStyle
    QString displayName;
    QString styleString;
    QString dots1;
    QString dots1_length;
    QString dots2;
    QString dots2_length;
    QString distance;

    switch (strokeStyle) {
    case 2:
        displayName = "Fine Dashed";
        styleString = "rect";
        dots1 = '1';
        dots1_length = "0.508cm";
        dots2 = '1';
        dots2_length = "0.508cm";
        distance = "0.508cm";
        break;
    case 3:
        displayName = "Fine Dotted";
        styleString = "rect";
        dots1 = '1';
        distance = "0.257cm";
        break;
    case 4:
        displayName = "Ultrafine 1 Dot 1 Dash";
        styleString = "rect";
        dots1 = '1';
        dots1_length = "0.051cm";
        dots2 = '1';
        dots2_length = "0.254cm";
        distance = "0.127cm";
        break;
    case 5:
        displayName = "2 Dots 1 Dash";
        styleString = "rect";
        dots1 = '2';
        dots2 = '1';
        dots2_length = "0.203cm";
        distance = "0.203cm";
        break;
    } // switch strokeStyle

    // Not all the strings are filled always so in oder to not
    // flood the style with unneeded "", we check if it was written
    style.addAttribute("draw:display-name", displayName);
    style.addAttribute("draw:style", styleString);
    style.addAttribute("draw:dots1", dots1);
    style.addAttribute("draw:distance", distance);
    if (!dots1_length.isNull()) {
        style.addAttribute("draw:dots1-length", dots1_length);
    }
    if (!dots2.isNull()) {
        style.addAttribute("draw:dots2", dots2);
    }
    if (!dots2_length.isNull()) {
        style.addAttribute("draw:dots2-length", dots2_length);
    }

    return m_styles.insert(style, "stroke");
}

const QString Filterkpr2odf::createHatchStyle(int brushStyle, const QString &fillColor)
{
    KoGenStyle style(KoGenStyle::HatchStyle);

    //"Containment" strings
    QString displayName;
    QString styleString;
    QString distance;
    QString rotation;

    // Other numbers aren't needed because those aren't a hatch style
    switch (brushStyle) {
    case 9:
        displayName = fillColor + " 0 Degrees";
        styleString = "single";
        distance = "0.102cm";
        rotation = '0';
        break;
    case 10:
        displayName = fillColor + " 90 Degrees";
        styleString = "single";
        distance = "0.102cm";
        rotation = "900";
        break;
    case 11:
        displayName = fillColor + " Crossed 0 Degrees";
        styleString = "double";
        distance = "0.076cm";
        rotation = "900";
        break;
    case 12:
        displayName = fillColor + " 45 Degrees";
        styleString = "single";
        distance = "0.102cm";
        rotation = "450";
        break;
    case 13:
        displayName = fillColor + " -45 Degrees";
        styleString = "single";
        distance = "0.102cm";
        rotation = "3150";
        break;
    case 14:
        displayName = fillColor + " Crossed 45 Degrees";
        styleString = "double";
        distance = "0.076cm";
        rotation = "450";
        break;
    }

    style.addAttribute("draw:display-name", displayName);
    style.addAttribute("draw:style", styleString);
    style.addAttribute("draw:color", fillColor);
    style.addAttribute("draw:distance", distance);
    style.addAttribute("draw:rotation", rotation);

    return m_styles.insert(style, "hs");
}

const QString Filterkpr2odf::createParagraphStyle(const KoXmlElement &element)
{
    KoGenStyle style(KoGenStyle::ParagraphAutoStyle, "paragraph");
    style.setAutoStyleInStylesDotXml(m_sticky);

    QString textAlign;
    if (element.hasAttribute("align")) {
        int align = element.attribute("align").toInt();
        switch (align) {
        case 0: // left
            textAlign = "start";
            break;
        case 2: // right
            textAlign = "end";
            break;
        case 4: // center
            textAlign = "center";
            break;
        case 8: // justify
            textAlign = "justify";
            break;
        }
    }
    if (!textAlign.isNull()) {
        style.addProperty("fo:text-align", textAlign);
    }

    KoXmlElement indents = element.namedItem("INDENTS").toElement();
    if (!indents.isNull()) {
        if (indents.hasAttribute("left")) {
            style.addPropertyPt("fo:margin-left", indents.attribute("left").toDouble());
        }
        if (indents.hasAttribute("right")) {
            style.addPropertyPt("fo:margin-right", indents.attribute("right").toDouble());
        }
        if (indents.hasAttribute("first")) {
            style.addPropertyPt("fo:text-indent", indents.attribute("first").toDouble());
        }
    }

    KoXmlElement offsets = element.namedItem("OFFSETS").toElement();
    if (!offsets.isNull()) {
        if (offsets.hasAttribute("before")) {
            style.addPropertyPt("fo:margin-top", offsets.attribute("before").toDouble());
        }
        if (offsets.hasAttribute("after")) {
            style.addPropertyPt("fo:margin-bottom", offsets.attribute("after").toDouble());
        }
    }

    KoXmlElement lineSpacing = element.namedItem("LINESPACING").toElement();
    if (!lineSpacing.isNull()) {
        QString type = lineSpacing.attribute("type");
        QString lineHeight;
        double lineSpacingDouble = -1;
        double lineHeightAtLeast = -1;
        if (type == "single") {
            lineHeight = "100%";
        } else if (type == "oneandhalf") {
            lineHeight = "150%";
        } else if (type == "double") {
            lineHeight = "200%";
        } else if (type == "multiple") {
            lineHeight = QString("%1%").arg(lineSpacing.attribute("spacingvalue").toInt() * 100);
        } else if (type == "custom") {
            lineSpacingDouble = lineSpacing.attribute("spacingvalue").toDouble();
        } else if (type == "atleast") {
            lineHeightAtLeast = lineSpacing.attribute("spacingvalue").toDouble();
        }

        if (!lineHeight.isNull()) {
            style.addProperty("fo:line-height", lineHeight);
        }
        if (lineSpacingDouble != -1) {
            style.addPropertyPt("text:line-spacing", lineSpacingDouble);
        }
        if (lineHeightAtLeast != -1) {
            style.addPropertyPt("style:line-height-at-least", lineHeightAtLeast);
        }
    }

    KoXmlElement leftBorder = element.namedItem("LEFTBORDER").toElement();
    KoXmlElement rightBorder = element.namedItem("RIGHTBORDER").toElement();
    KoXmlElement topBorder = element.namedItem("TOPBORDER").toElement();
    KoXmlElement bottomBorder = element.namedItem("BOTTOMBORDER").toElement();
    if (!leftBorder.isNull()) {
        style.addProperty("fo:border-left", convertBorder(leftBorder));
    }
    if (!rightBorder.isNull()) {
        style.addProperty("fo:border-right", convertBorder(rightBorder));
    }
    if (!topBorder.isNull()) {
        style.addProperty("fo:border-top", convertBorder(topBorder));
    }
    if (!bottomBorder.isNull()) {
        style.addProperty("fo:border-left", convertBorder(bottomBorder));
    }

    return m_styles.insert(style, "P");
}

QString Filterkpr2odf::convertBorder(const KoXmlElement &border)
{
    QString style;
    int styleInt = border.attribute("style").toInt();
    if (styleInt == 5) {
        style = "double";
    } else {
        style = "solid";
    }
    QString width = QString("%1pt").arg(border.attribute("width").toDouble());

    QColor color(border.attribute("red").toInt(), border.attribute("green").toInt(), border.attribute("blue").toInt());

    return QString("%1 %2 %3").arg(width).arg(style).arg(color.name());
}

const QString Filterkpr2odf::createTextStyle(const KoXmlElement &element)
{
    KoGenStyle style(KoGenStyle::TextAutoStyle, "text");
    style.setAutoStyleInStylesDotXml(m_sticky);

    if (element.hasAttribute("family")) {
        style.addProperty("style:font-name", element.attribute("family"));
    }
    if (element.hasAttribute("pointSize")) {
        style.addPropertyPt("fo:font-size", element.attribute("pointSize").toDouble());
    }
    if (element.hasAttribute("color")) {
        style.addProperty("fo:color", element.attribute("color"));
    }
    if (element.hasAttribute("bold")) {
        if (element.attribute("bold") == "1") {
            style.addProperty("fo:font-weight", "bold");
        }
    }
    if (element.hasAttribute("italic")) {
        if (element.attribute("italic") == "1") {
            style.addProperty("fo:font-style", "italic");
        }
    }
    if (element.hasAttribute("strikeOut")) {
        QString strikeOut = element.attribute("strikeOut");
        QString textLineThroughType;
        QString textLineThroughWidth;
        if (strikeOut == "single") {
            textLineThroughType = "single";
        } else if (strikeOut == "single-bold") {
            textLineThroughType = "single";
            textLineThroughWidth = "bold";
        } else if (strikeOut == "double") {
            textLineThroughType = "double";
        }
        style.addProperty("style:text-line-through-type", textLineThroughType);
        style.addProperty("style:text-line-through-style", "solid");
        if (!textLineThroughWidth.isNull()) { // avoid saving ""
            style.addProperty("style:text-line-through-width", textLineThroughWidth);
        }
    }
    if (element.hasAttribute("underline")) {
        QString underline = element.attribute("underline");
        QString underlineStyleLine = element.attribute("underlinestyleline");

        style.addProperty("style:text-underline-color", element.attribute("underlinecolor"));

        QString textUnderlineStyle;
        QString textUnderlineWidth;
        QString textUnderlineType;
        if (underlineStyleLine == "solid") {
            if (underline == "1") {
                textUnderlineType = "single";
                textUnderlineStyle = "solid";
            } else if (underline == "single-bold") {
                textUnderlineType = "single";
                textUnderlineStyle = "solid";
                textUnderlineWidth = "bold";
            } else if (underline == "double") {
                textUnderlineType = "double";
                textUnderlineStyle = "solid";
            } else if (underline == "wave") {
                textUnderlineType = "single";
                textUnderlineStyle = "wave";
            }
        } // if underlineStyleLine == solid
        else if (underlineStyleLine == "dot") {
            if (underline == "1") {
                textUnderlineType = "single";
                textUnderlineStyle = "dotted";
            } else if (underline == "single-bold") {
                textUnderlineType = "single";
                textUnderlineStyle = "dotted";
                textUnderlineWidth = "bold";
            }
        } else if (underlineStyleLine == "dash") {
            textUnderlineType = "single";
            textUnderlineStyle = "dash";
        }

        style.addProperty("style:text-underline-type", textUnderlineType);
        style.addProperty("style:text-underline-style", textUnderlineStyle);
        if (!textUnderlineWidth.isNull()) { // avoid saving ""
            style.addProperty("style:text-underline-width", textUnderlineWidth);
        }
    } // if element.hasAttribute( "strikeOut" )

    QString shadow = element.attribute("text-shadow");
    if (!shadow.isNull()) {
        style.addProperty("fo:text-shadow", shadow);
    }

    return m_styles.insert(style, "T");
}

const QString Filterkpr2odf::createListStyle(const KoXmlElement &element)
{
    KoGenStyle style(KoGenStyle::ListAutoStyle);
    style.setAutoStyleInStylesDotXml(m_sticky);

    static const int s_oasisCounterTypes[] = {
        '\0',
        '1',
        'a',
        'A',
        'i',
        'I',
        '\0',
        '\0', // custombullet, custom
        0x2022, // circle -> small disc
        0xE00A, // square
        0x25CF, // disc -> large disc
        0x27A2 // box -> right-pointing triangle
    };

    KoXmlElement counter = element.namedItem("COUNTER").toElement();

    QBuffer buffer;
    buffer.open(QIODevice::WriteOnly);
    KoXmlWriter elementWriter(&buffer, 3);

    int type = counter.attribute("type").toInt();
    bool isBullet = false;
    if (6 <= type) {
        isBullet = true;
    }

    int depth = counter.attribute("depth", "0").toInt();
    elementWriter.startElement((isBullet) ? "text:list-level-style-bullet" : "text:list-level-style-number");
    elementWriter.addAttribute("text:level", depth + 1);

    if (isBullet) {
        QChar bulletChar;
        if (type == 6) { // STYLE_CUSTOMBULLET
            bulletChar = QChar(counter.attribute("bullet").toInt());
        } else {
            bulletChar = QChar(s_oasisCounterTypes[type]);
        }
        elementWriter.addAttribute("text:bullet-char", QString(bulletChar));
    } else {
        if (type != 7) { // STYLE_CUSTOM in 1.6 was not implemented, I assume it's never used
            elementWriter.addAttribute("style:number-format", s_oasisCounterTypes[type]);
        }
        if (counter.attribute("restart", "0") == "1" || counter.attribute("restart") == "true") {
            QString start = counter.attribute("start", "1");
            elementWriter.addAttribute("text:start-value", start);
        }
    }

    elementWriter.addAttribute("style:num-prefix", counter.attribute("lefttext"));
    elementWriter.addAttribute("style:num-suffix", counter.attribute("righttext"));
    elementWriter.endElement(); // text:list-level-style-bullet or text:list-level-style-number

    QString align;
    switch ((Qt::AlignmentFlag)element.attribute("align", "0").toInt()) {
    case Qt::AlignLeft:
        align = "left";
        break;
    case Qt::AlignRight:
        align = "right";
        break;
    case Qt::AlignHCenter:
        align = "center";
        break;
    case Qt::AlignJustify:
        align = "justify";
        break;
    default:
        align = "start";
        break;
    }
    elementWriter.startElement("text:list-level-properties");
    elementWriter.addAttribute("fo:text-align", align);
    elementWriter.endElement(); //

    QString elementContent = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
    style.addChildElement("listLevelStyle", elementContent);

    return m_styles.insert(style, "L");
}
