#include <stdio.h>
#include <sp/spBaseLib.h>
#include <sp/spComponentLib.h>
#include <sp/spComponentMain.h>
#include <sp/spDIBitmap.h>

#define TEST_STRETCH_BITMAP
//#define DUMP_DIBITMAP
#undef DUMP_DIBITMAP

static spComponent image = NULL;
static spGraphics graphics = NULL;
static spDIBitmap dibitmap = NULL;

static spTimerId timer_id = SP_TIMER_ID_NONE;
static long timer_interval_ms = 50;
static long fade_length = 256;
static long fade_counter = 0;

#define INITIAL_FADE_MODE 0

static int fade_mode = INITIAL_FADE_MODE;
static int next_fade_mode = INITIAL_FADE_MODE;

typedef enum {
    IMAGE_COPY_MODE_NORMAL,
    IMAGE_COPY_MODE_REVERSE,
    IMAGE_COPY_MODE_FLIP_LR,
    IMAGE_COPY_MODE_FLIP_UD,
} ImageCopyMode;
static ImageCopyMode image_copy_mode = /*IMAGE_COPY_MODE_NORMAL*//*IMAGE_COPY_MODE_REVERSE*/IMAGE_COPY_MODE_FLIP_LR;

static spBool copyToCanvas(spDIBitmap dbm, spComponent canvas)
{
    spBool status;
    
#if !defined(TEST_STRETCH_BITMAP)
    if ((status = spCopyDIBitmapToImage(dbm, canvas, 0, 0,
                                        dbm->info.width, dbm->info.height, 0, 0)) == SP_FALSE) {
        spDisplayError(canvas, NULL, "timerCB: Cannot execute spCopyDIBitmapToImage");
    }
#else
    {
        int cw, ch;
        spGetClientSize(canvas, &cw, &ch);
        
        if ((status = spStretchDIBitmapToImage(dbm, canvas, 0, 0,
                                               dbm->info.width, dbm->info.height,
                                               0, 0, cw, ch)) == SP_FALSE) {
            spDisplayError(canvas, NULL, "timerCB: Cannot execute spStretchDIBitmapToImage");
        }
    }
#endif

    return status;
}

static spBool timerCB(spComponent frame, void *data)
{
    int incr;
    int r = 0, g = 0, b = 0;
    int x, y;
    int canvas_width, canvas_height;
    spBool status = SP_TRUE;
    spComponent canvas = (spComponent)data;

    if (spIsDrawable(canvas) == SP_FALSE) {
        /* canvas has not been drawable yet */
        return SP_TRUE;
    }
    spGetSize(canvas, &canvas_width, &canvas_height);
    spFillRectangle(canvas, graphics, 0, 0, canvas_width, canvas_height);
    
    fprintf(stderr, "timerCB: counter = %ld/%ld\n", fade_counter, fade_length);
    
    if (fade_counter >= fade_length / 2) {
        incr = -1;
    } else {
        incr = 1;
    }
    if (fade_mode == 0 || fade_mode == 1) {
        r = incr;
    }
    if (fade_mode == 0 || fade_mode == 2) {
        g = incr;
    }
    if (fade_mode == 0 || fade_mode == 3) {
        b = incr;
    }
    spDebug(100, "timerCB", "r = %d, g = %d, b = %d\n", r, g, b);
    spDebug(100, "timerCB", "width = %d, height = %d\n", dibitmap->info.width, dibitmap->info.height);

    spLockDIBitmap(dibitmap);
    for (y = 0; y < dibitmap->info.height; y++) {
        for (x = 0; x < dibitmap->info.width; x++) {
            spAddDIBitmapRGB(dibitmap, x, y, r, g, b);
        }
    }
    status = copyToCanvas(dibitmap, canvas);
    spUnlockDIBitmap(dibitmap);

    spDebug(80, "timerCB", "status = %d\n", status);
    
    if (status == SP_TRUE) {
        spRefreshCanvas(canvas);
    }

    fade_counter++;
    spDebug(100, "timerCB", "fade_counter = %ld / %ld\n", fade_counter, fade_length);
    
    if (fade_counter >= fade_length) {
        spDebug(80, "timerCB", "fade_mode = %d, next_fade_mode = %d\n", fade_mode, next_fade_mode);
#if defined(DUMP_DIBITMAP)
        spDumpDIBitmapValue(dibitmap);
#endif
        
        fade_counter = 0;
        fade_mode = next_fade_mode;
        spLockDIBitmap(dibitmap);
        spCopyImageToDIBitmap(image, dibitmap, 0, 0,
                              dibitmap->info.width, dibitmap->info.height, 0, 0);
        if (image_copy_mode == IMAGE_COPY_MODE_REVERSE) {
            spReverseDIBitmap(dibitmap);
        } else if (image_copy_mode == IMAGE_COPY_MODE_FLIP_LR) {
            spFlipDIBitmap(dibitmap, SP_TRUE);
        } else if (image_copy_mode == IMAGE_COPY_MODE_FLIP_UD) {
            spFlipDIBitmap(dibitmap, SP_FALSE);
        }
        spUnlockDIBitmap(dibitmap);
        
#if defined(DUMP_DIBITMAP)
        spDumpDIBitmapValue(dibitmap);
#endif
    }
    
    return SP_TRUE;
}

static void canvasCB(spComponent component, void *data)
{
    int canvas_width, canvas_height;
    
    spDebug(80, "canvasCB", "in\n");

    spGetSize(component, &canvas_width, &canvas_height);
    spFillRectangle(component, graphics, 0, 0, canvas_width, canvas_height);

    spLockDIBitmap(dibitmap);
    copyToCanvas(dibitmap, component);
    spUnlockDIBitmap(dibitmap);
    
    spRefreshCanvas(component);
    spDebug(80, "canvasCB", "done\n");
    return;
}

static void radioMenuItemCB(spComponent component, void *data)
{
    next_fade_mode = (int)data;
    return;
}

static spComponent frame = NULL;
static spComponent canvas = NULL;

static spBool toplevelCB(spTopLevel toplevel, spTopLevelCallbackReason reason, void *data)
{
    spDebug(10, "toplevelCB", "reason = %d\n", reason);

    if (reason == SP_TL_CR_INACTIVE) {
        if (timer_id != SP_TIMER_ID_NONE) {
            spKillTimer(frame, timer_id);
        }
    } else if (reason == SP_TL_CR_ACTIVE) {
        if (timer_id != SP_TIMER_ID_NONE) {
            timer_id = spSetTimer(frame, timer_interval_ms, timerCB, canvas);
        }
    }

    return SP_TRUE;
}

int spMain(int argc, char *argv[])
{
    spTopLevel toplevel;
    spComponent menu, menu_bar;
    spComponent mode_menu;
    char *filename;
    int width, height;
    spDIBitmapInfo dibinfo;

    /*spSetDebugLevel(100);*/
    
    /* initialize toolkit */
    toplevel = spInitialize(&argc, &argv,
                            SppTopLevelCallbackFunc, toplevelCB,
                            NULL);
    
    /* create main window */
    frame = spCreateMainFrame("Test DIBitmap",
                              /*SppResizable, SP_FALSE,*/
                              SppCloseStyle, SP_CALLBACK_CLOSE,
                              SppCallbackFunc, spQuitCB,
                              NULL);

    /* create menu bar */
    menu_bar = spCreateMenuBar(frame, "menuBar", NULL);
    
    /* create file menu */
    menu = spCreatePulldownMenu(menu_bar, "fileMenu",
                                SppTitle, "File",
                                NULL);

    /* add menu item */
    spAddMenuItem(menu, SP_QUIT_MENU_ITEM_NAME,
                  SppCallbackFunc, spQuitCB,
                  SppTitle, "Quit",
                  SppShortcut, "A-q",
                  NULL);

    /* add menu item */
    mode_menu = spAddSubMenu(menu_bar, "modeMenu",
                             SppTitle, "Fade Mode",
                             NULL);
    spAddRadioButtonMenuItem(mode_menu, "RGBMenuItem",
                             SppTitle, "RGB",
                             SppCallbackFunc, radioMenuItemCB,
                             SppCallbackData, 0,
                             SppSet, SP_TRUE,
                             NULL);
    spAddRadioButtonMenuItem(mode_menu, "redMenuItem",
                             SppTitle, "Red Only",
                             SppCallbackFunc, radioMenuItemCB,
                             SppCallbackData, 1,
                             NULL);
    spAddRadioButtonMenuItem(mode_menu, "greenMenuItem",
                             SppTitle, "Green Only",
                             SppCallbackFunc, radioMenuItemCB,
                             SppCallbackData, 2,
                             NULL);
    spAddRadioButtonMenuItem(mode_menu, "blueMenuItem",
                             SppTitle, "Blue Only",
                             SppCallbackFunc, radioMenuItemCB,
                             SppCallbackData, 3,
                             NULL);
    
    if (argc >= 2) {
        spDebug(-10, "spMain", "argv[1] = %s\n", argv[1]);
        filename = argv[1];
    } else {
        filename = "tool_bar";
    }

    /* load image file */
    if ((image = spCreateImageFromFile(frame, filename, NULL)) != NULL) {
        spGetSize(image, &width, &height);
        fprintf(stderr, "image: width = %d, height = %d\n", width, height);

        dibinfo.pixel_format = SP_DI_PIXEL_FORMAT_RGB;
        dibinfo.width = width;
        dibinfo.height = height;
        dibinfo.upside_down = SP_FALSE;
        dibinfo.bit_size = 8;

        if ((dibitmap = spCreateDIBitmap(&dibinfo)) == NULL) {
            spDebug(10, "spMain", "spCreateDIBitmap failed\n");
            spDisplayError(frame, NULL, "Cannot create DIBitmap");
            spQuit(0);
        } else {
            spLockDIBitmap(dibitmap);
            if (spCopyImageToDIBitmap(image, dibitmap, 0, 0, width, height, 0, 0) == SP_FALSE) {
                spDebug(10, "spMain", "spCopyImageToDIBitmap failed\n");
                spUnlockDIBitmap(dibitmap);
                spDisplayError(frame, NULL, "Cannot copy image to DIBitmap");
                spQuit(0);
            } else {
                if (image_copy_mode == IMAGE_COPY_MODE_REVERSE) {
                    spReverseDIBitmap(dibitmap);
                } else if (image_copy_mode == IMAGE_COPY_MODE_FLIP_LR) {
                    spFlipDIBitmap(dibitmap, SP_TRUE);
                } else if (image_copy_mode == IMAGE_COPY_MODE_FLIP_UD) {
                    spFlipDIBitmap(dibitmap, SP_FALSE);
                }
                spUnlockDIBitmap(dibitmap);
#if defined(DUMP_DIBITMAP)
                spDumpDIBitmapValue(dibitmap);
#endif

                /* create canvas */
                canvas = spCreateCanvas(frame, "canvas", width, height,
                                        SppBorderOn, SP_FALSE,
                                        SppDrawBackground, /*SP_TRUE*/SP_FALSE,
                                        SppCallbackFunc, canvasCB,
                                        NULL);

                graphics = spCreateGraphics("graphics1",
                                            SppLineWidth, 1,
                                            SppForeground, "white",
                                            NULL);

                /* set timer */
                timer_id = spSetTimer(frame, timer_interval_ms, timerCB, canvas);
            }
        }
    } else {
        spDebug(10, "spMain", "spCreateImageFromFile failed\n");
        spDisplayError(frame, NULL, "Cannot open image file: %s", filename);
        spQuit(0);
    }
    
    /* popup window */
    spPopupWindow(frame);
    
    /* main loop */
    return spMainLoop(toplevel);
}
