/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2008 by Maurus Cuelenaere
 *
 * This program 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 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <dlfcn.h>
#include <unistd.h>
#include "gd.h"
#include "gdfonts.h"
#include "api.h"

#define DEBUGF1 _debug
#define DEBUGF2 if(verbose) _debug

#define getFont() gdFontGetSmall()

static struct trackstate mp3data =
{
(char*)"Test title",
(char*)"Test artist",
(char*)"Test album",
(char*)"Test genre",
(char*)"Test disc",
(char*)"Test track",
(char*)"Test year",
(char*)"Test composer",
(char*)"Test comment",
(char*)"Test album artist",
(char*)"Test grouping",
1, /* int discnum */
1, /* int tracknum */
1, /* int version */
1, /* int layer */
2008, /* int year */

100, /* int length */
70 /* int elapsed */
};

static struct wpsstate wpsdata = {-20, -1, -1, 70, API_STATUS_FASTFORWARD};
              /* volume, fontheight, fontwidth, battery_level, audio_status */

static struct proxy_api api;
static bool verbose = false;
static int (*wps_init)(const char* buff, struct proxy_api *api, bool isfile);
static int (*wps_display)();
static int (*wps_refresh)();
static gdImagePtr framebuffer;
static gdImagePtr backdrop;

extern gdImagePtr gdImageCreateFromBmp(FILE * inFile);
extern char *get_current_dir_name (void) __THROW;

static bool next_nl = false;

int _debug(const char* fmt,...)
{
    va_list ap;
    
    va_start(ap, fmt);
    
    if(!next_nl)
        fprintf(stdout, "[DBG]  ");
    
    vfprintf(stdout, fmt, ap);
    
    if(fmt[strlen(fmt)-1] != 0xa)
        next_nl = true;
    else
        next_nl = false;
    
    va_end(ap);

    return 0;
}

void _putsxy(int x, int y, const unsigned char *str)
{
    struct viewport_api avp;
    int black = gdImageColorAllocate(framebuffer, 0, 0, 0);
    
    api.get_current_vp(&avp);
    
    gdImageString(framebuffer, getFont(), x + avp.x, y + avp.y - avp.fontheight, (unsigned char*)str, black);
}

void _transparent_bitmap_part(const void *src, int src_x, int src_y,
                                        int stride, int x, int y, int width, int height)
{
    FILE *_image;
    gdImagePtr image;
    int pink;
    
    DEBUGF2("transparent_bitmap_part(const void *src=%s, int src_x=%d, int src_y=%d, int stride=%d, int x=%d, int y=%d, int width=%d, int height=%d\n", (char*)src, src_x, src_y, stride, x, y, width, height);
    
    _image = fopen(src, "rb");
    if(_image == NULL)
        return;
    
    image = gdImageCreateFromBmp(_image);
    fclose(_image);
    
    pink = gdTrueColor(255, 0, 255); 
    gdImageColorTransparent(image, pink);
    
    gdImageCopy(framebuffer, image, x, y, src_x, src_y, width, height);
    
    gdImageDestroy(image);
}

void _bitmap_part(const void *src, int src_x, int src_y,
                  int stride, int x, int y, int width, int height)
{
    FILE *_image;
    gdImagePtr image;
    
    DEBUGF2("bitmap_part(const void *src=%s, int src_x=%d, int src_y=%d, int stride=%d, int x=%d, int y=%d, int width=%d, int height=%d\n", (char*)src, src_x, src_y, stride, x, y, width, height);
    
    _image = fopen(src, "rb");
    if(_image == NULL)
        return;
    
    image = gdImageCreateFromBmp(_image);
    fclose(_image);
    
    gdImageCopy(framebuffer, image, x, y, src_x, src_y, width, height);
    
    gdImageDestroy(image);
}

void _drawpixel(int x, int y)
{
    int black = gdImageColorAllocate(framebuffer, 0, 0, 0);
    gdImageSetPixel(framebuffer, x, y, black);
}

void _fillrect(int x, int y, int width, int height)
{
    /* Don't draw this as backdrop is used */
#if 0
    int black = gdImageColorAllocate(framebuffer, 0, 0, 0);
    gdImageFilledRectangle(framebuffer, x, y, x+width, y+height, black);
#endif
}

void _hline(int x1, int x2, int y)
{
    int black = gdImageColorAllocate(framebuffer, 0, 0, 0); 
    gdImageLine(framebuffer, x1, y, x2, y, black);
}

void _vline(int x, int y1, int y2)
{
    int black = gdImageColorAllocate(framebuffer, 0, 0, 0); 
    gdImageLine(framebuffer, x, y1, x, y2, black);
}

void _clear_viewport(int x, int y, int w, int h, int color)
{
    if(backdrop == NULL)
        return;
    
    gdImageCopy(framebuffer, backdrop, x, y, x, y, w, h);
}

static bool _load_wps_backdrop(char* filename)
{
    FILE *image;
    if(backdrop != NULL)
        gdImageDestroy(backdrop);
    
    DEBUGF2("load backdrop: %s", filename);
    
    image = fopen(filename, "rb");
    if(image == NULL)
        return false;
    
    backdrop = gdImageCreateFromBmp(image);
    fclose(image);
    
    return true;
}

int _read_bmp_file(const char* filename, int *width, int *height)
{
    FILE *_image;
    gdImagePtr image;
    
    DEBUGF2("load backdrop: %s", filename);
    
    _image = fopen(filename, "rb");
    if(_image == NULL)
        return 0;
    
    image = gdImageCreateFromBmp(_image);
    fclose(_image);
    
    *width = image->sx;
    *height = image->sy;
    
    gdImageDestroy(image);
    
    return 1;
}

static void _drawBackdrop()
{
    if(backdrop == NULL)
        return;
    
    gdImageCopy(framebuffer, backdrop, 0, 0, 0, 0, backdrop->sx, backdrop->sy);
}

static int screenshot(char *model, char *wps, char *png)
{
    char lib[255];
    void *handle;
    FILE *out, *in;
    int res;
    
    in = fopen(wps, "rb");
    if(in == NULL)
    {
        fprintf(stderr, "[ERR]  Cannot open WPS: %s\n", wps);
        return -1;
    }
    fclose(in);
    
    out = fopen(png, "wb");
    if(out == NULL)
    {
        fprintf(stderr, "[ERR]  Cannot open PNG: %s\n", png);
        return -2;
    }
    
    snprintf(lib, 255, "%s/libwps_%s.so", (char*)get_current_dir_name(), (char*)model);
    handle = dlopen(lib, RTLD_LAZY);
    if (!handle)
    {
        fprintf(stderr, "[ERR]  Cannot open library: %s\n", dlerror());
        fclose(out);
        return -3;
    }
    
    wps_init = dlsym(handle, "wps_init");
    wps_display = dlsym(handle, "wps_display");
    wps_refresh = dlsym(handle, "wps_refresh");
    
    if (!wps_init || !wps_display || !wps_refresh)
    {
        fprintf(stderr, "[ERR]  Failed to resolve funcs!");
        dlclose(handle);
        fclose(out);
        return -4;
    }

    memset(&api, 0, sizeof(struct proxy_api));
    
    if(verbose)
        api.verbose = 3;
    else
        api.verbose = 0;

    api.putsxy =                     &_putsxy;
    api.transparent_bitmap_part =    &_transparent_bitmap_part;
    api.bitmap_part =                &_bitmap_part;
    api.drawpixel =                  &_drawpixel;
    api.fillrect =                   &_fillrect;
    api.hline =                      &_hline;
    api.vline =                      &_vline;
    api.clear_viewport =             &_clear_viewport;
    api.load_wps_backdrop =          &_load_wps_backdrop;
    api.read_bmp_file =              &_read_bmp_file;
    api.debugf =                     &_debug;
    
    res = wps_init(wps, &api, true);
    if(res != 1)
    {
        fprintf(stderr, "[ERR]  WPS wasn't correctly inited\n");
        dlclose(handle);
        fclose(out);
        return -5;
    }
    
    framebuffer = gdImageCreateTrueColor(api.getwidth(), api.getheight());

    _drawBackdrop();
    
    fprintf(stdout, "[INFO] Model: %s\n", api.get_model_name());
    
    wpsdata.fontheight = getFont()->h;
    wpsdata.fontwidth = getFont()->w;
    api.set_wpsstate(wpsdata);
    api.set_trackstate(mp3data);
    api.set_next_trackstate(mp3data);
    
    _drawBackdrop();
    wps_refresh();
    gdImagePng(framebuffer, out);
    
    fprintf(stdout, "[INFO] Image written\n");

    dlclose(handle);
    fclose(out);
    gdImageDestroy(framebuffer);
    if(backdrop != NULL)
        gdImageDestroy(backdrop);
    
    wps_init = NULL;
    wps_display = NULL;
    wps_refresh = NULL;
    
    return 0;
}

static void usage(void)
{
    fprintf(stderr, "Rockbox WPS screenshot utility\n");
    fprintf(stderr, "Made by Maurus Cuelenaere\n");
    fprintf(stderr, "\n");
    fprintf(stderr, "Usage: screenshot [-V] <MODEL> <WPS> <OUT>.png\n");
    fprintf(stderr, "     -> creates a PNG screenshot of the WPS for the specific MODEL\n");
    fprintf(stderr, "     -> libwps_<MODEL>.so must be present in the same directory\n");
    fprintf(stderr, "     -> -V sets verbose mode ON\n");
    fprintf(stderr, "\n");
    fprintf(stderr, "Example: screenshot IRIVER_H10_5GB iCatcher.wps out.png\n");
}

int main(int argc, char ** argv)
{  
    if(argc < 4)
    {
        usage();
        return -1;
    }
    
    if(argv[1] == NULL || argv[2] == NULL ||
       argv[3] == NULL ||
       (strcmp(argv[1], "-V") == 0 && argv[4] == NULL)
      )
    {
        usage();
        return -1;
    }
    
    if(strcmp(argv[1], "-V") == 0)
    {
        verbose = true;
        return screenshot(argv[2], argv[3], argv[4]);
    }
    else
    {
        verbose = false;
        return screenshot(argv[1], argv[2], argv[3]);
    }
    
    return 0;
}
