#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include "DocConvert.h"
#include "DocVisitor.h"
#include "DocTemplate.h"
#include "DocOutput.h"
#include "DocSVG.h"
#include "Util.h"

#include "html.h"
#include "tinyxml.h"

#ifndef _WIN32
#  include <unistd.h>
#else
   extern "C" void __stdcall Sleep(unsigned int);
#endif


#define DEF_IUNIT        1024
#define DEF_OUNIT        64
#define DEF_MAX_NESTING  16


static void RemoveFile(const char* fileName)
{
#ifdef _WIN32
    FILE* tmpF = 0;
    bool first = true;
    do
    {
        fopen_s(&tmpF, fileName, "rb");
        if (tmpF)
        {
            fclose(tmpF);
            _unlink(fileName);
            ::Sleep(100);
            if (first)
                printf("waiting for output file to be removed.");
            else
                printf(".");
            first = false;
            fflush(stdout);
        }
    } while (tmpF != 0);
#else
    unlink(fileName);
#endif
}


/*
fseek(f, 0L, SEEK_END);
long fileSize = ftell(f);
fseek(f, 0L, SEEK_SET);
uint8_t* inputBuffer = (uint8_t*)malloc(fileSize);
size_t inputBufferSize = fread(inputBuffer, 1, fileSize, f);
*/


hoedown_buffer *ReadInWholeFile(const char* inputFileName)
{
    // Read in the markdown file
    FILE* f = 0;
    fopen_s(&f, inputFileName, "rt"); // text or binary? Can it be utf8, and if so, do I need to read in binary mode?
    if (!f)
        return 0;
    hoedown_buffer *ib = hoedown_buffer_new(DEF_IUNIT);
    if (hoedown_buffer_putf(ib, f))
        fprintf(stderr, "I/O errors found while reading input.\n");
    fclose(f);
    return ib;
}


hoedown_buffer *ConvertMarkdownToHTML(uint8_t* inputBuffer, size_t inputBufferSize)
{
    hoedown_html_flags flags = (hoedown_html_flags)(HOEDOWN_HTML_ESCAPE | HOEDOWN_HTML_HARD_WRAP | HOEDOWN_HTML_USE_XHTML);
    hoedown_renderer *renderer = hoedown_html_renderer_new(flags, 0);
    hoedown_buffer *ob = hoedown_buffer_new(DEF_OUNIT);
    hoedown_document *document = hoedown_document_new(renderer, HOEDOWN_EXT_SPACE_HEADERS, DEF_MAX_NESTING);
    hoedown_document_render(document, ob, inputBuffer, inputBufferSize);
    hoedown_document_free(document);
    hoedown_html_renderer_free(renderer);
    return ob;
}


void SVGTest(const char* a_fileName, double scale, DocOutputDevice* outputDoc)
{
    hoedown_buffer* inputBuffer = ReadInWholeFile(a_fileName);
    if (!inputBuffer)
        return;
    // SVG xml parse
    TiXmlDocument parser;
    parser.Parse((char*)inputBuffer->data);
    DocSVG visitor(scale);
    parser.Accept(&visitor);
    visitor.DumpOperations();
    visitor.WriteTo(outputDoc);
    hoedown_buffer_free(inputBuffer);
}


void ConvertHTMLToPDF(uint8_t* inputBuffer, DocOutputDevice* outputDoc)
{
    // xml parse
    DocStyle style;
    DocTemplate templ;
    TiXmlDocument parser;

    templ.ReadTemplateFile("test.tmpl");
    parser.Parse((char*)inputBuffer);
    DocVisitor visitor(outputDoc, &style, &templ);
    parser.Accept(&visitor);

    //SVGTest("test/triangle.svg", 0.02, outputDoc);
    //SVGTest("test/ArcTest.svg", 1.0, outputDoc);
}


void SaveHTML(const char* fileName, const uint8_t* data, size_t dataSize)
{
    RemoveFile(fileName);
    FILE* f = 0;
    fopen_s(&f, fileName, "wb");
    if (!f)
        return;
    const char *CSSText = "<!DOCTYPE html><html><head><title>test</title><link rel=\"stylesheet\" type=\"text/css\" href=\"base.css\"></head><body>";
    fwrite(CSSText, 1, strlen(CSSText), f);
    fwrite(data, 1, dataSize, f);
    fclose(f);
}


struct DocConvert::Pimpl
{
    ~Pimpl() {
        if (inputBuffer)
            hoedown_buffer_free(inputBuffer);
        if (outputBuffer)
            hoedown_buffer_free(outputBuffer);
    }
    DocOutputDevice doc;
    hoedown_buffer *inputBuffer;
    hoedown_buffer *outputBuffer;
    std::string m_title;
};


DocConvert::DocConvert() : m_pimpl(std::make_unique<Pimpl>())
{
    m_pimpl->inputBuffer = 0;
    m_pimpl->outputBuffer = 0;
}


DocConvert::~DocConvert()
{
}


void DocConvert::SetTitle(const char* title)
{
    m_pimpl->m_title = title;
}


void DocConvert::SetSource(const char* inFileName)
{
    if (m_pimpl->inputBuffer)
        hoedown_buffer_free(m_pimpl->inputBuffer);
    m_pimpl->inputBuffer = ReadInWholeFile(inFileName);
}


void DocConvert::SetSourceData(const char* inData, size_t a_size)
{
    if (m_pimpl->inputBuffer)
        hoedown_buffer_free(m_pimpl->inputBuffer);
    m_pimpl->inputBuffer = hoedown_buffer_new(DEF_IUNIT);
    hoedown_buffer_put(m_pimpl->inputBuffer, (const uint8_t*)inData, a_size);
}


void DocConvert::Convert()
{
    if (!m_pimpl->inputBuffer)
        return;
    m_pimpl->outputBuffer = ConvertMarkdownToHTML(m_pimpl->inputBuffer->data, m_pimpl->inputBuffer->size);
    ConvertHTMLToPDF(m_pimpl->outputBuffer->data, &m_pimpl->doc);
}


void DocConvert::GetHTMLData(char** data, size_t* size)
{
    *data = (char*)m_pimpl->outputBuffer->data;
    *size = m_pimpl->outputBuffer->size;
}


void DocConvert::OutputHTML(const char* outFileName)
{
    SaveHTML(outFileName, m_pimpl->outputBuffer->data, m_pimpl->outputBuffer->size);
}


void DocConvert::OutputPDF(const char* outFileName)
{
    //RemoveFile(outFileName);
    m_pimpl->doc.finalize(outFileName);
}


void DocConvert::GetPDFData(char* data, size_t* size)
{
    m_pimpl->doc.finalize(data, size);
}

