#include "hpdf.h"
#include "DocOutput.h"
#include "Util.h"


HPDF_Font fontStyles[4];
extern const float fontSizes[4];


struct DocImage
{
	HPDF_Image image;
};


struct DocOutputPage::Pimpl
{
	HPDF_Doc  pdf;
	HPDF_Page page;
};


DocOutputPage::DocOutputPage(void* a_ptr) : m_pimpl(std::make_unique<Pimpl>())
{
	m_pimpl->pdf = (HPDF_Doc)a_ptr;
	m_pimpl->page = HPDF_AddPage(m_pimpl->pdf);
	//HPDF_Page_SetFontAndSize(page, fontStyles[currentStyle.top()], fontSizes[currentHeadingLevel.top()]);
	HPDF_Page_BeginText(m_pimpl->page);
}


DocOutputPage::~DocOutputPage()
{
	HPDF_Page_EndText(m_pimpl->page);
}


HPDF_Page DocOutputPage::page()
{
	return m_pimpl->page;
}


float DocOutputPage::width()
{
	return HPDF_Page_GetWidth(m_pimpl->page);
}


float DocOutputPage::height()
{
	return HPDF_Page_GetHeight(m_pimpl->page);
}


float DocOutputPage::textWidth(const char* str)
{
	return HPDF_Page_TextWidth(m_pimpl->page, str);
}


void DocOutputPage::setColor(float r, float g, float b)
{
	HPDF_Page_SetRGBFill(m_pimpl->page, r, g, b);
}


void DocOutputPage::setColor(unsigned int color)
{
	setColor(((color >> 16) & 0xff) / 255.0f, ((color >> 8) & 0xff) / 255.0f, (color & 0xff) / 255.0f);
}


void DocOutputPage::setFontSize(int font, float size)
{
	HPDF_Page_SetFontAndSize(m_pimpl->page, fontStyles[font], size);
}


void DocOutputPage::drawText(float x, float y, const char* str)
{
	HPDF_Page_TextOut(m_pimpl->page, x, height() - y, str);
}


void DocOutputPage::drawLine(float x1, float y1, float x2, float y2)
{
	HPDF_Page_EndText(m_pimpl->page);
	HPDF_Page_SetLineWidth(m_pimpl->page, 2.0);
	HPDF_Page_MoveTo(m_pimpl->page, x1, height() - y1);
	HPDF_Page_LineTo(m_pimpl->page, x2, height() - y2);
	HPDF_Page_Stroke(m_pimpl->page);
	HPDF_Page_BeginText(m_pimpl->page);
}


void DocOutputPage::drawRect(float x1, float y1, float w, float h)
{
	HPDF_Page_EndText(m_pimpl->page);
	HPDF_Page_SetLineWidth(m_pimpl->page, 0.05f);
	HPDF_Page_SetRGBStroke(m_pimpl->page, 0.85f, 0.85f, 0.85f); // TODO: set this via API
	HPDF_Page_SetRGBFill(m_pimpl->page, 0.95f, 0.95f, 0.95f);
	HPDF_Page_Rectangle(m_pimpl->page, x1, height() - y1 - h, w, h);
	HPDF_Page_FillStroke(m_pimpl->page);
	HPDF_Page_BeginText(m_pimpl->page);
}


void DocOutputPage::drawImage(DocImage& img, float x1, float y1, float w, float h)
{
	HPDF_Page_EndText(m_pimpl->page);
	HPDF_Page_DrawImage(m_pimpl->page, img.image, x1, height() - y1 - h, w, h);
	//HPDF_Page_FillStroke(m_pimpl->page); // TODO: need this?
	HPDF_Page_BeginText(m_pimpl->page);
}


void DocOutputPage::drawImage(const char* fileName, float x1, float y1, float w, float h)
{
	HPDF_Page_EndText(m_pimpl->page);
	//HPDF_Image img = HPDF_LoadJpegImageFromFile(m_pimpl->pdf, fileName);
	HPDF_Image img = HPDF_LoadPngImageFromFile2(m_pimpl->pdf, fileName);
	HPDF_Page_DrawImage(m_pimpl->page, img, x1, height() - y1 - h, w, h);
	//HPDF_Page_FillStroke(m_pimpl->page); // TODO: need this?
	HPDF_Page_BeginText(m_pimpl->page);
}


void DocOutputPage::drawCircle(float x, float y, float radius)
{
	HPDF_Page_EndText(m_pimpl->page);
	HPDF_Page_Circle(m_pimpl->page, x, height() - y, radius);
	HPDF_Page_Stroke(m_pimpl->page);
	HPDF_Page_BeginText(m_pimpl->page);
}


struct DocOutputDevice::Pimpl
{
	HPDF_Doc pdf;
};


#ifdef _WIN32
#include <windows.h> // for OutputDebugStringA
static void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, void * /*user_data*/, int line, const char* file)
{
	// DocOutputDevice* context = (DocOutputDevice*)user_data;
	auto sizeRequired = size_t(_scprintf("%s(%i): error %04X: detail_no=%u\n", file, line, (HPDF_UINT)error_no, (HPDF_UINT)detail_no) + 1);
	auto a = (char*)_alloca(sizeRequired);
	sprintf_s(a, sizeRequired, "%s(%i): error %04X: detail_no=%u\n", file, line, (HPDF_UINT)error_no, (HPDF_UINT)detail_no);
	OutputDebugStringA(a);
	_freea(a);
	// delete context; // Can't do this, it could be on the stack as is the case currently
	exit(-1);
}
#else

static void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, void * /*user_data*/, int line, const char* file)
{
	// DocOutputDevice* context = (DocOutputDevice*)user_data;
	fprintf(stderr, "%s(%i): error %04X: detail_no=%u\n", file, line, (HPDF_UINT)error_no, (HPDF_UINT)detail_no);
	// delete context; // Can't do this, it could be on the stack as is the case currently
	exit(-1);
}

#endif


DocOutputDevice::DocOutputDevice() : m_pimpl(std::make_unique<Pimpl>())
{
	m_pimpl->pdf = HPDF_New(error_handler, this);
	if (!m_pimpl->pdf)
	{
		printf("error: cannot create PdfDoc object\n");
		return;
	}
	HPDF_SetCompressionMode(m_pimpl->pdf, HPDF_COMP_ALL);
	//fontStyles[0] = HPDF_GetFont(context->pdf, "David", NULL);
	fontStyles[0] = HPDF_GetFont(m_pimpl->pdf, "Helvetica", NULL);
	fontStyles[1] = HPDF_GetFont(m_pimpl->pdf, "Helvetica-Oblique", NULL);
	fontStyles[2] = HPDF_GetFont(m_pimpl->pdf, "Helvetica-Bold", NULL);
	fontStyles[3] = HPDF_GetFont(m_pimpl->pdf, "Helvetica-BoldOblique", NULL);
}


DocOutputDevice::~DocOutputDevice()
{
	HPDF_Free(m_pimpl->pdf);
}


DocOutputPage* DocOutputDevice::newPage()
{
	return new DocOutputPage((void*)m_pimpl->pdf);
}


DocImage& DocOutputDevice::loadImage(const char* fileName)
{
	DocImage* img = new DocImage();
	img->image = HPDF_LoadJpegImageFromFile(m_pimpl->pdf, fileName);
	//img->image = HPDF_LoadPngImageFromFile2(m_pimpl->pdf, fileName);
	return *img;
}


void DocOutputDevice::freeImage(DocImage& img)
{
	//HPDF_Free(img.image);
	delete &img;
}


void DocOutputDevice::finalize(char* outputBuffer, size_t* size)
{
    HPDF_UINT32 siz = *size;
    HPDF_GetContents(m_pimpl->pdf, (HPDF_BYTE*)outputBuffer, &siz);
    *size = siz;
}


void DocOutputDevice::finalize(const char* outputFileName)
{
    HPDF_SaveToFile(m_pimpl->pdf, outputFileName);
}

