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

#define SP_SCROLL_CANVAS_NUM_RECT 20
#define SP_SCROLL_CANVAS_IMAGE_WIDTH 500
#define SP_SCROLL_CANVAS_IMAGE_HEIGHT 500
#define SP_SCROLL_CANVAS_INCREMENT 50
#define SP_SCROLL_CANVAS_PAGE_INCREMENT 50
#define SP_SCROLL_CANVAS_WHEEL_INCREMENT 10

void drawRect(spComponent component, spGraphics gr, int x, int y, int width, int height,
              double r, double g, double b)
{
    spPixel pixel;
    unsigned char rc, gc, bc;

    rc = (unsigned char)spRound(255.0 * r);
    gc = (unsigned char)spRound(255.0 * g);
    bc = (unsigned char)spRound(255.0 * b);

    spDebug(100, "drawRect", "x = %d, y = %d, width = %d, height = %d, r, g, b = %d, %d, %d\n",
            x, y, width, height, rc, gc, bc);
    
    pixel = spRGB(rc, gc, bc);
    spSetForegroundPixel(gr, pixel);
    spFillRectangle(component, gr, x, y, width, height);
    
    return;
}

static void drawCanvas(spComponent component, spGraphics gr, int width, int height,
                       int image_width, int image_height, 
                       int xpos, int xpagesize, double xfact,
                       int ypos, int ypagesize, double yfact)
{
    int i, j;
    int x, y;
    int xn, yn;
    double x2, y2, x2incr, y2incr;
    spPixel pixel;

    pixel = spRGB(0, 0, 255);
    spSetForegroundPixel(gr, pixel);
#if 0
    spFillRectangle(component, gr, 0, 0, width, height);
    spSetForegroundPixel(gr, spRGB(0, 255, 0));
    spSetGraphicsParams(gr, SppLineWidth, 10, NULL);
    spDrawRectangle(component, gr, 0, 0, width, height);
    spDrawLine(component, gr, 0, 0, width, height);
    spDrawLine(component, gr, width, 0, 0, height);
    spSetGraphicsParams(gr, SppLineWidth, 1, NULL);
#else
    spFillRectangle(component, gr, 0, 0, width, height);

    x2incr = (double)(image_width / SP_SCROLL_CANVAS_NUM_RECT);
    if (x2incr < 1.0f) x2incr = 1.0;
    y2incr = (double)(image_height / SP_SCROLL_CANVAS_NUM_RECT);
    if (y2incr < 1.0f) y2incr = 1.0;
    spDebug(80, "drawCanvas", "xfact = %f, yfact = %f, x2incr = %f, y2incr = %f\n",
            xfact, yfact, x2incr, y2incr);
    
    x = y = 0;
    xn = yn = -1;
    for (i = 0, x2 = 0.0; x2 < (double)image_width; i++, x2 += x2incr) {
        if (x2 + x2incr > (double)xpos) {
            if (xn < 0) {
                x = (int)spRound((x2 - (double)xpos) * xfact);
            } else {
                x = xn;
            }
            if (x > width) {
                break;
            }
            x = MAX(x, 0);
            xn = (int)spRound((x2 + x2incr - (double)xpos) * xfact);

            yn = -1;
            for (j = 0, y2 = 0.0; y2 < (double)image_height; j++, y2 += y2incr) {
                if (y2 + y2incr > (double)ypos) {
                    if (yn < 0) {
                        y = (int)spRound((y2 - (double)ypos) * yfact);
                    } else {
                        y = yn;
                    }
                    if (y > height) {
                        break;
                    }
                    y = MAX(y, 0);
                    yn = (int)spRound((y2 + y2incr - (double)ypos) * yfact);
                    drawRect(component, gr, x, y, xn - x, yn - y,
                             x2 / (double)image_width, y2 / (double)image_height, 0.0);
                }
            }
        }
    }
    
    spRefreshCanvas(component);

    spDebug(80, "drawCanvas",
            "x = %d, y = %d, x2 = %f, y2 = %f, width = %d, height = %d, image_width = %d, image_height = %d, i = %d, j = %d\n",
            x, y, x2, y2, width, height, image_width, image_height, i, j);
#endif
    
    return;
}

static void canvasCB(spComponent component, void *data)
{
    spCallbackReason reason;
    int width = 0, height = 0;
    int h_value;
    int h_page_size;
    double h_content_factor;
    int v_value;
    int v_page_size;
    double v_content_factor;
    double content_factor;
    int h_new_range;
    int v_new_range;
    spGraphics graphics = (spGraphics)data;
    
    spDebug(20, "canvasCB", "name = %s\n", spGetName(component));
    
    if (spGetSize(component, &width, &height) == SP_TRUE) {
        reason = spGetCallbackReason(component);
        spDebug(20, "canvasCB", "reason = %d, width = %d, height = %d\n", reason, width, height);

        spGetParams(component,
                    SppHorizontalValue, &h_value,
                    SppHorizontalPageSize, &h_page_size,
                    SppHorizontalContentFactor, &h_content_factor,
                    SppVerticalValue, &v_value,
                    SppVerticalPageSize, &v_page_size,
                    SppVerticalContentFactor, &v_content_factor,
                    NULL);
        spDebug(20, "canvasCB", "h_value = %d, h_page_size = %d, v_value = %d, v_page_size = %d\n",
                h_value, h_page_size, v_value, v_page_size);
        spDebug(20, "canvasCB", "h_content_factor = %f, v_content_factor = %f\n",
                h_content_factor, v_content_factor);

        h_new_range = (int)spRound(h_content_factor * (double)h_page_size);
        v_new_range = (int)spRound(v_content_factor * (double)v_page_size);
        spDebug(20, "canvasCB", "h_new_range = %d, v_new_range = %d\n", h_new_range, v_new_range);
        
        content_factor = MIN(h_content_factor, v_content_factor);
        
        drawCanvas(component, graphics, width, height, 
                   SP_SCROLL_CANVAS_IMAGE_WIDTH, SP_SCROLL_CANVAS_IMAGE_HEIGHT, 
                   h_value, h_page_size, content_factor, v_value, v_page_size, content_factor);
    }
    
    spDebug(20, "canvasCB", "done\n");
    
    return;
}

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

    /* initialize toolkit */
    toplevel = spInitialize(&argc, &argv, NULL);
    
    /* create main window */
    frame = spCreateMainFrame("Scroll Canvas", NULL);

    graphics = spCreateGraphics("graphics", NULL);
    
    /* create canvas */
    canvas = spCreateCanvas(frame, "canvas",
                            SP_SCROLL_CANVAS_IMAGE_WIDTH, SP_SCROLL_CANVAS_IMAGE_HEIGHT,
                            SppBorderOn, SP_TRUE,
                            SppCallbackFunc, canvasCB,
                            SppCallbackData, graphics,
                            SppScrollHorizontal, SP_TRUE,
                            SppHorizontalMaximum, SP_SCROLL_CANVAS_IMAGE_WIDTH,
                            SppHorizontalPageSize, SP_SCROLL_CANVAS_IMAGE_WIDTH,
                            SppHorizontalIncrement, SP_SCROLL_CANVAS_INCREMENT,
                            SppHorizontalPageIncrement, SP_SCROLL_CANVAS_PAGE_INCREMENT,
                            SppHorizontalWheelIncrement, SP_SCROLL_CANVAS_WHEEL_INCREMENT,
                            SppScrollVertical, SP_TRUE,
                            SppVerticalMaximum, SP_SCROLL_CANVAS_IMAGE_HEIGHT,
                            SppVerticalPageSize, SP_SCROLL_CANVAS_IMAGE_HEIGHT,
                            SppVerticalIncrement, SP_SCROLL_CANVAS_INCREMENT,
                            SppVerticalPageIncrement, SP_SCROLL_CANVAS_PAGE_INCREMENT,
                            SppVerticalWheelIncrement, SP_SCROLL_CANVAS_WHEEL_INCREMENT,
                            NULL);

    /* create menu bar */
    menu_bar = spCreateMenuBar(frame, "menuBar", NULL);
    
    /* create menu */
    menu = spCreatePulldownMenu(menu_bar, "fileMenu",
                                SppTitle, "File",
                                NULL);
    
    /* add menu item */
    spAddMenuItem(menu, "zoomIn",
                  SppCallbackFunc, spZoomInScrollCanvasCB,
                  SppCallbackData, canvas,
                  SppTitle, "Zoom In",
                  SppShortcut, "A-+",
                  NULL);
    spAddMenuItem(menu, "zoomOut",
                  SppCallbackFunc, spZoomOutScrollCanvasCB,
                  SppCallbackData, canvas,
                  SppTitle, "Zoom Out",
                  SppShortcut, "A--",
                  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);
}
