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

#define SP_PRINT_CANVAS_DEFAULT_NUM_PAGE 3

#define FONT_SIZE_TEST
#define STRING_ROTATION_TEST

#define JAPANESE_TEST
#if defined(COCOA)
#define UTF8_JP_TEST
#endif

#ifdef JAPANESE_TEST
#if (defined(_WIN32) && !defined(__CYGWIN32__)) || (defined(MACOS) && !defined(COCOA))
#include "strings_sjis.h"
#elif defined(UTF8_JP_TEST)
#include "strings_utf8jp.h"
#else
#include "strings_eucjp.h"
#endif
#else
#define HELLO_STRING "Hello World"
#endif

#define DEFAULT_FONT "-*-*-medium-*-normal--16-*-*-*-*-*-*-*"
#ifdef FONT_SIZE_TEST
#define FONTNAME_FORMAT_STRING "-*-*-medium-*-normal--%d-*-*-*-*-*-*-*"
#endif

#define STRING_LEFT_OFFSET 5
#define STRING_TOP_OFFSET 50
#define FONT_SIZE_TEST_VSPACING_FACTOR 3

static spGraphics graphics = NULL;
static char hello_string[] = HELLO_STRING;

static char current_print_plugin[SP_MAX_LINE] = "";
static spComponent print_canvas = NULL;
static spComponent file_image = NULL;
static spComponent multi_page_print_menu_item = NULL;
static spBool multi_page_print = SP_FALSE;

static void canvasCB(spComponent component, void *data)
{
    int width = 0, height = 0;
    int sx = 0, sy = 0;
    int swidth = 0, sheight = 0;
    int file_width = 0, file_height = 0;
    
    spDebug(-20, "canvasCB", "in\n");
    
    spGetSize(component, &width, &height);
    spDebug(-20, "canvasCB", "width = %d, height = %d\n", width, height);

    spSetGraphicsParams(graphics, SppForeground, "white", NULL);
    spFillRectangle(component, graphics, 0, 0, width, height);
    
    spSetGraphicsParams(graphics, SppForeground, "blue", NULL);
    spDrawRectangle(component, graphics, 0, 0, width - 1, height - 1);
    
    if (multi_page_print == SP_TRUE) {
        char buf[SP_MAX_LINE];

        sprintf(buf, "%d", spGetCurrentPage(component));
        spDebug(-20, "canvasCB", "buf = %s\n", buf);
        if (spGetStringExtent(component, graphics, buf, &sx, &sy, &swidth, &sheight, NULL) == SP_TRUE) {
            spDrawString(component, graphics, (width - swidth) / 2, height - (sheight + sy + 5), buf);
        }
    }
    
    spSetGraphicsParams(graphics, SppForeground, "red", NULL);

    spDebug(20, "canvasCB", "before spDrawLine\n");
    spDrawLine(component, graphics, 0, 0, width, height);
    spDrawLine(component, graphics, width, 0, 0, height);
    
    spDebug(20, "canvasCB", "before spFillRectangle\n");
    spFillRectangle(component, graphics, 80, 80, 100, 50);

    spDebug(20, "canvasCB", "before spFillArc\n");
    spFillArc(component, graphics, width / 2 - 50, height / 2 - 50,
              100, 100, 0, 45);
    spDrawArc(component, graphics, width / 2 - 50, height / 2 - 50,
              100, 100, 90, 45);
    
    spDebug(20, "canvasCB", "before spGetStringExtent\n");
    if (spGetStringExtent(component, graphics, hello_string, &sx, &sy, &swidth, &sheight, NULL) == SP_TRUE) {
        spDebug(20, "canvasCB", "sx = %d, sy = %d, swidth = %d, sheight = %d\n", sx, sy, swidth, sheight);
        spDrawRectangle(component, graphics, STRING_LEFT_OFFSET + sx, STRING_TOP_OFFSET + sy, swidth, sheight);
    }
    spDrawString(component, graphics, STRING_LEFT_OFFSET, STRING_TOP_OFFSET, hello_string);
    spDebug(20, "canvasCB", "spDrawString done\n");

#ifdef FONT_SIZE_TEST
    {
        int i, j, r;
        int current_left;
        int current_top;
        int current_font_size;
        spAlignment align;
        char fontname[SP_MAX_LINE];

        current_top = STRING_TOP_OFFSET + (int)spRound((double)sheight * 0.5 * FONT_SIZE_TEST_VSPACING_FACTOR);
        current_font_size = 20;

        for (i = 0; i < 6; i++) {
            current_left = STRING_LEFT_OFFSET;
            sprintf(fontname, FONTNAME_FORMAT_STRING, current_font_size);
            spSetGraphicsParams(graphics, SppFontName, fontname, NULL);

            if (spGetStringExtent(component, graphics, hello_string, &sx, &sy, &swidth, &sheight, NULL) == SP_TRUE) {
                spDebug(-20, "canvasCB", "i = %d: sx = %d, sy = %d, swidth = %d, sheight = %d\n", i, sx, sy, swidth, sheight);
                spDrawRectangle(component, graphics, current_left + sx, current_top, swidth, sheight);
                spDrawString(component, graphics, current_left, current_top - sy, hello_string);
            }

#ifdef STRING_ROTATION_TEST
            spDrawLine(component, graphics, 0, current_top - sy, width, current_top - sy);
            for (j = 0; j <= 2; j++) {
                align = j;
                spSetGraphicsParams(graphics, SppStringAlignment, align, NULL);
                current_left = (j + 1) * width / 4;
                if (i == 0) {
                    spDrawLine(component, graphics, current_left, 0, current_left, height);
                }
                for (r = -30; r <= /*120*/90; r += 30) {
                    spSetGraphicsParams(graphics, SppStringRotation, r, NULL);
                    spDrawString(component, graphics, current_left, current_top - sy, hello_string);
                }
            }
            spSetGraphicsParams(graphics,
                                SppStringRotation, 0,
                                SppStringAlignment, SP_ALIGNMENT_BEGINNING,
                                NULL);
#endif
            
            current_font_size += 4;
            current_top += (int)spRound((double)sheight * 1.5 * FONT_SIZE_TEST_VSPACING_FACTOR);
        }

        spSetGraphicsParams(graphics, SppFontName, DEFAULT_FONT, NULL);
    }
#endif
    
    if (spGetClientSize(file_image, &file_width, &file_height) == SP_TRUE) {
        spDebug(10, "canvasCB", "file_width = %d, file_height = %d\n", file_width, file_height);
        spCopyImage(file_image, component, 0, 0, file_width, file_height,
                    width - file_width - 10, height - file_height - 10);
    }
    
    spDebug(-20, "canvasCB", "done\n");
    
    return;
}

static spComponent createPluginCanvas(spComponent component, char *plugin_name)
{
    spDebug(80, "createPluginCanvas", "component = %ld, plugin_name = %s\n", (long)component, plugin_name);
    
    if (!streq(plugin_name, current_print_plugin)) {
        if (print_canvas != NULL) spDestroyComponent(print_canvas);
        
        print_canvas = spCreatePluginCanvas(spGetWindow(component), plugin_name,
                                            SppDrawFunc, canvasCB,
                                            NULL);
        if (print_canvas != NULL) {
            spStrCopy(current_print_plugin, sizeof(current_print_plugin), plugin_name);
        } else {
            spDisplayError(component, NULL, "Cannot open drawing plugin %s.", plugin_name);
        }
    }

    return print_canvas;
}

static void drawingPluginInfoCB(spComponent component, const char *plugin_name)
{
    const char *plugin_info;

    if ((plugin_info = spGetDrawingPluginInformation(plugin_name)) != NULL) {
        spDisplayInformation(NULL, NULL, plugin_info);
    } else {
        spDisplayWarning(NULL, NULL, "Cannot get information of print target.");
    }
    
    return;
}

static void createDrawingPluginMenu(spComponent parent_menu, spBool help_flag)
{
    int i;
    spComponent sub_menu, menu_item;
    const char *plugin_name;
    const char *plugin_desc;
    int printer_index = -1;

    if (help_flag == SP_TRUE) {
        sub_menu = spAddSubMenu(parent_menu, "drawingPluginHelpSubMenu",
                                SppTitle, "&Plugin Information",
                                NULL);
    } else {
        sub_menu = spAddSubMenu(parent_menu, "drawingPluginSubMenu",
                                SppTitle, "&Print Target",
                                NULL);
    }

    for (i = 0;; i++) {
        plugin_name = spSearchDrawingPluginFile(i, SP_DRAWING_DEVICE_FILE | SP_DRAWING_DEVICE_PRINTER);
        if (plugin_name == NULL) break;
        
        plugin_desc = spGetDrawingPluginDescription(plugin_name);
        
        if (plugin_name != NULL && plugin_desc != NULL) {
            if (help_flag == SP_TRUE) {
                menu_item = spAddMenuItem(sub_menu, "drawingPluginHelpMenuItem",
                                          SppTitle, spGetString(plugin_desc),
                                          SppCallbackFunc, drawingPluginInfoCB,
                                          SppCallbackData, plugin_name,
                                          NULL);
            } else {
                if (streq(plugin_name, SP_DRAWING_PLUGIN_PRINTER)) {
                    printer_index = i;
                }
                menu_item = spAddRadioButtonMenuItem(sub_menu, "drawingPluginMenuItem",
                                                     SppTitle, spGetString(plugin_desc),
                                                     SppCallbackFunc, createPluginCanvas,
                                                     SppCallbackData, plugin_name,
                                                     SppSet, (printer_index == i ? SP_TRUE : SP_FALSE),
                                                     NULL);
            }
        }
    }

    if (printer_index >= 0) {
        createPluginCanvas(spGetWindow(parent_menu), SP_DRAWING_PLUGIN_PRINTER);
    }

    return;
}

static void pageSetupCB(spComponent component, void *data)
{
    spBool flag;
    
    flag = spOpenDrawingSetupDialog(print_canvas, NULL);
    
    spDebug(20, "pageSetupCB", "done: flag = %d\n", flag);
    
    return;
}

static void printCB(spComponent component, void *data)
{
    spBool flag;

    if (multi_page_print == SP_TRUE) {
        if (spSetNumPage(print_canvas, SP_PRINT_CANVAS_DEFAULT_NUM_PAGE) == SP_FALSE) {
            spDisplayWarning(NULL, NULL, "Printing a multiple-page document is not supported.");
            spSetToggleState(multi_page_print_menu_item, SP_FALSE);
            multi_page_print = SP_FALSE;
        }
    }

    if (multi_page_print == SP_TRUE) {
        flag = spOpenDrawingTargetDialog(print_canvas,
                                         SppEnablePageRange, SP_TRUE,
                                         SppCurrentPage, 2,
                                         NULL);
    } else {
        spSetNumPage(print_canvas, 1);
        flag = spOpenDrawingTargetDialog(print_canvas, NULL);
    }
    
    if (flag == SP_TRUE) {
        flag = spRedrawCanvas(print_canvas);
        spDebug(20, "printCB", "spRedrawCanvas: flag = %d\n", flag);
        spCloseDrawingTarget(print_canvas);
    }
    
    return;
}

static void checkCB(spComponent component, spBool *flag)
{
    spGetToggleState(component, flag);
    return;
}

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

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

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

    /* create menu items for printing */
    multi_page_print_menu_item = spAddCheckBoxMenuItem(menu, "multiPagePrintMenuItem",
                                                       SppTitle, "Enable Multiple Page Printing",
                                                       SppCallbackFunc, checkCB,
                                                       SppCallbackData, &multi_page_print,
                                                       SppSet, multi_page_print,
                                                       SppDescription, "Enable multiple-page printing test",
                                                       NULL);
    createDrawingPluginMenu(menu, SP_FALSE);
    spAddMenuItem(menu, "pageSetupMenuItem",
                  SppTitle, "Page Set&up...",
                  SppCallbackFunc, pageSetupCB,
                  SppDescription, "Open page setup dialog",
                  NULL);
    spAddMenuItem(menu, "printMenuItem",
                  SppTitle, "&Print...",
                  SppCallbackFunc, printCB,
                  SppDescription, "Print window contents",
                  SppShortcut, "A-p",
                  NULL);

    /* quit menu separator */
    spAddMenuSeparator(menu, SP_QUIT_MENU_SEPARATOR_NAME, NULL);
    
    /* add menu item */
    spAddMenuItem(menu, SP_QUIT_MENU_ITEM_NAME,
                  SppCallbackFunc, spQuitCB,
                  SppTitle, "Quit",
                  SppDescription, "Quit program",
                  SppShortcut, "A-q",
                  NULL);

    /* create help menu */
    menu = spCreatePulldownMenu(menu_bar, "Help",
                                SppMenuHelp, SP_TRUE,
                                NULL);
    spAddMenuItem(menu, "About",
                  SppCallbackFunc, spDisplayInformationCB,
                  SppCallbackData, "Print Canvas  Version 1.0\nA printing demo using spComponent",
                  NULL);
    createDrawingPluginMenu(menu, SP_TRUE);

    /* create canvas */
    canvas = spCreateCanvas(frame, "canvas", 300, 300,
                            SppBorderOn, SP_TRUE,
                            /*SppDrawBackground, SP_TRUE,*/
                            SppCallbackFunc, canvasCB,
                            NULL);
    file_image = spCreateImageFromFile(canvas, "tool_bar",
                                       NULL);

    graphics = spCreateGraphics("graphics1",
                                SppLineType, SP_LINE_DASH_DOT,
                                SppLineWidth, 2,
                                SppFontName, DEFAULT_FONT,
                                NULL);
    
    /* popup window */
    spPopupWindow(frame);
    
    /* main loop */
    return spMainLoop(toplevel);
}
