// SOLCM.C

// This file includes methods for:

// SOLCM (subclass of COMMAN)

#include <solipeg.g>
#include <solipeg.rsg>
#include <hwim.h>
#include <solglob.h>


// Launch a specific dialog

// class - the class of dialog
// resid - the resource id from the application resource file
// rbuf - additional data which needs to be passed into the dialog
LOCAL_C INT LaunchDialog(INT class, INT resid, VOID *rbuf) {
    DL_DATA     dld ;

    dld.id = resid ;
    dld.rbuf = rbuf ;
    dld.pdlg = NULL ;
    return(hLaunchDial(CAT_SOLIPEG_SOLIPEG, class, &dld)) ;
}


#pragma save, METHOD_CALL


/************************************/
/* Command Manager Subclass Methods */
/************************************/


#undef this
#define this    (self->solcm)


// Solipeg Command Manager Initialization
METHOD VOID solcm_com_init(PR_SOLCM *self) {
    TEXT *      strList[2] ;

    hLoadResBuf(RS_MAXLIBVER, &this.wMaxLibVer) ;
    hLoadResource(RS_LIBID, &this.strLibID) ;
    hLoadResource(RS_JUMP_STR, &this.strJump) ;
    hLoadResource(RS_MOVE_STR, &this.strMove) ;
    hLoadResource(RS_PLAY, &strList[0]) ;
    hLoadResource(RS_EDIT, &strList[1]) ;
    wsSetList(2, strList, this.editing = 0) ;
    p_free(strList[1]) ;
    p_free(strList[0]) ;
    this.waveao = f_newsend(    CAT_SOLIPEG_SOLIPEG,
                                C_WAVEAO,
                                O_AO_INIT ) ;
}

// Handle the exit - saving the board position to env
METHOD VOID solcm_com_exit(PR_SOLCM *self) {
    // Store the options etc. in an environment variable
    p_setenviron(   DatApp1,
                    p_slen(DatApp1),
                    &options,
                    sizeof(options) ) ;
    p_exit(0) ;
}

// Handle the mode change due to diamond key
METHOD VOID solcm_com_mode_change(PR_SOLCM *self) {
    this.editing = !this.editing ;
    wsSelectList(this.editing) ;
}

// Handle menu replacement for redo/undo
METHOD VOID solcm_com_menu( PR_SOLCM *  self,
                            INT         menu_num,
                            PR_VAROOT * array ) {
    if ( menu_num == 1 ) {
        TEXT *  p ;

        if ( !p_send2(this.undoman, O_UMAN_CANREDO) ) {
            p_send3(array, O_VA_DELETE, 1) ;
        }
        else {
            p = ((TEXT *) p_send3(array, O_VA_PREC, 1)) + 2 ;
            p += hLoadResBuf(RS_REDO, p) - 1 ;
            *p = ' ' ;
            p_send3(this.undoman, O_UMAN_REDOTXT, ++p) ;
        }

        if ( !p_send2(this.undoman, O_UMAN_CANUNDO) ) {
            p_send3(array, O_VA_DELETE, 0) ;
        }
        else {
            p = ((TEXT *) p_send3(array, O_VA_PREC, 0)) + 2 ;
            p += hLoadResBuf(RS_UNDO, p) - 1 ;
            *p = ' ' ;
            p_send3(this.undoman, O_UMAN_UNDOTXT, ++p) ;
        }
    }
}

// New game command
METHOD VOID solcm_scm_new(PR_SOLCM *self) {
    // Confirm New Game request
    if ( hConfirm(RS_NEW_GAME) ) {
        p_send2(self, O_SCM_DO_NEW) ;
    }
}

// Open game command
METHOD VOID solcm_scm_open(PR_SOLCM *self, INT comid) {
    SOLRBUF     rb ; // structure to be passed into file dialogs
    INT         iResID ;

    hLoadResBuf(    comid == O_SCM_EXTERNAL ? RS_DEF_EXTERNAL : RS_DEFNAME,
                    rb.strFilename ) ;

    // Choose which dialog
    switch ( comid ) {
        case O_SCM_OPEN :
            iResID = OPEN_DIALOG ;
            break ;

        case O_SCM_SAVE :
            iResID = SAVE_DIALOG ;
            break ;

        case O_SCM_EXTERNAL :
            iResID = OPEN_EXTERNAL_DIALOG ;
            break ;

        default :
            return ;
    }

    // Launch the dialog
    if ( !LaunchDialog(C_SOLDLG_OPEN, iResID, &rb) ) {
        // dialog was cancelled
        return ;
    }

    // If an external library is accepted
    if ( comid == O_SCM_EXTERNAL ) {
        PR_SOLEL *      pEL ;

        // Create a SOLEL object
        pEL = p_new(CAT_SOLIPEG_SOLIPEG, C_SOLEL) ;

        // Initialize it - this loads the list
        if ( p_send5(   pEL,
                        O_EL_INIT,
                        rb.strFilename,
                        this.strLibID,
                        this.wMaxLibVer ) ) {
            // Problem
            p_send5(w_am, O_AM_NOTIFY, RS_BADFILE, NULL, NULL) ;
        }
        else {
            // Get a choice of problem out of the library
            LaunchDialog(C_SOLDLG_EXT_LIB, EXTERNAL_LIBRARY_DIALOG, pEL) ;
        }

        // No more need for the SOLEL object
        hDestroy(pEL) ;
    }
    else {
        // Open or save a single game
        p_send3(    soleng,
                    comid == O_SCM_OPEN ? O_ENG_OPEN_GAME : O_ENG_SAVE_GAME,
                    rb.strFilename) ;
        p_send2(self, O_SCM_UPDATE) ;
    }
}

// Use library command
METHOD VOID solcm_scm_library(PR_SOLCM *self) {
    // Internal library - simple application resource load
    if ( LaunchDialog(C_SOLDLG_LIBRARY, LIBRARY_DIALOG, NULL) ) {
        // Start a new game
        p_send2(self, O_SCM_DO_NEW) ;
    }
}

// Launch the Options dialog and reset board as necessary
METHOD VOID solcm_scm_options(PR_SOLCM *self) {
    UWORD   uwOldShape ;
    UWORD   uwOldRules ;
    UWORD   uwOldShow ;

    // Get current settings
    uwOldShape = options.shape ;
    uwOldRules = options.rules ;
    uwOldShow = options.show ;

    LaunchDialog(C_SOLDLG_OPTIONS, OPTIONS_DIALOG, NULL) ;

    // Certain option changes require more work then others
    if ( options.shape != uwOldShape ) {
        // Only shape changed
        p_send2(soleng, O_ENG_INIT) ;
        p_send2(self, O_SCM_DO_NEW) ;
    }
    else if (   options.rules != uwOldRules ||
                options.show != uwOldShow ) {
        // Rules changed - need to recalculate and redraw movable pieces
        // Board needs to be redrawn
        p_send2(self, O_SCM_UPDATE) ;
    }

    // Reposition window
    p_send2(w_ws->wserv.cli, O_WN_REPOS) ;
}

// Menu Undo
METHOD VOID solcm_scm_undo(PR_SOLCM *self) {
    p_send2(this.undoman, O_UMAN_UNDO) ;
}

// Menu Redo
METHOD VOID solcm_scm_redo(PR_SOLCM *self) {
    p_send2(this.undoman, O_UMAN_REDO) ;
}

// Use statistics command
METHOD VOID solcm_scm_statistics(PR_SOLCM *self) {
    LaunchDialog(C_SOLDLG_STATS, STATS_DIALOG, NULL) ;
}

METHOD VOID solcm_scm_engine(   PR_SOLCM *  self,
                                VOID *      pEngine ) {
    this.undoman = f_newsend(   CAT_SOLIPEG_SOLIPEG,
                                C_UNDOMAN,
                                O_UMAN_INIT,
                                pEngine,
                                CAT_SOLIPEG_OLIB,
                                C_VAFLAT ) ;
}

// Get the current cursor position
METHOD UWORD solcm_scm_gpos(PR_SOLCM *self) {
    return(XY(this.cx, this.cy)) ;
}

// Set the current cursor position
METHOD VOID solcm_scm_spos(PR_SOLCM *self, UWORD xy) {
    this.cx = X(xy) ;
    this.cy = Y(xy) ;
}

// Beep (if sound enabled)
METHOD VOID solcm_scm_beep(PR_SOLCM *self, INT iResID) {
    if ( options.sound ) {
        TEXT        strName[P_FNAMESIZE] ;

        // Load the name of the sound file
        hLoadResBuf(iResID, strName) ;

        // Play the sound
        p_send2(this.waveao, O_AO_CANCEL) ;
        p_send4(this.waveao, O_WV_PLAY, strName, 0) ;
    }
}

// Update the board
METHOD INT solcm_scm_update(PR_SOLCM *self) {
    INT     iRet ;

    iRet = p_send2(soleng, O_ENG_UPDATE_BOARD) ;

    if ( !p_send3(soleng, O_ENG_USABLE, XY(this.cx, this.cy)) ) {
        p_send(self, O_SCM_SPOS, XY(3, 5)) ;
        p_send2(w_ws->wserv.cli, O_WN_CURSOR) ;
    }

    gCreateTempGC0(((PR_SOLBWIN *) w_ws->wserv.cli)->solbwin.swn->win.id) ;
//    p_send2(((PR_SOLBWIN *) w_ws->wserv.cli)->solbwin.swn, O_WN_DRAW) ;
    p_send3(soleng, O_ENG_DRAW_BOARD, ((PR_SOLBWIN *) w_ws->wserv.cli)->solbwin.swn) ;
    gFreeTempGC() ;

    return(iRet) ;
}

// New game
METHOD VOID solcm_scm_do_new(PR_SOLCM *self) {
    p_send2(this.undoman, O_UMAN_CLEAR) ;
    p_send2(soleng, O_ENG_NEW_GAME) ;
}

// Handle keypresses
METHOD VOID solcm_scm_key(PR_SOLCM *self, INT keycode, INT mods) {
    INT     iDir = DIR_NONE ;
    INT     fJumpMove = FALSE ;

    if (    keycode == W_KEY_DELETE_LEFT &&
            mods == (W_SHIFT_MODIFIER | W_CTRL_MODIFIER) ) {
        // Special un-install keypress - frees environment space
        p_delenv(DatApp1) ;
        p_exit(0) ;
    }

    switch ( keycode ) {

        case ' ' :
            // Toggle - if editing
            if ( this.editing ) {
                p_send3(soleng, O_ENG_TOGGLE, XY(this.cx, this.cy)) ;
            }
            else {
                p_send3(self, O_SCM_BEEP, RS_WARN_SOUND) ;
            }
            return ;

        case W_KEY_UP :
            iDir = DIR_N ;
            break ;

        case W_KEY_DOWN :
            iDir = DIR_S ;
            break ;

        case W_KEY_RIGHT :
            iDir = DIR_E ;
            break ;

        case W_KEY_LEFT :
            iDir = DIR_W ;
            break ;

        case W_KEY_DELETE_LEFT :
        case W_KEY_ESCAPE :
            p_send2(    this.undoman,
                        mods == W_SHIFT_MODIFIER ? O_UMAN_REDO : O_UMAN_UNDO ) ;
            return ;

        case W_KEY_DELETE_RIGHT :
            p_send2(this.undoman, O_UMAN_REDO) ;
            return ;

        default :
            iDir = p_sloc(this.strJump, keycode) ;

            if ( iDir != DIR_NONE ) {
                // Jump Move
                fJumpMove = options.rules || !(iDir % 2) ;
                // Mask out diagonals if not allowed
                if ( !fJumpMove ) {
                    p_send3(self, O_SCM_BEEP, RS_WARN_SOUND) ;
                    return ;
                }
            }
            else {
                // Move Move?
                iDir = p_sloc(this.strMove, keycode) ;

                if ( iDir == DIR_NONE ) {
                    p_send3(self, O_SCM_BEEP, RS_WARN_SOUND) ;
                    return ;
                }
            }
            break ;
    }

    fJumpMove = fJumpMove || mods == W_SHIFT_MODIFIER ;

    if ( this.editing || !fJumpMove ) {
        // Valid cursor move
        UWORD       newXY ;

        newXY = XY(this.cx + vectors[iDir].x, this.cy + vectors[iDir].y) ;
        if ( p_send3(soleng, O_ENG_USABLE, newXY) ) {
            p_send3(self, O_SCM_SPOS, newXY) ;
            p_send2(w_ws->wserv.cli, O_WN_CURSOR) ;
        }
    }
    else if ( p_send5(soleng, O_ENG_JUMP_VALID, this.cx, this.cy, iDir) ) {
        PR_TASK *   pTask ;

        p_send3(self, O_SCM_BEEP, RS_JUMP_SOUND) ;

        pTask = f_newsend(  CAT_SOLIPEG_SOLIPEG,
                            C_JUMP,
                            O_TASK_INIT,
                            this.cx,
                            this.cy,
                            iDir ) ;
        p_send3(this.undoman, O_UMAN_SUBMIT, pTask) ;

        if (    !this.editing &&
                p_send2(soleng, O_ENG_MOVES) ) {
            // No move remaining - check for win
            p_send3(    self,
                        O_SCM_BEEP,
                        p_send2(soleng, O_ENG_ENDGAME)
                            ? RS_WIN_SOUND
                            : RS_FINIS_SOUND ) ;

            // Want a new game?
            p_send2(self, hConfirm(RS_NEW_GAME) ? O_SCM_DO_NEW : O_COM_EXIT) ;
        }
    }
    else {
        // Invalid move
        p_send3(self, O_SCM_BEEP, RS_WARN_SOUND) ;
    }
}

