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

#if defined(MACOS9)
#define MAX_VALUE 1000000
#else
#define MAX_VALUE 100000000
#endif
#define CHECK_INTERVAL 1000

static spTopLevel toplevel;

static volatile spBool thread_exit_flag = SP_FALSE;
static void *thread = NULL;
static spBool show_dialog = SP_FALSE;

spThreadReturn threadFunc(void *data)
{
    long k;
    int percentage, prev_percentage;
    char buf[SP_MAX_LINE];
    spComponent component = (spComponent)data;
    
    spDebug(10, "threadFunc", "thread start\n");
    spThreadEnter();
    spSetTextString(component, "Thread started.");
    spThreadLeave();

    show_dialog = SP_TRUE;
    
    prev_percentage = -1;
    for (k = 1; k < MAX_VALUE; k++) {
        if (thread_exit_flag == SP_TRUE) {
            break;
        }
        
        percentage = (int)(100.0 * ((double)k / (double)MAX_VALUE));
            
        if (k % CHECK_INTERVAL == 0) {
            spDebug(100, "threadFunc", "k = %ld, prev_percentage = %d\n", k, prev_percentage);
            if (percentage > prev_percentage) {
#if 0
                spDebug(10, "threadFunc", "k = %ld, percentage = %d\n", k, percentage);
                fflush(stderr);
#endif
                
                sprintf(buf, "Processing... (%d %% done)", percentage);
                spThreadEnter();
                spDebug(10, "threadFunc", "spThreadEnter done: percentage = %d, buf = %s\n",
                        percentage, buf);
                spSetTextString(component, buf);
                spDebug(10, "threadFunc", "spSetTextString done\n");
                if (percentage >= 50 && show_dialog == SP_TRUE) {
                    show_dialog = SP_FALSE;
                    spDisplayMessage(component, NULL,
                                     "%s\nThis message is displayed from a thread.\nDisplaying a dialog from a thread is not recommended.\nPlease see the sample program `thread_event'.",
                                     buf);
                }
                spThreadLeave();
                
                prev_percentage = percentage;
            }
            spYieldThread();
        }
    }
    
    spDebug(10, "threadFunc", "loop end\n");
    
    spThreadEnter();
    spDebug(10, "threadFunc", "spThreadEnter done\n");
    spSetTextString(component, "Thread finished.");
    spThreadLeave();
    spDebug(10, "threadFunc", "thread end\n");
    
    return SP_THREAD_RETURN_SUCCESS;
}

static void quitCB(spComponent component, void *data)
{
    /* quit program */
    if (spQuitPrompt(component) == SP_FALSE) {
        /* quit canceled */
        thread_exit_flag = SP_TRUE;
        
        spWaitThreadLoop(toplevel, thread);
        spDestroyThread(thread);
        
        /* create thread again */
        thread_exit_flag = SP_FALSE;
        thread = spCreateThread(0, SP_THREAD_PRIORITY_NORMAL, threadFunc, data);
    } else {
        thread_exit_flag = SP_TRUE;

        spDebug(10, "quitCB", "call spWaitThreadLoop\n");
        spWaitThreadLoop(toplevel, thread);
        
        spDebug(10, "quitCB", "call spDestroyThread\n");
        spDestroyThread(thread);

        spQuit(0);
    }

    return;
}

static void textCB(spComponent component, void *data)
{
    char *string;

    if ((string = xspGetTextString(component)) != NULL) {
#if defined(DEBUG)
        printf("reason = %d: %s\n", spGetCallbackReason(component), string);
#endif
        xspFree(string);
    }
    
    return;
}

int spMain(int argc, char *argv[])
{
    spComponent frame;
    spComponent button;
    spComponent text;

    /*spSetDebugLevel(10);*/
    
    /*spSetThreadLevel(SP_THREAD_LEVEL_LOW);*/
    
    /* initialize toolkit */
    toplevel = spInitialize(&argc, &argv,
                            SppThreadSafe, SP_TRUE,
                            NULL);
    
    /* create main window */
    frame = spCreateMainFrame("Thread", NULL);

    /* create text field */
    text = spCreateTextField(frame, "text",
                             SppCallbackFunc, textCB,
                             SppEditable, SP_FALSE,
                             NULL);

    /* create push button */
    button = spCreatePushButton(frame, "Quit Program",
                                SppCallbackFunc, quitCB,
                                SppCallbackData, text,
                                SppInitialWidth, 200,
                                NULL);

    /* popup window */
    spPopupWindow(frame);

    thread_exit_flag = SP_FALSE;
    thread = spCreateThread(0, SP_THREAD_PRIORITY_NORMAL, threadFunc, text);
    
    /* main loop */
    return spMainLoop(toplevel);
}
