#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "simplewidget.h"
#include "_simplescreen.h"
#include "_simplebutton.h"
#include "_simpletext.h"

#include "_gfx.h"
#include "_font.h"
#include "_jpeg.h"
#include "_png.h"
#include "_types.h"

/* Enables or disables debug output */
#ifdef __DEBUG
#define __D(fmt, args...) fprintf(stderr, "Simplewidget Debug: " fmt, ## args)
#else
#define __D(fmt, args...)
#endif

#define __E(fmt, args...) fprintf(stderr, "Simplewidget Error: " fmt, ## args)

#define FONT_FILE "data/fonts/decker.ttf"

#define DEPTH_OFFSET 0x10
#define PRESSED_OFFSET 0x40         // Color difference for pressed button.

int simplewidget_screen_init(void *fbp, int fbw, int fbh,
                             simplewidget_screen *swsp)
{
    _simplescreen *scrp;

    scrp = malloc(sizeof(_simplescreen));
    if (scrp == NULL) {
        __E("Failed to allocate memory for screen object\n");
        return SIMPLEWIDGET_FAILURE;
    }

    scrp->bufp = fbp;
    scrp->w = fbw;
    scrp->h = fbh;

    *swsp = (simplewidget_screen) scrp;

    return SIMPLEWIDGET_SUCCESS;
}

int simplewidget_screen_clear(simplewidget_screen sws,
                              int x, int y, int w, int h)
{
    _simplescreen *scrp = (_simplescreen *) sws;
    unsigned short *ptr;
    int i;

    if (!scrp)
        return SIMPLEWIDGET_FAILURE;

    /* Does the clearing area fit on screen? */
    if (y + h > scrp->h || x + w > scrp->w) {
        __E("Screen clearing area does not fit on screen\n");
        return SIMPLEWIDGET_FAILURE;
    }

    ptr = scrp->bufp + y * scrp->w + x;

    for (i = 0; i < h; i++) {
        memset(ptr, 0, w * 2);

        ptr += scrp->w;
    } 

    return SIMPLEWIDGET_SUCCESS;
}

int simplewidget_screen_draw_rectangle(simplewidget_screen sws, int x, int y,
                                       int w, int h, int r, int g, int b)
{
    _simplescreen *scrp = (_simplescreen *) sws;

    if (!scrp)
        return SIMPLEWIDGET_FAILURE;

    draw_rectangle(scrp, x, y, w, h, r, g, b);

    return SIMPLEWIDGET_SUCCESS;
}

int simplewidget_screen_exit(simplewidget_screen sws)
{
    _simplescreen *scrp = (_simplescreen *) sws;

    if (!scrp)
        return SIMPLEWIDGET_FAILURE;

    free(scrp);

    return SIMPLEWIDGET_SUCCESS;
}

int simplewidget_button_create(int x, int y, int w, int h, int r, int g, int b,
                               int font_height, int border, char *txt,
                               simplewidget_button *swbp)
{
    _simplebutton *btnp;

    btnp = malloc(sizeof(_simplebutton));

    if (btnp == NULL) {
        __E("Failed to allocate memory for button\n");
        return SIMPLEWIDGET_FAILURE;
    }

    btnp->x = x;
    btnp->y = y;
    btnp->w = w;
    btnp->h = h;
    btnp->r = r;
    btnp->g = g;
    btnp->b = b;
    btnp->font_height = font_height;
    btnp->border = border;

    strncpy(btnp->txt, txt, BUTTON_MAX_TEXT_LENGTH);

    *swbp = btnp;

    return SIMPLEWIDGET_SUCCESS;
}

int simplewidget_button_show(simplewidget_button swb, simplewidget_screen sws)
{
    _simplebutton *btnp = (_simplebutton *) swb;
    _simplescreen *scrp = (_simplescreen *) sws;
    int string_width, string_height;
    int x, y;

    if (!btnp || !scrp)
        return SIMPLEWIDGET_FAILURE;

    gfx_render_button(btnp, scrp, btnp->r, btnp->g, btnp->b);

    font_init(FONT_FILE);

    if (font_string_size(btnp->txt, btnp->font_height,
                         &string_width, &string_height) < 0)
        return SIMPLEWIDGET_FAILURE;

    if (string_width > btnp->w || string_height > btnp->h) {
        __E("String larger than button size!\n");
        return SIMPLEWIDGET_FAILURE;
    }

    x = btnp->x + btnp->w / 2 - string_width / 2;
    y = btnp->y + btnp->h / 2 + string_height / 2;

    if (font_render(btnp->txt, x, y, 0xff, btnp->font_height, scrp) < 0)
        return SIMPLEWIDGET_FAILURE;

    return SIMPLEWIDGET_SUCCESS;
}

int simplewidget_button_hide(simplewidget_button swb, simplewidget_screen sws)
{
    _simplebutton *btnp = (_simplebutton *) swb;
    _simplescreen *scrp = (_simplescreen *) sws;
    int y;
    __u16 *dst = (__u16 *) scrp->bufp;

    if (!btnp || !scrp)
        return SIMPLEWIDGET_FAILURE;

    dst += btnp->y * scrp->w + btnp->x;

    for (y=0; y<btnp->h; y++) {
        memset(dst, 0, btnp->w * BYTES_PER_PIXEL);
        dst += scrp->w;
    }

    return SIMPLEWIDGET_SUCCESS;
}

int simplewidget_button_press(simplewidget_button swb, simplewidget_screen sws)
{
    _simplebutton *btnp = (_simplebutton *) swb;
    _simplescreen *scrp = (_simplescreen *) sws;
    int string_width, string_height;
    int x, y;

    if (!btnp || !scrp)
        return SIMPLEWIDGET_FAILURE;

    gfx_render_button(btnp, scrp, btnp->r + PRESSED_OFFSET,
                      btnp->g + PRESSED_OFFSET, btnp->b + PRESSED_OFFSET);

    font_init(FONT_FILE);
    if (font_string_size(btnp->txt, btnp->font_height, &string_width,
                         &string_height) < 0)
        return SIMPLEWIDGET_FAILURE;

    if (string_width > btnp->w || string_height > btnp->h) {
        __E("String larger than button size!\n");
        return SIMPLEWIDGET_FAILURE;
    }

    x = btnp->x + btnp->w / 2 - string_width / 2;
    y = btnp->y + btnp->h / 2 + string_height / 2;

    if (font_render(btnp->txt, x, y, 0xff, btnp->font_height, scrp) < 0)
        return SIMPLEWIDGET_FAILURE;

    return SIMPLEWIDGET_SUCCESS;
}

int simplewidget_button_release(simplewidget_button swb,
                                simplewidget_screen sws)
{
    return simplewidget_button_show(swb, sws);
}

int simplewidget_button_delete(simplewidget_button swb)
{
    _simplebutton *btnp = (_simplebutton *) swb;

    if (!btnp)
        return SIMPLEWIDGET_FAILURE;

    free(btnp);

    return SIMPLEWIDGET_SUCCESS;
}

int simplewidget_text_create(int x, int y, int height, char *txt,
                             simplewidget_text *swtp)
{
    _simpletext *txtp;

    txtp = malloc(sizeof(_simpletext));

    if (txtp == NULL) {
        __E("Failed to allocate memory for text line\n");
        return SIMPLEWIDGET_FAILURE;
    }

    txtp->x = x;
    txtp->y = y;
    txtp->h = height;

    strncpy(txtp->txt, txt, TEXT_MAX_TEXT_LENGTH);

    *swtp = txtp;

    return SIMPLEWIDGET_SUCCESS;
}

int simplewidget_text_show(simplewidget_text swt, simplewidget_screen sws)
{
    _simpletext *txtp = (_simpletext *) swt;
    _simplescreen *scrp = (_simplescreen *) sws;

    if (!txtp || !scrp)
        return SIMPLEWIDGET_FAILURE;

    font_init(FONT_FILE);

    if (font_render(txtp->txt, txtp->x, txtp->y, 0xff, txtp->h, scrp) < 0)
        return SIMPLEWIDGET_FAILURE;

    return SIMPLEWIDGET_SUCCESS;
}

int simplewidget_text_delete(simplewidget_text swt)
{
    _simplebutton *txtp = (_simplebutton *) swt;

    if (!txtp)
        return SIMPLEWIDGET_FAILURE;

    free(txtp);

    return SIMPLEWIDGET_SUCCESS;
}

inline int simplewidget_jpeg_create(char *filename, simplewidget_jpeg *swjp)
{
    _simplejpeg *jpegp;

    jpegp = malloc(sizeof(_simplejpeg));

    if (jpegp == NULL) {
        __E("Failed to allocate memory for jpeg image\n");
        return SIMPLEWIDGET_FAILURE;
    }

    if (jpeg_create(filename, jpegp) == SIMPLEWIDGET_FAILURE) {
        free(jpegp);
        return SIMPLEWIDGET_FAILURE;
    }

    *swjp = jpegp;

    return SIMPLEWIDGET_SUCCESS;
}

int simplewidget_jpeg_show(simplewidget_jpeg swj, simplewidget_screen sws,
                           int x, int y)
{
    _simplejpeg *jpegp = (_simplejpeg *) swj;
    _simplescreen *scrp = (_simplescreen *) sws;

    if (!jpegp || !scrp)
        return SIMPLEWIDGET_FAILURE;

    return jpeg_show(jpegp, scrp, x, y);
}

int simplewidget_jpeg_delete(simplewidget_jpeg swj)
{
    _simplejpeg *jpegp = (_simplejpeg *) swj;
    int result;

    if (!jpegp)
        return SIMPLEWIDGET_FAILURE;

    result = jpeg_delete(jpegp);
    free(jpegp);

    return result;
}

inline int simplewidget_png_create(char *filename, simplewidget_png *swpp)
{
    _simplepng *pngp;

    pngp = malloc(sizeof(_simplepng));

    if (pngp == NULL) {
        __E("Failed to allocate memory for jpeg image\n");
        return SIMPLEWIDGET_FAILURE;
    }

    if (png_create(filename, pngp) == SIMPLEWIDGET_FAILURE) {
        free(pngp);
        return SIMPLEWIDGET_FAILURE;
    }

    *swpp = pngp;

    return SIMPLEWIDGET_SUCCESS;
}

int simplewidget_png_show(simplewidget_png swp, simplewidget_screen sws,
                          int x, int y)
{
    _simplepng *pngp = (_simplepng *) swp;
    _simplescreen *scrp = (_simplescreen *) sws;

    if (!pngp || !scrp)
        return SIMPLEWIDGET_FAILURE;

    return png_show(pngp, scrp, x, y);
}

int simplewidget_png_delete(simplewidget_png swp)
{
    _simplepng *pngp = (_simplepng *) swp;
    int result;

    if (!pngp)
        return SIMPLEWIDGET_FAILURE;

    result = png_delete(pngp);
    free(pngp);

    return result;
}
