/*
 *      File mguimain.c
 *      Creates the main shell of the application
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "mgui.h"

/*
 *     Menu Callbacks.
 */
void STextCB(MOBJECT o, void *id, void *a_data);
void SListCB(MOBJECT o, void *id, void *a_data);
void BlockListCB(MOBJECT o, void *id, void *a_data);
void EditListCB(MOBJECT o, void *id, void *a_data);
void SkipListCB(MOBJECT o, void *id, void *a_data);
void MSelListCB(MOBJECT o, void *id, void *a_data);
void SFileCB(MOBJECT o, void *id, void *a_data);
void ScrolledFormCB(MOBJECT o, void *id, void *a_data);
void PagedFormCB(MOBJECT o, void *id, void *a_data);
void ClippedFormCB(MOBJECT o, void *id, void *a_data);
void FontViewCB(MOBJECT o, void *id, void *a_data);
void ColPickCB(MOBJECT o, void *id, void *a_data);
void ObjectManageCB(MOBJECT o, void *id, void *a_data);
void TRButtonCB(MOBJECT obj, void *id, void *a_data);
void EditCB(MOBJECT obj, void *id, void *a_data);
void MLEditCB(MOBJECT obj, void *id, void *a_data);
void DrawAreaCB(MOBJECT obj, void *id, void *a_data);
void StatusBarCB(MOBJECT obj, void *id, void *a_data);
void TableCB(MOBJECT obj, void *id, void *a_data);

void InfoCB(MOBJECT o, void *id, void *a_data);
void ChangeTitleCB(MOBJECT o, void *id, void *a_data);

/*
 *      Main Object
 */
MOBJECT mainshell;
/*
 *      Color Identifiers
 */
MTColor black, white, darkblue, cadetblue, seagreen;
MTColor vmbg, green, red, orange;
MTColor darkvmbg, darkorange, darkred, darkgreen;

int PaletteMode;                /* True if the system allows color animation */
long NColors;

TIMEOUT_ID to_id;

#define N_VM_LED        18
#define N_GREEN_LED     13
#define VM_UPDATE_TIME 100L

struct
{
    int daw, dah;
    int ledw, ledh;
    int hoff, voff;
}
vmvars;

struct
{
    int l_status, r_status;
    MTColor offcolor, oncolor;
}
vmleds[N_VM_LED];

/**************************************************
 * This function initializes the V-Meter led parameters
 **************************************************/
void 
InitLeds(void)
{
    int i;

    for (i = 0; i < N_GREEN_LED; i++)
    {
        vmleds[i].l_status = 0;
        vmleds[i].r_status = 0;
        vmleds[i].offcolor = darkgreen;
        vmleds[i].oncolor = green;
    }

    vmleds[i].l_status = 0;
    vmleds[i].r_status = 0;
    vmleds[i].offcolor = darkorange;
    vmleds[i].oncolor = orange;

    for (i++; i < N_VM_LED; i++)
    {
        vmleds[i].l_status = 0;
        vmleds[i].r_status = 0;
        vmleds[i].offcolor = darkred;
        vmleds[i].oncolor = red;
    }
}

/**************************************************
 * This Timeout callback turns on/off the V-Meter
 * leds drawn in a drawarea Object
 **************************************************/
void 
VMeterTimerCB(TIMEOUT_ID id, void *data)
{
    MOBJECT vmdrawa = (MOBJECT) data;
    int i, maxl, maxr, x, y, w, h;

    to_id = MAddTimeout(VM_UPDATE_TIME, VMeterTimerCB, data);

    maxl = (int) ((float) rand() / RAND_MAX * N_VM_LED);
    maxr = maxl + (int) ((float) rand() / RAND_MAX * 5 - 2);

    w = vmvars.ledw;
    h = vmvars.ledh;
    for (i = 0; i < N_VM_LED; i++)
    {
        x = (i + 1) * vmvars.hoff + i * w;
        y = vmvars.voff;
        if (i <= maxl && !vmleds[i].l_status)
            MDrawFilledBox(vmdrawa, x, y, w, h, vmleds[i].oncolor);
        else if (i > maxl && vmleds[i].l_status)
            MDrawFilledBox(vmdrawa, x, y, w, h, vmleds[i].offcolor);
        vmleds[i].l_status = (i <= maxl);
        y += vmvars.voff + h;
        if (i <= maxr && !vmleds[i].r_status)
            MDrawFilledBox(vmdrawa, x, y, w, h, vmleds[i].oncolor);
        else if (i > maxr && vmleds[i].r_status)
            MDrawFilledBox(vmdrawa, x, y, w, h, vmleds[i].offcolor);
        vmleds[i].r_status = (i <= maxr);
    }
}

/**************************************************
 * This callback starts the V-Meter animation
 **************************************************/
void 
StartVMeterCB(MOBJECT obj, void *item, void *data)
{
    MRefreshTimeout(&to_id, VM_UPDATE_TIME, VMeterTimerCB, data);
}

/**************************************************
 * This callback stops the V-Meter animation
 **************************************************/
void 
StopVMeterCB(MOBJECT obj, void *item, void *data)
{
    MClearTimeout(&to_id);
}

/**************************************************
 * This callback draws the drawarea Object which
 * simulates a V-Meter when a portion of its area
 * becomes visible.
 **************************************************/
void 
VMDrawAreaExposeCB(MOBJECT o, MEvent * pexp, void *a_data)
{
    int i, x, y, w, h;

    MDrawFilledBox(o, 0, 0, MObjectGetWidth(o), MObjectGetHeight(o), vmbg);
    w = vmvars.ledw;
    h = vmvars.ledh;
    for (i = 0; i < N_VM_LED; i++)
    {
        x = (i + 1) * vmvars.hoff + i * w;
        y = vmvars.voff;
        MDrawBox(o, x - 1, y - 1, w + 2, h + 2, darkvmbg);
        MDrawFilledBox(o, x, y, w, h,
             (vmleds[i].l_status ? vmleds[i].oncolor : vmleds[i].offcolor));
        y += vmvars.voff + h;
        MDrawBox(o, x - 1, y - 1, w + 2, h + 2, darkvmbg);
        MDrawFilledBox(o, x, y, w, h,
             (vmleds[i].r_status ? vmleds[i].oncolor : vmleds[i].offcolor));
    }
}


/**************************************************
 *  Callback that recomputes the V-Meter dimensions
 *  as the drawarea Object is resized.
 **************************************************/
void 
VMDrawAreaResizeCB(MOBJECT obj, DRAWAREA_PREF * pref, void *a_data)
{
    vmvars.ledw = (pref->new_w - (N_VM_LED + 1) * vmvars.hoff) / N_VM_LED;
    vmvars.ledh = (pref->new_h - 3 * vmvars.voff) / 2;
}

/**************************************************
 *      Callback that destroys the shell containing
 *      the Object 'p' (the calling Object) and all
 *      its descendants.
 **************************************************/
void 
CloseDialogCB(MOBJECT p, void *s, void *v)
{
    MShellDestroy(p);
}

/**************************************************
 *      Callback called when an accelarator key
 *      associated to a FORM is pressed
 **************************************************/
void 
FormKeyCB(MOBJECT obj, MVALUE key, void *udata)
{
    char str[128];

    sprintf(str, "Form Key Callback\nkey code: %lx\nuser data: %ld", key, (long) udata);
    MMessageDialog("", str, "OK", NULL);
}

/**************************************************
 *      This callback shows the check status of
 *      a menu item
 **************************************************/
void 
ItemCheckCB(MENU_ITEM item, MVALUE status, void *udata)
{
    char str[128];

    sprintf(str, "The selected menu item\nhas been %s",
            (status ? "Activated" : "Deactivated"));
    MMessageDialog("", str, "Ok", NULL);
}

/**************************************************
 *      This callback changes a menu item text
 **************************************************/
void 
ChangeItemTextCB(MOBJECT obj, void *item, void *udata)
{
    char text[32];

    strcpy(text, MMenuItemGetText(item));
    if (MInputPrompt("Item Text", "Type in the new text", text, 31, "Exec", "Cancel"))
        MMenuItemSetText(item, text);
}

/**************************************************
 *      This callback changes a menu title text
 **************************************************/
void 
ChangeTitleTextCB(MOBJECT obj, void *item, void *udata)
{
    char text[32];
    const char *oldtext = MMenuGetTitleText(obj);

    strcpy(text, (oldtext != NULL ? oldtext : "NULL"));
    if (MInputPrompt("Title Text", "Type in the new text", text, 31, "Exec", "Cancel"))
        MMenuSetTitleText(obj, text);
}

/**************************************************
 *      This callback is called when the user
 *      selects destroy a pulldown
 **************************************************/
void 
DestroyMenuCB(MOBJECT obj, void *item, void *udata)
{
    if (udata != NULL)
        MDestroyMenu((MOBJECT) udata);
    else
        MDestroyMenu(obj);
}

/**************************************************
 *      This callback is called when the user
 *      selects a menu item
 **************************************************/
void 
RadioItemSelectCB(MENU_ITEM item, MVALUE status, void *udata)
{
    char str[128];

    if (!status)
        return;
    sprintf(str, "Selected the menu item <%ld>", (long) item);
    MMessageDialog("", str, "Ok", NULL);
}

/**************************************************
 *  This callback is called when the user
 *      selects the menu item that makes itself unsensitive
 **************************************************/
void 
MakeItemUnsensitiveCB(MOBJECT obj, void *item, void *udata)
{
    MMenuItemSetUnsensitive(item);
}

/**************************************************
 *      This callback handles the Window Manager
 *      request to close the main shell (the program)
 **************************************************/
void 
WMCloseCB(MOBJECT obj, void *o_data, void *a_data)
{
    if (MMessageDialog("Closing",
                       "Do you want to Quit ?", "Yes", "No", NULL) == 0)
    {
        MGUITerm();
        exit(0);
    }
}

/**************************************************
 *  This callback opens a popup menu when the right
 *  mouse button is pressed within the Object which
 *  this callback is set to.
 *  X and Y positions for this popup are those in the
 *  mouse event.
 **************************************************/
void 
PopupMenuCB(MOBJECT obj, MEvent * pe, void *pdown)
{
    if (pe->mouse.button == Button3)
        MPopupMenu((MOBJECT) pdown, pe->mouse.x_root, pe->mouse.y_root);
}

/************************************************
 *      Program termination callback.
 ************************************************/
void 
ExitCB(MENU_ITEM item, MVALUE status, void *a_data)
{
    MShellUnrealize(mainshell);
    MShellDestroy(mainshell);

    MFreeColor(seagreen);
    MFreeColor(cadetblue);
    MFreeColor(darkblue);

    MGUITerm();
    exit(0);
}

/**************************************************
 *      Access point to the MGUI application
 **************************************************/

void 
MGUIMain(int argc, char **argv)
{
    MOBJECT menu, pdown, popup_pd, submenu, sfile, label, form, drawa;
    MENU_ITEM item_id, item2_id;
/*
 *  Log parameter initialization:
 *
 *      log level:              3
 *      n. of stored lines:     10
 *      n. of visble lines:     5
 *      row len:                80
 *      visible row len:        60
 *      log file:               "mguitest.log" (NULL if not needed)
 *
 * To show/hide log window press        <Ctrl + Shift + F1>
 * To change run time log level press   <Ctrl + Shift + F2>
 */
    MLogInit(3, 10, 5, 80, 60, "mguitest.log");
    MEnableCustomizing();
//    MEnableCharBrowser();

    MSetFileSelectionTextStrings("Archivio", "Cartelle", "Dischi", "Filtro", "File", "Cartella corrente", "&Apri", NULL);

    MLog(1, "This is a %s at line: %d", "test", __LINE__);

    PaletteMode = MPaletteMode();
    NColors = MGetNColors();
/*
 *  Colors allocation
 */
    black = MBlackColor();
    white = MWhiteColor();
    cadetblue = MAllocColor(0x60, 0x85, 0x95);
    seagreen = MAllocColor(0x70, 0xa5, 0xa0);
    darkblue = (NColors > 16 ? MAllocShiftColor(cadetblue, 65) : cadetblue);
    vmbg = darkblue;
    darkvmbg = MAllocShiftColor(vmbg, 90);
    orange = MAllocColor(0xe0, 0xa0, 0x40);
    red = MAllocColor(0xf0, 0x00, 0x40);
    green = MAllocColor(0x40, 0xe0, 0x40);
    darkorange = MAllocColor(0x65, 0x58, 0x48);
    darkred = MAllocColor(0x60, 0x48, 0x50);
    darkgreen = MAllocColor(0x48, 0x60, 0x50);

    MSetMessageDialogRGB(120, 140, 130);
    MSetInputPromptRGB(150, 120, 120);

    mainshell = MCreateShell("MGUI Library Test", 0);

    MShellSetWMCloseCallback(mainshell, WMCloseCB, NULL);
    menu = MCreateMenuBar(mainshell, HELV_MEDIUM);
    MMenuBarSetAcc(menu, F10);
	MObjectSetBackgroundImageFile(menu, "tile1.bmp", BI_TILED);
    pdown = MCreateMenu(menu, "File");
	MMenuAddItemObsolete(pdown, "Font Browser", FontViewCB, 0L);
	MMenuAddItemObsolete(pdown, "Color Browser", ColPickCB, 0L);
    MMenuAddItemObsolete(pdown, "Shell title...", ChangeTitleCB, 0L);
    MMenuAddSeparator(pdown);
    item_id = MMenuAddItem(pdown, "Quit", ExitCB, 0L);
    MMenuItemSetAccel(item_id, C_Q, "Ctrl+Q");

    pdown = MCreateMenu(menu, "Objects");
    popup_pd = pdown;

    item_id = MMenuAddItemObsolete(pdown, "Form", NULL, 0L);
    submenu = MCreateSubMenu(item_id);
    MMenuAddItemObsolete(submenu, "Table", TableCB, 0L);
    MMenuAddItemObsolete(submenu, "Scrolled Form", ScrolledFormCB, 0L);
    MMenuAddItemObsolete(submenu, "Paged Form", PagedFormCB, 0L);
    MMenuAddItemObsolete(submenu, "Clipped Form", ClippedFormCB, 0L);
    MMenuAddItemObsolete(submenu, "Object Manage", ObjectManageCB, 0L);

    MMenuAddSeparator(pdown);
    MMenuAddItemObsolete(pdown, "Radio && Toggle", TRButtonCB, 0L);
    MMenuAddSeparator(pdown);
    MMenuAddItemObsolete(pdown, "Edit", EditCB, 0L);
    MMenuAddItemObsolete(pdown, "Multi Line &Edit", MLEditCB, 0L);
    MMenuAddSeparator(pdown);
    MMenuAddItemObsolete(pdown, "DrawArea", DrawAreaCB, 0L);
    MMenuAddItemObsolete(pdown, "Status Bar", StatusBarCB, 0L);

    item_id = MMenuAddItemObsolete(pdown, "List", NULL, 0L);
    submenu = MCreateSubMenu(item_id);
    MMenuAddItemObsolete(submenu, "Scrolled List", SListCB, 0L);
    MMenuAddItemObsolete(submenu, "Block List", BlockListCB, 0L);
    MMenuAddItemObsolete(submenu, "Edit List", EditListCB, 0L);
    MMenuAddItemObsolete(submenu, "Skip List", SkipListCB, 0L);
    MMenuAddItemObsolete(submenu, "Multi Sel List", MSelListCB, 0L);
    MMenuAddItemObsolete(submenu, "Scrolled Text", STextCB, 0L);
    MMenuAddItemObsolete(submenu, "Scrolled File", SFileCB, 0L);

    pdown = MCreateMenu(menu, "Pulldown");
    MMenuAddToggleItem(pdown, "Item 1", ItemCheckCB, 0L);
    item_id = MMenuAddItemObsolete(pdown, "Subpulldown", NULL, 0L);
    MMenuAddItemObsolete(pdown, "Change me", ChangeItemTextCB, 0L);
    MMenuAddItemObsolete(pdown, "Change title", ChangeTitleTextCB, 0L);
    item2_id = MMenuAddItemObsolete(pdown, "Unsensitive", MakeItemUnsensitiveCB, 0L);
    MMenuItemSetAccel(item2_id, C_U, "Ctrl+U");

    submenu = MCreateSubMenu(item_id);
    MMenuAddToggleItem(submenu, "subItem 2-1", ItemCheckCB, 0L);
    MMenuAddToggleItem(submenu, "subItem 2-2", ItemCheckCB, 0L);
    item_id = MMenuAddItemObsolete(submenu, "Subpulldown", NULL, 0L);

    submenu = MCreateSubMenu(item_id);
    item_id = MMenuAddRadioItem(submenu, "option 1-1", RadioItemSelectCB, 0L);
    MMenuItemSetCheckStatus(item_id, True);
    MMenuAddRadioItem(submenu, "option 1-2", RadioItemSelectCB, 0L);
    MMenuAddRadioItem(submenu, "option 1-3", RadioItemSelectCB, 0L);
    MMenuAddSeparator(submenu);
    MMenuAddRadioItem(submenu, "option 2-1", RadioItemSelectCB, 0L);
    item_id = MMenuAddRadioItem(submenu, "option 2-2", RadioItemSelectCB, 0L);
    MMenuItemSetCheckStatus(item_id, True);
    MMenuAddItemObsolete(submenu, "Change me", ChangeItemTextCB, 0L);
    MMenuAddItemObsolete(submenu, "Change title", ChangeTitleTextCB, 0L);
    MMenuAddItemObsolete(submenu, "Destroy SubMenu", DestroyMenuCB, 0L);
    MMenuAddItemObsolete(submenu, "Destroy Menu", DestroyMenuCB, pdown);

    MObjectEnableOptions(menu, "menu", MO_ALL);

    MFormAddKeyCallback(mainshell, F1, FormKeyCB, 0L);
    MFormAddKeyCallback(mainshell, C_F1, FormKeyCB, (void *) 1);
    MFormAddKeyCallback(mainshell, C_A, FormKeyCB, (void *) 2);

    label = MCreateLabel(mainshell, "Ctrl+Shift+MouseClick here to customize", FIXED_MEDIUM);
	MObjectEnableOptions(label, "label_1", MO_ALL);

    form = MCreateColForm(mainshell);
    MObjectSetShadow(form, WS_ETCHED_IN, 2, 1);
    MObjectSetColor(form, vmbg, black);

    vmvars.hoff = 4;
    vmvars.voff = 10;
    vmvars.ledw = 14;
    vmvars.ledh = 6;
    vmvars.daw = N_VM_LED * vmvars.ledw + (N_VM_LED + 1) * vmvars.hoff;
    vmvars.dah = 2 * vmvars.ledh + 3 * vmvars.voff;
    InitLeds();

    drawa = MCreateDrawArea(form, vmvars.daw, vmvars.dah);
    MObjectSetResize(drawa, True, False);
    MDrawAreaSetExposeCallback(drawa, VMDrawAreaExposeCB, NULL);
    MDrawAreaSetResizeCallback(drawa, VMDrawAreaResizeCB, NULL);
    MObjectSetPopupHelp(drawa, "Click the right mouse\nbutton to start/stop", 500, 5000);

    pdown = MCreatePopupMenu(HELV_SMALL);
    MObjectSetColor(pdown, seagreen, white);
    MMenuAddItemObsolete(pdown, "Start", StartVMeterCB, drawa);
    MMenuAddItemObsolete(pdown, "Stop", StopVMeterCB, drawa);
    MObjectEnableOptions(pdown, "pdown", MO_TEXT);
    MObjectAddEventNotifyCallback(drawa, PopupMenuCB, EM_BUTTON_PRESS, pdown);

    label = MCreateLabel(form, "Click here the right mouse\n\
button to open a popup menu", FIXED_MEDIUM);
    MLabelSetAlignment(label, RIGHT_ALIGN);
    MObjectSetColor(label, darkblue, white);
    MObjectSetResize(label, False, False);
    MObjectSetShadow(label, WS_SHADOW_IN, 1, 0);
    MObjectAddEventNotifyCallback(label, PopupMenuCB, EM_BUTTON_PRESS, popup_pd);

    sfile = MCreateSFile(mainshell, __FILE__, FIXED_MEDIUM, 16, 74);
    MObjectSetColor(sfile, darkblue, white);

    MShellRealize(mainshell);

/*
 * Now the program passes control to MGUI library
 * and gains control only through callbacks
 */
    MMainLoop();
}

/***********************************************
 *      This callback changes the mainshell title
 *      using the utility function MInputPrompt()
 ************************************************/
void
ChangeTitleCB(MOBJECT o, void *item, void *a_data)
{
    char title[128];

    MObjectGetText(mainshell, title);
    if (MInputPrompt("INPUT", "New title:", title, 40, "Exec", "Cancel"))
        MShellSetTitle(mainshell, title);
}
