/***************************************************************************
                     svggenerator.cpp  -  description
                             -------------------
    begin                : Mo 01.04.2019
    copyright            : (C) 2019-2023 by Andre Simon
    email                : a.simon@mailbox.org
 ***************************************************************************/


/*
This file is part of Highlight.

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

Highlight is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Highlight.  If not, see <http://www.gnu.org/licenses/>.
*/


#include <fstream>
#include <string>
#include <sstream>
#include <algorithm>

#include "version.h"
#include "svggenerator.h"

namespace ansifilter
{

SVGGenerator::SVGGenerator()
    : CodeGenerator ( SVG ),
     fileSuffix(".svg")
{
    spacer = "&#160;";
    newLineTag = "\n";
    styleCommentOpen="/*";
    styleCommentClose="*/";
}

SVGGenerator::~SVGGenerator() = default;


string SVGGenerator::getOpenTag()
{
    ostringstream fmtStream;
    string attrName("style");

    if (applyDynStyles){
        attrName = "class";

        ostringstream fgColStream;
         if (elementStyle.isFgColorSet()) {
            fgColStream << elementStyle.getFgColour().getRed(HTML)
                        << elementStyle.getFgColour().getGreen(HTML)
                        << elementStyle.getFgColour().getBlue(HTML);
         }

        ostringstream bgColStream;
        if (elementStyle.isBgColorSet()) {

            bgColStream << elementStyle.getBgColour().getRed(HTML)
                        << elementStyle.getBgColour().getGreen(HTML)
                        << elementStyle.getBgColour().getBlue(HTML);
        }

        StyleInfo sInfo( fgColStream.str(), bgColStream.str(),
                         elementStyle.isBold(), elementStyle.isItalic(), elementStyle.isConceal(),
                         elementStyle.isBlink(), elementStyle.isUnderline() );

        auto fit = std::find(documentStyles.begin(), documentStyles.end(), sInfo );
        if (fit == documentStyles.end()){
            documentStyles.push_back(sInfo);
            fmtStream << "af_"<< documentStyles.size();
        } else {
            fmtStream << "af_"<< 1+(int)std::distance(documentStyles.begin(), fit);
        }

    } else {

        if (elementStyle.isBold()) {
            fmtStream<< "font-weight:bold;";
        }
        if (elementStyle.isItalic()) {
            fmtStream<< "font-style:italic;";
        }
        if (elementStyle.isBlink()) {
            fmtStream<< "text-decoration:blink;";
        }
        if (elementStyle.isUnderline()) {
            fmtStream<< "text-decoration:underline;";
        }
        if (elementStyle.isConceal()) {
            fmtStream<< "display:none;";
        }

        if (elementStyle.isFgColorSet()) {
            fmtStream << "fill:#"
                    << elementStyle.getFgColour().getRed(HTML)
                    << elementStyle.getFgColour().getGreen(HTML)
                    << elementStyle.getFgColour().getBlue(HTML)
                    << ";";
        }

        /*if (elementStyle.isBgColorSet()) {
            fmtStream <<"background-color:#"
                    << elementStyle.getBgColour().getRed(HTML)
                    << elementStyle.getBgColour().getGreen(HTML)
                    << elementStyle.getBgColour().getBlue(HTML)
                    <<";";
        }*/
    }


    string fmt  = fmtStream.str();
    tagIsOpen = fmt.size()>0;
    if (tagIsOpen) {
        ostringstream spanTag;
        spanTag<< "<tspan "<<attrName<<"=\""<<fmt<<"\">";
        return spanTag.str();
    }
    return "";
}

string SVGGenerator::getCloseTag()
{
    string retVal = tagIsOpen ? "</tspan>"  : "";
    tagIsOpen = false;
    return retVal;
}

string SVGGenerator::getGeneratorComment()
{
    ostringstream s;
    s << "<!--SVG generated by ansifilter "
      << ANSIFILTER_VERSION << ", " <<  ANSIFILTER_URL <<"-->\n";

    return s.str();
}

string SVGGenerator::getHeader()
{
    ostringstream header;
    header << "<?xml version=\"1.0\"";
    if ( encodingDefined() ) {
        header << " encoding=\"" << encoding << "\"";
    }
    header << "?>\n";
    if (!styleSheetPath.empty() ) {
        header  << R"(<?xml-stylesheet type="text/css" href=")"
                << styleSheetPath
                << "\"?>\n";
    }
    header << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.2//EN\" "
           << "\"http://www.w3.org/Graphics/SVG/1.2/DTD/svg12.dtd\">\n";
    header << R"(<svg xmlns="http://www.w3.org/2000/svg" version="1.2" )"
           << R"(baseProfile="full" xml:space="preserve")";
    if ( width.size() ) header << " width=\""<<width<<"\"";
    if ( height.size() ) header << " height=\""<<height<<"\"";

    header << ">\n<desc>" << docTitle << "</desc>\n";

    if ( styleSheetPath.empty() ) {
        header << "<defs><style type=\"text/css\">\n"
            << "<![CDATA[\n";

        header << "rect { fill:#ffffff; } \n";
        header << "g { font-size: " << fontSize;
        header << "; font-family: " << font << "; white-space: pre; }\n";

        header << "]]>\n"
            << "</style></defs>";
    }

    return header.str();
}

void SVGGenerator::printBody()
{
    *out << "<g>\n<rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\"/>"; // rect: background color
    int fontSizeSVG=10;
    StringTools::str2num<int>(fontSizeSVG, fontSize, std::dec);

    *out << "\n<text x=\"10\" y=\""<<fontSizeSVG*2<<"\">";
    processInput();
    *out << "</text>\n</g>\n";
}


string SVGGenerator::getFooter()
{
    ostringstream os;
    os <<"</svg>\n";

    if (true) {
        os  << "<!-- SVG generated by ansifilter "
            << ANSIFILTER_VERSION
            << ", "
            << ANSIFILTER_URL
            <<" -->\n";
    }
    return os.str();
}

string SVGGenerator::maskCharacter ( unsigned  char c )
{
    switch ( c ) {
    case ' ' :
        return spacer;
        break;
    case '<' :
        return "&lt;";
        break;
    case '>' :
        return "&gt;";
        break;
    case '&' :
        return "&amp;";
        break;
    case '\"' :
        return "&quot;";
        break;
    default:
        return string( 1, c );;
    }
}

void SVGGenerator::insertLineNumber()
{

    int fontSizeSVG=10;
    StringTools::str2num<int>(fontSizeSVG, fontSize, std::dec);

    if ( showLineNumbers ) {
        ostringstream lnum;
        lnum << setw ( 5 ) << right;

        if ( numberCurrentLine ) {
            lnum << lineNumber;
            *out<< "</text>\n<text x=\"10\" y=\""<< ( lineNumber*fontSizeSVG*2 ) <<"\">";
            *out << lnum.str() ;
        } else {
            *out << lnum.str(); //for indentation
        }
        *out << " ";
    } else {
        *out<< "</text>\n<text x=\"10\" y=\""<< ( lineNumber*fontSizeSVG*2 ) <<"\">";

    }
}


bool SVGGenerator::printDynamicStyleFile ( const std::string &outPath ) {

    //do not overwrite
    std::ifstream infile(outPath.c_str());
    if (infile.good()) return true;

    ofstream indexfile ( outPath.c_str() );

    if ( !indexfile.fail() ) {
        indexfile << "/* CSS generated by ansifilter - styles derived from document formatting\n   Ansifilter will not overwrite this file\n*/\n";


        indexfile << "rect { fill:#ffffff; }\n";
        indexfile << "g { font-size: " << fontSize;
        indexfile << "; font-family: " << font << "; white-space: pre; }\n";

        for (unsigned int i=0; i<documentStyles.size();i++){
            StyleInfo sInfo = documentStyles[i];
            indexfile << "tspan.af_" << (i+1) <<" {";

            if (sInfo.isBold) {
                indexfile<< "font-weight:bold;";
            }
            if (sInfo.isItalic) {
                indexfile<< "font-style:italic;";
            }
            if (sInfo.isBlink) {
                indexfile<< "text-decoration:blink;";
            }
            if (sInfo.isUnderLine) {
                indexfile<< "text-decoration:underline;";
            }
            if (sInfo.isConcealed) {
                indexfile<< "display:none;";
            }

            if (sInfo.fgColor.size()) {
                indexfile << "fill:#"
                          << sInfo.fgColor
                          << ";";
            }

           /* if (sInfo.bgColor!="") {
                indexfile << "background-color:#"
                          << sInfo.bgColor
                          << ";";
            }*/

            indexfile << "}\n";
        }

    } else {
        return false;
    }
    return true;
}

}
