
/*
 * See the files COPYING and README for copyright information and how to reach 
 * the author.
 *
 *  $Id: render.c,v 1.3 2004/12/02 15:02:52 lordjaxom Exp $
 */

#include "common.h"
#include "render.h"
#include "config.h"
#include "jobs.h"
#include "setup.h"
#include <vdr/tools.h>
#include <vdr/plugin.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>

void cBurnRender::RenderProc(void *This)
{
    _exit(((cBurnRender *) This)->CreateMenu()? 0 : 1);
}

cBurnRender::cBurnRender(void)
{
    asprintf(&mBackground, "%s/menu-bg.png",
            cPlugin::ConfigDirectory("burnold"));
    asprintf(&mButton, "%s/menu-button.png",
            cPlugin::ConfigDirectory("burnold"));
    mFontFace = strdup("helmetr");
    mFontSize = 16;
    mFontPath = strdup(cPlugin::ConfigDirectory("burnold"));
    mJob = NULL;
    mRecording = NULL;
}

cBurnRender::~cBurnRender()
{
    free(mBackground);
    free(mButton);
    free(mFontFace);
    free(mFontPath);
}

char *cBurnRender::WrapText(const char *Text, int Width, int &LineHeight)
{
    int lines = 1;
    char *t = strdup(Text);
    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;
}

void cBurnRender::DrawImage(const char *Filename, int X, int Y)
{
    Imlib_Image img, old;
    Imlib_Load_Error error;
    int w, h;

    old = imlib_context_get_image();
    img = imlib_load_image_with_error_return(Filename, &error);
    if (!img) {
        esyslog("BURN: Couldn't load image %s, imlib2 error code %d",
                Filename, error);
        if (X != 0 || Y != 0) {
            return;
        }
        w = 720;
        h = 576;
        img = imlib_create_image(w, h);
        imlib_context_set_image(img);
        imlib_image_clear_color(0x00, 0x00, 0x00, 0xff);
    } else {
        imlib_context_set_image(img);
        w = imlib_image_get_width();
        h = imlib_image_get_height();
    }

    imlib_context_set_image(old);
    //imlib_context_set_blend(1);
    imlib_blend_image_onto_image(img, 1, 0, 0, w, h, X, Y, w, h);
    imlib_image_copy_alpha_to_image(img, X, Y);

    imlib_context_set_image(img);
    imlib_free_image();
    imlib_context_set_image(old);
}

int cBurnRender::DrawText(const char *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(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 (y + lineh < Y + Height && *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::CreateMenu(void)
{
    // runs in a separate process
    const char *path;
    char *cmd;
    int res, pages;

    if (mJob != NULL) {
        path = mJob->TempDir();
        if ((pages = CreateMainMenuPages()) == 0 ||
                !CreateMainMenuButtons(pages))
        {
            return false;
        }
    } else if (mRecording != NULL) {
        path = mRecording->TempDir();
        if ((pages = CreateTitleMenuPages()) == 0 ||
                !CreateTitleMenuButtons(pages))
        {
            return false;
        }
    } else {
        return false;
    }

    for (int i = 0; i < pages; ++i) {
        cSubmuxConfig subcfg("%s/menu-%d.xml", path, i);
        subcfg.SetButtonNormal("%s/menu-buttons-ns.png", path);
        subcfg.SetButtonSelect("%s/menu-buttons-ns.png", path);
        subcfg.SetButtonHighlight("%s/menu-buttons-h-%d.png", path, i);
        subcfg.Write();

        asprintf(&cmd, "%s MKMENU '%s' '%d' '%s'",
                Script, path, i, cPlugin::ConfigDirectory("burnold"));
        fprintf(stderr, " ++ starting sh -c '%s' in internal procedure\n",
                cmd);
        res = SystemExec(cmd);
        free(cmd);
        if (WEXITSTATUS(res) != 0) {
            return false;
        }
    }
    return true;
}

int cBurnRender::CreateMainMenuPages(void)
{
    Imlib_Image image;
    Imlib_Font font;
    Imlib_Load_Error error;
    char *fontname;
    char *next, *prev;
    char *path;
    int cnt;

    imlib_add_path_to_font_path(mFontPath);
    asprintf(&fontname, "%s/%d", mFontFace, mFontSize);
    font = imlib_load_font(fontname);
    free(fontname);
    if (!font) {
        esyslog("BURN: Couldn't load font %s", mFontFace);
        return 0;
    }
    imlib_context_set_font(font);

    cnt = (mJob->Count() + MAXPAGETITLES - 1) / MAXPAGETITLES;
    for (int p = 0; p < cnt; ++p) {
        image = imlib_create_image(720, 576);
        imlib_context_set_image(image);

        DrawImage(mBackground, 0, 0);

        next = EscapeLatin1toUTF8(tr("Next page"));
        prev = EscapeLatin1toUTF8(tr("Previous page"));

        for (int i = p * MAXPAGETITLES, j = 0;
                i < min(p * MAXPAGETITLES + MAXPAGETITLES, mJob->Count());
                ++i, ++j)
        {
            cBurnRecording *rec = mJob->Get(i);
            char *name = EscapeLatin1toUTF8(rec->Name());
            if (BurnSetup.RenderRecordingIndex) {
                char *indexedName;
                asprintf(&indexedName, "%d. %s", i + 1, name);
                free(name);
                name = indexedName;
            }
            DrawText(name, 138, 124 + j * 40, 374, 30); // width 374 was 450
            // This ought to take j * the actual line height as read & rendered
            free(name);
        }
        char *name = EscapeLatin1toUTF8(mJob->Title());
//      DrawText(name, 138, 52, 502, 30); // width 502 was 450
        DrawText(name,  90, 52, 550, 30); // width 550 was 450, start 90 was 138
        free(name);

        if (mJob->Count() > p * MAXPAGETITLES + MAXPAGETITLES) {
            DrawText(next, 360, 513, 240, 25, 2);
        }
        if (p > 0) {
            DrawText(prev, 120, 513, 240, 25);
        }

        free(next);
        free(prev);

        imlib_image_set_format("png");
        asprintf(&path, "%s/menu-bg-%d.png", mJob->TempDir(), p);
        imlib_save_image_with_error_return(path, &error);
        if (error != IMLIB_LOAD_ERROR_NONE) {
            esyslog("BURN: Couldn't save image %s, imlib2 error code %d",
                    path, error);
        }
        free(path);
        imlib_free_image();
        if (error != IMLIB_LOAD_ERROR_NONE) {
            return 0;
        }
    }
    imlib_free_font();
    return cnt;
}

bool cBurnRender::CreateMainMenuButtons(int Pages)
{
    Imlib_Image image;
    Imlib_Load_Error error;
    char *path;

    image = imlib_create_image(720, 576);
    imlib_context_set_image(image);
    imlib_image_set_has_alpha(1);
    DrawRectangle(0, 0, 720, 576, 0x00000000);
    imlib_image_set_format("png");
    asprintf(&path, "%s/menu-buttons-ns.png", mJob->TempDir());
    imlib_save_image_with_error_return(path, &error);
    if (error != IMLIB_LOAD_ERROR_NONE) {
        esyslog("BURN: Couldn't save image %s, imlib2 error code %d", path,
                error);
    }
    free(path);
    imlib_free_image();

    for (int i = 0; i < Pages; ++i) {
        image = imlib_create_image(720, 576);
        imlib_context_set_image(image);
        imlib_image_set_has_alpha(1);

        DrawRectangle(0, 0, 720, 576, 0x00000000);
        if (i < Pages - 1) {
            DrawImage(mButton, 605, 509);
        }
        if (i > 0) {
            DrawImage(mButton, 80, 509);
        }

        for (int j = i * MAXPAGETITLES, k = 0;
                j < min(i * MAXPAGETITLES + MAXPAGETITLES, mJob->Count());
                ++j, ++k)
        {
            DrawImage(mButton, 93, 118 + k * 40);
        }

        imlib_image_set_format("png");
        asprintf(&path, "%s/menu-buttons-h-%d.png", mJob->TempDir(), i);
        imlib_save_image_with_error_return(path, &error);
        if (error != IMLIB_LOAD_ERROR_NONE) {
            esyslog("BURN: Couldn't save image %s, imlib2 error code %d",
                    path, error);
        }
        free(path);
        imlib_free_image();
        if (error != IMLIB_LOAD_ERROR_NONE) {
            return false;
        }
    }
    return true;
}

int cBurnRender::CreateTitleMenuPages(void)
{
    Imlib_Image image;
    Imlib_Font font;
    Imlib_Load_Error error;
    char *fontname;
    char *summary, *play, *back, *next, *prev;
    char *path;
    char *stitle;
    int lines = 0, cnt = 0;

    imlib_add_path_to_font_path(mFontPath);
    asprintf(&fontname, "%s/%d", mFontFace, mFontSize);
    font = imlib_load_font(fontname);
    free(fontname);
    if (!font) {
        esyslog("BURN: Couldn't load font %s", mFontFace);
        return 0;
    }
    imlib_context_set_font(font);

    while (lines != -1) {
        image = imlib_create_image(720, 576);
        imlib_context_set_image(image);

        DrawImage(mBackground, 0, 0);

//        stitle = EscapeLatin1toUTF8(tr("DESCRIPTION OF FILMTITLE"));
        stitle = EscapeLatin1toUTF8(
                 mRecording->RecordingTitle() ? mRecording->RecordingTitle() :
                 tr("DESCRIPTION OF FILMTITLE"));
        summary = EscapeLatin1toUTF8(
                mRecording->Summary() ? mRecording->Summary() :
                mRecording->Name());
        play = EscapeLatin1toUTF8(tr("Play movie"));
        back = EscapeLatin1toUTF8(tr("Back"));
        next = EscapeLatin1toUTF8(tr("Next page"));
        prev = EscapeLatin1toUTF8(tr("Previous page"));

        DrawText(stitle, 90, 52, 550, 30); // width 550 was 450
        lines = DrawText(summary, 90, 120, 410, 340, 0, lines); 
        // width 410, should be 430, was 450
        // WrapText seems to overshoot by 1 char, worst case "m" = 20px approx.
        if (lines != -1) {
            DrawText(next, 360, 472, 240, 25, 2);
        }
        if (cnt > 0) {
            DrawText(prev, 120, 472, 240, 25);
        }
        DrawText(play, 120, 513, 240, 25);
        DrawText(back, 360, 513, 240, 25, 2);

        free(stitle);
        free(summary);
        free(play);
        free(back);
        free(next);
        free(prev);

        imlib_image_set_format("png");
        asprintf(&path, "%s/menu-bg-%d.png", mRecording->TempDir(), cnt);
        imlib_save_image_with_error_return(path, &error);
        if (error != IMLIB_LOAD_ERROR_NONE) {
            esyslog("BURN: Couldn't save image %s, imlib2 error code %d",
                    path, error);
        }
        free(path);
        imlib_free_image();
        if (error != IMLIB_LOAD_ERROR_NONE) {
            return 0;
        }
        ++cnt;
    }
    imlib_free_font();
    return cnt;
}

bool cBurnRender::CreateTitleMenuButtons(int Pages)
{
    Imlib_Image image;
    Imlib_Load_Error error;
    char *path;

    image = imlib_create_image(720, 576);
    imlib_context_set_image(image);
    imlib_image_set_has_alpha(1);
    DrawRectangle(0, 0, 720, 576, 0x00000000);
    imlib_image_set_format("png");
    asprintf(&path, "%s/menu-buttons-ns.png", mRecording->TempDir());
    imlib_save_image_with_error_return(path, &error);
    if (error != IMLIB_LOAD_ERROR_NONE) {
        esyslog("BURN: Couldn't save image %s, imlib2 error code %d", path,
                error);
    }
    free(path);
    imlib_free_image();

    for (int i = 0; i < Pages; ++i) {
        image = imlib_create_image(720, 576);
        imlib_context_set_image(image);
        imlib_image_set_has_alpha(1);

        DrawRectangle(0, 0, 720, 576, 0x00000000);
        if (i < Pages - 1) {
            DrawImage(mButton, 605, 469);
        }
        if (i > 0) {
            DrawImage(mButton, 80, 469);
        }
        DrawImage(mButton, 80, 509);
        DrawImage(mButton, 605, 509);

        imlib_image_set_format("png");
        asprintf(&path, "%s/menu-buttons-h-%d.png", mRecording->TempDir(), i);
        imlib_save_image_with_error_return(path, &error);
        if (error != IMLIB_LOAD_ERROR_NONE) {
            esyslog("BURN: Couldn't save image %s, imlib2 error code %d",
                    path, error);
        }
        free(path);
        imlib_free_image();
        if (error != IMLIB_LOAD_ERROR_NONE) {
            return false;
        }
    }
    return true;
}

void cBurnRender::SetBackground(const char *Filename)
{
    free(mBackground);
    mBackground = strdup(Filename);
}

void cBurnRender::SetButton(const char *Filename)
{
    free(mButton);
    mButton = strdup(Filename);
}

void cBurnRender::SetFont(const char *Face, int Size)
{
    free(mFontFace);
    mFontFace = strdup(Face);
    mFontSize = Size;
}

void cBurnRender::SetFontPath(const char *Path)
{
    free(mFontPath);
    mFontPath = strdup(Path);
}
