/*
 * See the files COPYING and README for copyright information and how to reach 
 * the author.
 *
 *  $Id: render.c,v 1.2 2006/04/17 19:03:45 lordjaxom Exp $
 */

#include "common.h"
#include "render.h"
#include "config.h"
#include "jobs.h"
#include "proctools/logger.h"
#include "proctools/format.h"
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
#include <vdr/tools.h>
#include <vdr/plugin.h>

namespace vdr_burn
{

	using namespace std;
	using proctools::logger;
	using proctools::format;

cBurnRender::cBurnRender(const job& Job):
		process("render"),
		mJob(Job),
		mFontPath(cPlugin::ConfigDirectory("burn")),
		mFontFace("helmetr"),
		mFontSize(16)
{
}

string cBurnRender::info(pid_t pid)
{
	return format("internal rendering procedure (pid = {0})") % pid;
}

int cBurnRender::run(void)
{
	bool result;
	isyslog("burn: internal rendering procedure started (pid = %d)", getpid());
	result = Render();
	isyslog("burn: internal rendering procedure ended");
	return result ? 0 : 1;
}

char *cBurnRender::WrapText(const std::string& Text, int Width, int &LineHeight)
{
	int lines = 1;
	char *t = strdup(Text.c_str());
	char *blank = NULL;
	char *delim = NULL;
	int w = 0;

	while (*t && t[strlen(t) - 1] == '\n')
		t[strlen(t) - 1] = '\0';

	for (char *p = t; *p; ) {
		if (*p == '\n') {
			++lines; ++p;
			w = 0;
			blank = delim = NULL;
			continue;
		} else if (isspace(*p))
			blank = p;

		int cw;
		char buf[2] = { *p, '\0' };
		imlib_get_text_size(buf, &cw, &LineHeight);
		++cw; // char spacing
		if (w + cw > Width) {
			if (blank) {
				*blank = '\n';
				p = blank;
				continue;
			} else {
				if (delim)
					p = delim + 1;
				char *s = MALLOC(char, strlen(t) + 2);
				int l = p - t;
				strncpy(s, t, l);
				s[l] = '\n';
				strcpy(s + l + 1, p);
				free(t);
				t = s;
				p = t + l;
				continue;
			}
		} else
			w += cw;
		if (strchr("-.,:;!?_", *p)) {
			delim = p;
			blank = NULL;
		}
		++p;
	}
	return t;
}

bool cBurnRender::DrawImage(const std::string& Filename, int X, int Y)
{
	Imlib_Image image, current;
	Imlib_Load_Error error;
	int width, height;
	bool result = true;

	current = imlib_context_get_image();
	image = imlib_load_image_with_error_return(Filename.c_str(), &error);
	if (!image) {
		esyslog("ERROR[burn]: couldn't load image %s: %d", Filename.c_str(), error);
		return false;
	}

	imlib_context_set_image(image);

	width = imlib_image_get_width();
	height = imlib_image_get_height();

	imlib_context_set_image(current);
	imlib_blend_image_onto_image(image, 1, 0, 0, width, height, 
										   X, Y, width, height);
	imlib_image_copy_alpha_to_image(image, X, Y);
	
	imlib_context_set_image(image);
	imlib_free_image();

	imlib_context_set_image(current);

	return result;
}

int cBurnRender::DrawText(const std::string& Text, int X, int Y, int Width, 
						  int Height, int Align, int First) 
{
	int y = Y, lineh, lines = 0;
	int w, h;
	char *text = WrapText(escape_iso_to_utf8(Text), Width, lineh);
	char *pos = text, *next;

	imlib_context_set_color(0xff, 0xff, 0xff, 0xff);

	while (y + lineh < Y + Height && (next = strchr(pos, '\n')) != NULL) {
		*next = '\0';
		if (lines++ >= First) {
			imlib_get_text_size(pos, &w, &h);
			if (Align == 0)
				imlib_text_draw(X, y, pos);
			else if (Align == 2)
				imlib_text_draw(X + Width - w - 1, y, pos);
			y += lineh;
		}
		pos = next + 1;
		if (*pos == '\0')
			lines = -1;
	}

	if (*pos == '\0' || y + lineh < Y + Height) {
		if (*pos != '\0') {
			imlib_get_text_size(pos, &w, &h);
			if (Align == 0)
				imlib_text_draw(X, y, pos);
			else if (Align == 2)
				imlib_text_draw(X + Width - w - 1, y, pos);
		}
		lines = -1;
	}

	free(text);
	return lines;
}

void cBurnRender::DrawRectangle(int X, int Y, int Width, int Height, 
								uint32_t Color) 
{
	imlib_context_set_color((Color & 0xff0000) >> 16, (Color & 0xff00) >> 8,
							Color & 0xff, (Color & 0xff000000) >> 24);
	imlib_image_fill_rectangle(X, Y, Width, Height);
}

bool cBurnRender::InitializeFont(void)
{
	Imlib_Font font;
	
	imlib_add_path_to_font_path(mFontPath.c_str());
	if ((font = imlib_load_font(GetFontName().c_str())) == NULL) {
		logger::error(format("couldn't load font {0}/{1}.ttf") 
					  % mFontPath % mFontFace, false);
		return false;
	}
	imlib_context_set_font(font);
	return true;
}

void cBurnRender::DestroyFont(void)
{
	imlib_free_font();
}

void cBurnRender::InitializeImage(int Width, int Height, bool Alpha,
								  uint32_t Color)
{
	Imlib_Image image;

	image = imlib_create_image(Width, Height);
	imlib_context_set_image(image);

	if (Alpha)
		imlib_image_set_has_alpha(1);

	DrawRectangle(0, 0, Width, Height, Color);
}

bool cBurnRender::InitializeImage(int Width, int Height, bool Alpha,
								  const std::string& Background)
{
	InitializeImage(Width, Height, Alpha);

	return !Background.empty()
		 ? DrawImage(Background, 0, 0)
		 : true;
}

bool cBurnRender::SaveImage(const std::string& FileName)
{
	Imlib_Load_Error error;

	imlib_image_set_format("png");
	imlib_save_image_with_error_return(FileName.c_str(), &error);
	imlib_free_image();

	if (error != IMLIB_LOAD_ERROR_NONE) {
		logger::error(format("couldn't save image {0}: {1}") 
					  % FileName % error);
		return false;
	}
	return true;
}

bool cBurnRender::CreateMainMenuPage(int Page, int Count)
{
	int first = Page * MAXPAGETITLES;

	if (!InitializeImage(720, 576, false, mJob.get_skin().get_background()))
		return false;

	DrawText(mJob.get_title(), 90, 52, 450, 30);

	if (Page > 0)
		DrawText(tr("Previous page"), 120, 513, 240, 25);
	if (Page < Count - 1)
		DrawText(tr("Next page"), 360, 513, 240, 25, 2);

	recording_list::const_iterator rec = mJob.get_recordings().begin();
	advance(rec, first);
	for (int i = 0; rec != mJob.get_recordings().end() && i < MAXPAGETITLES; 
		 ++i, ++rec) {
		DrawText(rec->get_name(), 138, 124 + i * 40, 450, 30);
	}

	return SaveImage(mJob.get_menu_background(Page));
}

bool cBurnRender::CreateMainMenuButtons(int Page, int Count)
{
	int first = Page * MAXPAGETITLES;

	/* transparent page for non-selected buttons */
	InitializeImage(720, 576, true);

	if (!SaveImage(mJob.get_buttons_normal()))
		return false;

	/* now the highlighted buttons */
	InitializeImage(720, 576, true);

	if (Page < Count - 1)
		DrawImage(mJob.get_skin().get_button(), 605, 509);
	if (Page > 0)
		DrawImage(mJob.get_skin().get_button(), 80, 509);

	recording_list::const_iterator rec = mJob.get_recordings().begin();
	advance(rec, first);
	for (int i = 0; rec != mJob.get_recordings().end() && i < MAXPAGETITLES; 
		 ++i, ++rec) {
		DrawImage(mJob.get_skin().get_button(), 93, 118 + i * 40);
	}

	return SaveImage(mJob.get_buttons_highlight(Page));
}

bool cBurnRender::CreateMainMenu(void)
{
	int count = mJob.get_recordings().size() / MAXPAGETITLES;
	if (mJob.get_recordings().size() % MAXPAGETITLES > 0)
		++count;

	for (int page = 0; page < count; ++page) {
		int result;

		if (!CreateMainMenuPage(page, count) ||
			!CreateMainMenuButtons(page, count))
			return false;
		
		submux_xml<job> conf(mJob);
		conf.write(page);

		put_environment("MENU_BACKGROUND", mJob.get_menu_background(page));
		put_environment("MENU_SOUNDTRACK", 
						string( cPlugin::ConfigDirectory("burn") )
						+ "/menu-silence.mp2");
		put_environment("MENU_XML", conf.get_xml_path(page));
		put_environment("MENU_MPEG", mJob.get_menu_mpeg(page));
		set_environment();
		isyslog("burn: starting sh -c 'vdrburn-dvd.sh render");
		result = SystemExec("vdrburn-dvd.sh render");
	}
	return true;
}

bool cBurnRender::CreateTitleMenuPage(int &Lines, int Page)
{
	if (!InitializeImage(720, 576, false, mJob.get_skin().get_background()))
		return false;

	DrawText(m_current->get_name(), 90, 52, 450, 30);
	DrawText(tr("Play movie"), 120, 513, 240, 25);
	DrawText(tr("Back"), 360, 513, 240, 25, 2);
	
	Lines = DrawText(m_current->get_summary(), 90, 120, 450, 340, 0, Lines);

	if (Lines != -1)
		DrawText(tr("Next page"), 360, 472, 240, 25, 2);
	if (Page > 0)
		DrawText(tr("Previous page"), 120, 472, 240, 25);

	return SaveImage(m_current->get_menu_background(Page));
}

bool cBurnRender::CreateTitleMenuButtons(int Lines, int Page)
{
	const std::string& button = mJob.get_skin().get_button();

	/* transparent page for non-selected buttons */
	InitializeImage(720, 576, true);

	if (!SaveImage(m_current->get_buttons_normal()))
		return false;

	/* now the highlighted buttons */
	InitializeImage(720, 576, true);
	DrawImage(button, 80, 509);
	DrawImage(button, 605, 509);

	if (Lines != -1)
		DrawImage(button, 605, 469);
	if (Page > 0)
		DrawImage(button, 80, 469);

	return SaveImage(m_current->get_buttons_highlight(Page));
}

bool cBurnRender::CreateTitleMenu(void)
{
	/* Still quite ugly, but how else?? */
	int lines = 0, page = 0;

	while (lines != -1) {
		int result;

		if (!CreateTitleMenuPage(lines, page) ||
			!CreateTitleMenuButtons(lines, page))
			return false;

		submux_xml<recording> conf(*m_current);
		conf.write(page);

		put_environment("MENU_BACKGROUND", m_current->get_menu_background(page));
		put_environment("MENU_SOUNDTRACK",
						string( cPlugin::ConfigDirectory("burn") )
						+ "/menu-silence.mp2");
		put_environment("MENU_XML", conf.get_xml_path(page));
		put_environment("MENU_MPEG", m_current->get_menu_mpeg(page));
		set_environment();
		isyslog("burn: starting sh -c 'vdrburn-dvd.sh render");
		result = SystemExec("vdrburn-dvd.sh render");
		
		++page;
	}
	return true;
}

bool cBurnRender::Render(void)
{
	/* runs in a separate process */
	if (!InitializeFont() || !CreateMainMenu())
		return false;

	for (m_current = mJob.get_recordings().begin(); 
		 m_current != mJob.get_recordings().end(); ++m_current) {
		if (!CreateTitleMenu())
			return false;
	}

	DestroyFont();

	return true;
}


}
