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

#define INITIAL_CIRCLE_RADIUS 50
#define WHEEL_STEP /*10*/1
#define KEY_STEP_SHORT 50
#define KEY_STEP_LONG 100
#define MAX_ZOOM_FACTOR /*10.0*/2.0
#define MIN_ZOOM_FACTOR /*0.1*/0.6

static int circle_x = -1;
static int circle_y = -1;
static int circle_radius = INITIAL_CIRCLE_RADIUS;

static spBool wheel_target_horizontal = SP_FALSE;
static spBool scroll_left_by_wheel_down = SP_FALSE;

static void drawCanvas(spComponent component, spGraphics graphics)
{
    int width = 0, height = 0;

    if (spGetSize(component, &width, &height) == SP_FALSE) {
        return;
    }
    
    spDebug(20, "drawCanvas", "width = %d, height = %d\n", width, height);

    if (circle_x == -1 && circle_y == -1) {
        circle_x = width / 2;
        circle_y = height / 2;
    } else {
        if (circle_x > width) {
            circle_x = circle_x % width;
        } else if (circle_x < 0) {
            circle_x = width - ((-circle_x) % width);
        }
        if (circle_y > height) {
            circle_y = circle_y % height;
        } else if (circle_y < 0) {
            circle_y = height - ((-circle_y) % height);
        }
    }

    spSetGraphicsParams(graphics, SppForeground, "white", NULL);
    spFillRectangle(component, graphics, 0, 0, width, height);
    
    spSetGraphicsParams(graphics, SppForeground, "green", NULL);

    spFillArc(component, graphics, circle_x - circle_radius, circle_y - circle_radius,
              circle_radius * 2, circle_radius * 2, 0, 360);

    spRefreshCanvas(component);
    
    spDebug(20, "drawCanvas", "done\n");
    
    return;
}

static void wheelCB(spComponent component, void *data)
{
    double h_factor, v_factor, factor;
    double delta_x, delta_y;
    spCallbackReason reason;
    spGraphics graphics = (spGraphics)data;
    
    reason = spGetCallbackReason(component);
    spDebug(20, "wheelCB", "reason = %d\n", reason);
    
    if (reason == SP_CR_ZOOM) {
        spGetParams(component,
                    SppHorizontalContentFactor, &h_factor,
                    SppVerticalContentFactor, &v_factor,
                    NULL);
        spDebug(20, "wheelCB", "h_factor = %f, v_factor = %f\n", h_factor, v_factor);
        factor = MAX(h_factor, v_factor);
        factor = MIN(factor, MAX_ZOOM_FACTOR);
        factor = MAX(factor, MIN_ZOOM_FACTOR);
        circle_radius = (int)spRound(INITIAL_CIRCLE_RADIUS * factor);
        spDebug(20, "wheelCB", "factor = %f, circle_radius = %d\n", factor, circle_radius);

        spSetParams(component,
                    SppHorizontalContentFactor, factor,
                    SppVerticalContentFactor, factor,
                    NULL);
    } else if (reason == SP_CR_WHEEL) {
        if (spGetCallbackWheelValue(component, &delta_x, &delta_y) == SP_TRUE) {
            spDebug(20, "wheelCB", "delta_x = %f, delta_y = %f\n", delta_x, delta_y);
            circle_x += (int)spRound(delta_x * WHEEL_STEP);
                
            circle_y -= (int)spRound(delta_y * WHEEL_STEP);
        }
    } else if (reason == SP_CR_KEY_PRESS) {
        spKeySym key_sym;
        
        if (spGetCallbackKeySym(component, &key_sym) == SP_TRUE) {
            spDebug(20, "wheelCB", "key_sym = %lx\n", key_sym);
            switch (key_sym) {
              case SPK_Prior:
                circle_y -= KEY_STEP_LONG;
                break;
              case SPK_Next:
                circle_y += KEY_STEP_LONG;
                break;
                
              case SPK_Up:
                circle_y -= KEY_STEP_SHORT;
                break;
              case SPK_Down:
                circle_y += KEY_STEP_SHORT;
                break;
                
              case SPK_Left:
                circle_x -= KEY_STEP_SHORT;
                break;
              case SPK_Right:
                circle_x += KEY_STEP_SHORT;
                break;
                
              case SPK_Home:
                circle_y = 0;
                break;
              case SPK_End:
                circle_y = -1;
                break;
              default:
                break;
            }
        }
    }
    drawCanvas(component, graphics);
    
    return;
}

static void canvasCB(spComponent component, void *data)
{
    spCallbackReason reason;
    spGraphics graphics = (spGraphics)data;
    
    reason = spGetCallbackReason(component);
    spDebug(20, "canvasCB", "in: reason = %d\n", reason);
    drawCanvas(component, graphics);
    spDebug(20, "canvasCB", "done\n");
    
    return;
}
    
static void checkWheelTargetHorizontalCB(spComponent component, spComponent canvas)
{
    if (spGetToggleState(component, &wheel_target_horizontal)) {
        spDebug(20, "checkWheelTargetHorizontalCB", "flag = %d\n", wheel_target_horizontal);
        spSetParams(canvas, SppWheelTargetHorizontal, wheel_target_horizontal, NULL);
    }
    
    return;
}

static void checkScrollLeftByWheelDownCB(spComponent component, spComponent canvas)
{
    if (spGetToggleState(component, &scroll_left_by_wheel_down)) {
        spDebug(20, "checkScrollLeftByWheelDownCB", "flag = %d\n", scroll_left_by_wheel_down);
        spSetParams(canvas, SppScrollLeftByWheelDown, scroll_left_by_wheel_down, NULL);
    }
    
    return;
}

static void helloCB(spComponent component, void *data)
{
    static int count = 0;

    spDebug(10, "helloCB", "count = %d\n", count);

    if (count >= 1) {
        if (spQuitPrompt(component) == SP_TRUE) {
            /* quit program */
            spQuit(0);
        } else {
            /* change label */
            spSetParams(component, SppTitle, "Hello World", NULL);
            count = 0;
        }
    } else {
        /* change label */
        spSetParams(component, SppTitle, "Goodbye World", NULL);
        count++;
    }
    
    return;
}

int spMain(int argc, char *argv[])
{
    spTopLevel toplevel;
    spComponent frame;
    spComponent menu, menu_bar;
    spComponent canvas;
    spGraphics graphics;

    /*spSetDebugLevel(100);*/
    
    /* initialize toolkit */
    toplevel = spInitialize(&argc, &argv, NULL);
    
    /* create main window */
    frame = spCreateMainFrame("Wheel Test", NULL);

    graphics = spCreateGraphics("graphics1",
                                SppLineType, SP_LINE_DASH_DOT,
                                SppLineWidth, 1,
                                SppFontName, "-*-*-medium-*-normal--16-*-*-*-*-*-*-*",
                                NULL);
    
    /* create canvas */
    canvas = spCreateCanvas(frame, "canvas", 300, 300,
                            SppBorderOn, SP_TRUE,
                            SppAcceptWheelEvent, SP_TRUE,
                            SppCallbackFunc, canvasCB,
                            SppCallbackData, graphics,
                            NULL);
    spAddCallback(canvas, SP_WHEEL_CALLBACK | SP_ZOOM_CALLBACK | SP_KEY_PRESS_CALLBACK, wheelCB, graphics);

    /* create popup menu */
    menu = spCreatePopupMenu(canvas, "popupMenu",
                             NULL);
    
    /* add menu item */
    spAddMenuItem(menu, "Hello World",
                  SppCallbackFunc, helloCB,
                  SppShortcut, "A-d",
                  NULL);
    
    /* create menu bar */
    menu_bar = spCreateMenuBar(frame, "menuBar", NULL);
    
    /* create menu */
    menu = spCreatePulldownMenu(menu_bar, "fileMenu",
                                SppTitle, "File",
                                NULL);
    
    /* add menu item */
    spAddCheckBoxMenuItem(menu, "Wheel target is horizontal",
                          SppCallbackFunc, checkWheelTargetHorizontalCB,
                          SppCallbackData, canvas,
                          SppSet, wheel_target_horizontal,
                          NULL);
    spAddCheckBoxMenuItem(menu, "Move left by wheel down",
                          SppCallbackFunc, checkScrollLeftByWheelDownCB,
                          SppCallbackData, canvas,
                          SppSet, scroll_left_by_wheel_down,
                          NULL);
    spAddMenuSeparator(menu, SP_QUIT_MENU_SEPARATOR_NAME, NULL);
    spAddMenuItem(menu, SP_QUIT_MENU_ITEM_NAME,
                  SppCallbackFunc, spQuitCB,
                  SppTitle, "Quit",
                  SppShortcut, "A-q",
                  NULL);

    /* popup window */
    spPopupWindow(frame);
    
    /* main loop */
    return spMainLoop(toplevel);
}
