/* This file is part of the KDE project
   Copyright (C) 2008 Carlos Licea <carlos.licea@kdemail.net>
   Copyright (C) 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 <QTime>
#include <QColor>

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()) {
        QString marginLeft = QString("%1pt").arg(indents.attribute("left").toDouble());
        QString marginRight = QString("%1pt").arg(indents.attribute("right").toDouble());
        QString textIndent = QString("%1pt").arg(indents.attribute("first").toDouble());

        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 = 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");
}
