/* Program   Solitaire
 * Version   A 1.00
 * Author    Deri James
 * RISC User November 1992
 * Program   Subject to Copyright
 *           Not Public Domain
 */

#include "wimp.h"
#include "wimpt.h"
#include "win.h"
#include "event.h"
#include "baricon.h"
#include "res.h"
#include "resspr.h"
#include "menu.h"
#include "template.h"
#include "dbox.h"
#include "werr.h"
/* #include "coords.h" */
#include "bbc.h"

#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>

/********************************* CONSTANTS ********************************/

/* Menu items */
#define solitaire_menu_info     1
#define solitaire_menu_quit     2

#define MAX(a,b) ((a>b)?(a):(b))
#define MIN(a,b) ((a<b)?(a):(b))


/* Info box field for the version string */
#define solitaire_info_field    4

/******************************** GLOBAL DATA *******************************/

/* Application version */
static char *solitaire_Version_String = "1.00 (20 Jan 1992)";

/* The top of the menu tree */
static menu solitaire_menu;

/* Icon being dragged  */
static int dragx=-1,dragy=-1;

/* Handle for the solitaire window */
static wimp_w solitaire_win_handle;

/* Flag - is the window open */
static BOOL solitaire_window_open = FALSE;

/* Table holding the solitaire pegs, values:-

                        -1  = no hole
                        0   = no peg
                        1   = peg                   */
                        
static int brd[11][11]={ {0} };

static int fini=FALSE;


/****************************************************************************
 * Debug Routines:-                                                         *
 *                                                                          *
 *      debug()   - places message into Beebug's !DEBUG utility             *
 *      dprintf() - functions like printf() using Beebug's !DEBUG           *
 *                                                                          *
 ****************************************************************************/
 
void debug(int no, char *mess)
{
  char string[20]="db$mess";
  char null[20]="XX";
  int len,R0,R1;
  R0=(int) string;
  R1=(int) null;
  os_swi6(0x24,R0,R1,0,0,0,0);
  len=strlen(string);
  string[len]=no+48;
  string[len+1]='\0';
  R0=(int) string;
  R1=(int) mess;
  os_swi6(0x24,R0,R1,0,0,0,0);
}

int dprintf(int no, char *fmat, ...)
{
    char dres[50];
    int ct;
    va_list arg_ptr;

    va_start(arg_ptr,fmat);
    ct=vsprintf(dres,fmat,arg_ptr);
    va_end(arg_ptr);
    debug(no,dres);
    return ct;
}

/***************************** WINDOW FUNCTIONS *****************************/

/*  Create the window, yielding its handle. Return TRUE if ok.  */

static BOOL solitaire_create_window(char *name, wimp_w *handle)
{
    wimp_wind *window;    /* Pointer to window definition */

    /* Find template for the window */
    
    window = template_syshandle(name);
    
    if (window == 0)
        return FALSE;

    /* Create the window, dealing with errors */

    return (wimpt_complain(wimp_create_wind(window, handle)) == 0);
}

/*  Individual event routines for the window   */

static void solitaire_redraw_window(wimp_w handle)
{
    /*
        Method:
        
            Template file has all icons defined although the ones requiring
            'help' are defined below the bottom of the window (Y axis -1000)
            and so are not plotted automatically by the Wimp.
            
            This means they must be plotted manually by adding 1000 to
            their stored Y work area offsets.
    */
     
    int more,x,y,ystart,yend;
    wimp_redrawstr r;
    wimp_icon i;
  
    /* Start the redraw */
    r.w = handle;
    wimpt_noerr(wimp_redraw_wind(&r, &more));
  
    /* Do the redraw loop */
    while (more)
    {
        /*  Icons 0-2 are the background slabs in the window  */
        
        ystart=((r.g.y1-r.box.y1+10)/50)*-1+1;
        yend=ystart+(r.g.y1-r.g.y0)/50+2;

        for (x=0; x<3; x++)
        {
            wimp_get_icon_info(handle,(wimp_i)x,&i);
            i.box.y0+=1000;  /*  Move them to the visible area of the  */
            i.box.y1+=1000;  /*  window.                               */
            wimp_ploticon(&i);
        }

        wimp_get_icon_info(handle,(wimp_i)3,&i);
        i.box.y0+=1000;
        i.box.y1+=1000;
        
        for (y=MAX(ystart,1); y<MIN(yend,10); y++)
        {
            i.box.y1=-((y-1)*50+10);
            i.box.y0=i.box.y1-50;

            for (x=1; x<10; x++)
            {
                switch (brd[y][x])
                {
                    case 0:
                        strcpy(i.data.sprite_name,"nopeg");
                        wimp_ploticon(&i);
                        break;
                        
                    case 1:
                        strcpy(i.data.sprite_name,"peg");
                        wimp_ploticon(&i);
                        break;
                }
                i.box.x0+=50;
                i.box.x1+=50;
            }
            i.box.x0-=450;
            i.box.x1-=450;
        }
        
        wimp_get_rectangle(&r, &more);  
    }
        
}

static void resetgame(void)
{
    /*  Called to reset board  */
    
    int i,j;
  
    for (i=0; i<11; i++)
    {
        for (j=0; j<11; j++)
        {
            if ((i<4 || i>6) && (j<4 || j>6))
                brd[i][j]=-1;
            else
                brd[i][j]=1;
        }
    }
    
    brd[5][5]=0;
    fini=FALSE;
    wimp_set_icon_state(solitaire_win_handle,4,0,wimp_IBORDER | wimp_ISPRITE);
  
}


static int checkend(void)
{
    int i,j,v=0;
    
    for (i=1; i<=9,v==0; i++)
    {
        for (j=1; j<=9,v==0; j++)
        {
            if (brd[i][j]==1)
            {
                if (j<9 && brd[i][j+2]==0 && brd[i][j+1]==1) v++; 
                if (j>1 && brd[i][j-2]==0 && brd[i][j-1]==1) v++; 
                if (i<9 && brd[i+2][j]==0 && brd[i+1][j]==1) v++; 
                if (i>1 && brd[i-2][j]==0 && brd[i-1][j]==1) v++;                 
            }
        }
    }
    
    if (!v)
    {
        wimp_set_icon_state(solitaire_win_handle,4,wimp_ISPRITE | wimp_IBORDER,wimp_ISPRITE | wimp_IBORDER);
        return TRUE;
    }
    
    return FALSE;
}


static void solitaire_open_window(wimp_openstr *o)
{
    /* Just pass the open request on to the wimp */

    wimpt_noerr(wimp_open_wind(o));
}


static void mark_redraw(int y, int x)
{
    wimp_redrawstr r;

    x-=1; y-=1;
    r.w=solitaire_win_handle;
    r.box.x0=x*50+24;
    r.box.x1=r.box.x0+50;
    r.box.y1=-10-(y*50);
    r.box.y0=r.box.y1-50;
    wimpt_complain(wimp_force_redraw(&r));
}


static void solitaire_button_pressed(wimp_eventdata *edata)
{
    
    wimp_i i=edata->but.m.i;    
    wimp_dragstr drag;
    wimp_icon icon;
    wimp_wstate wind;
    wimp_redrawstr r;
    int tox,toy,v;

    if (i==4 && fini)       /*  Icon 4 is the solitaire sprite at top right  */
    {                       /*  can only be 'hit' at end of game          */
        resetgame();
        if (wimpt_complain(wimp_get_wind_state(edata->but.m.w,&wind))==0)
        {
            r.w=-1;
            r.box=wind.o.box;
            wimpt_complain(wimp_force_redraw(&r));
        }
    }
    else
    {   
        if (wimpt_complain(wimp_get_wind_state(edata->but.m.w,&wind))==0)
        {
            dragx=(edata->but.m.x-24-wind.o.box.x0)/50+1;
            dragy=(wind.o.box.y1-edata->but.m.y-10)/50+1;
            dprintf(0,"x=%d, y=%d",dragx,dragy);
            if (!fini && dragx>0 && dragx<10 && dragy>0 && dragy<10
                && !((dragx<4 || dragx>6) && (dragy<4 || dragy>6)) 
                && brd[dragy][dragx]==1) 
            {
                v=0; tox=0; toy=0;
                if (dragx<9 && brd[dragy][dragx+2]==0 && brd[dragy][dragx+1]==1) {v++; tox=2;}
                if (dragx>1 && brd[dragy][dragx-2]==0 && brd[dragy][dragx-1]==1) {v++; tox=-2;}
                if (dragy<9 && brd[dragy+2][dragx]==0 && brd[dragy+1][dragx]==1) {v++; toy=2;}
                if (dragy>1 && brd[dragy-2][dragx]==0 && brd[dragy-1][dragx]==1) {v++; toy=-2;}
                
                if (v==1)
                {
                    brd[dragy][dragx]=0;
                    mark_redraw(dragy,dragx);
                    brd[dragy+toy][dragx+tox]=1;
                    mark_redraw(dragy+toy,dragx+tox);
                    toy=toy/2; tox=tox/2;
                    brd[dragy+toy][dragx+tox]=0;
                    mark_redraw(dragy+toy,dragx+tox);
                    fini=checkend();
                }
                else
                if (v>1)
                {
                    if (wimpt_complain(wimp_get_icon_info(edata->but.m.w,(wimp_i)3,&icon))==0)
                    {
                        drag.box.x0=24+(dragx-1)*50+wind.o.box.x0;
                        drag.box.x1=drag.box.x0+50;
                        drag.box.y1=-(10+(dragy-1)*50)+wind.o.box.y1;
                        drag.box.y0=drag.box.y1-50;
                        drag.window=edata->but.m.w;
                        drag.type=wimp_USER_FIXED;
                        drag.parent=wind.o.box;
                        wimpt_complain(wimp_drag_box(&drag));
                    }
                }
            }
        }
    }
}

void solitaire_userdrag(void)
{
    /*  End of dragging - insert coloured peg  */
    wimp_mousestr p;
    wimp_wstate w;
    int dragx2,dragy2,v,tox,toy;
    
    if (wimpt_complain(wimp_get_point_info(&p))==0)
    {
        wimpt_complain(wimp_get_wind_state(solitaire_win_handle,&w));
        dragx2=(p.x-24-w.o.box.x0)/50+1;
        dragy2=(w.o.box.y1-p.y-10)/50+1;
        v=0;tox=0;toy=0;

        if (dragx2<dragx && dragx>1 && brd[dragy][dragx-2]==0) {v++; tox=-2;}
        if (dragx2>dragx && dragx<9 && brd[dragy][dragx+2]==0) {v++; tox=2;}
        if (dragy2<dragy && dragy>1 && brd[dragy-2][dragx]==0) {v++; toy=-2;}
        if (dragy2>dragy && dragy<9 && brd[dragy+2][dragx]==0) {v++; toy=2;}

        if (v==1)
        {
            brd[dragy][dragx]=0;
            mark_redraw(dragy,dragx);
            brd[dragy+toy][dragx+tox]=1;
            mark_redraw(dragy+toy,dragx+tox);
            toy=toy/2; tox=tox/2;
            brd[dragy+toy][dragx+tox]=0;
            mark_redraw(dragy+toy,dragx+tox);
            fini=checkend();
        }
    }
}
            
/****************************** EVENT HANDLERS ******************************/

/*  Event handler called on a left click on the icon.  */

static void solitaire_iconclick(wimp_i icon)
{
    wimp_wstate  state;
    wimp_redrawstr r;

    icon = icon; /* We don't need the handle: this stops compiler warning */

    /* Get the state of the window */

    if (wimpt_complain(wimp_get_wind_state(solitaire_win_handle, &state)) == 0)
    {
        state.o.behind = -1;   /* Make sure window is opened in front */
        wimpt_noerr(wimp_open_wind(&state.o));
        if (fini)
        {
            r.w=-1;
            r.box=state.o.box;
            wimpt_complain(wimp_force_redraw(&r));
            resetgame();
        }
        solitaire_window_open = TRUE;     
    }
}


/*  Display the program info box - called from the menu processor.  */

static void solitaire_info_about_program(void)
{
    dbox  d;  /* Dialogue box handle */
  
    /* Create the dialogue box */

    if (d = dbox_new("ProgInfo"), d != NULL)
    {
        /* Fill in the version number */
        dbox_setfield(d, solitaire_info_field, solitaire_Version_String);
    
        /* Show the dialogue box */
        dbox_show(d);
    
        /* Keep it on the screen as long as needed */
        dbox_fillin(d);
    
        /* Dispose of the dialogue box */
        dbox_dispose(&d);
    }
}


/*  Event handler for the menu.  */

static void solitaire_menuproc(void *handle, char *hit)
{
    handle = handle; /* We don't need handle: this stops compiler warning */
  
    /* Find which menu item was hit and take action as appropriate */
    switch (hit[0])
    {
        case solitaire_menu_info:
            solitaire_info_about_program();
            break;
        
        case solitaire_menu_quit:
            /*  Exit from the program. 
                The wimp gets rid of the window and icon */
            exit(0);
    }
}


/*  Event handler for window.  */

static void solitaire_event_handler(wimp_eventstr *e, void *handle)
{
    handle = handle; /* We don't need handle: this stops compiler warning */
      
    /* Deal with event */

    switch (e->e)
    {
        case wimp_EREDRAW:
            solitaire_redraw_window(e->data.o.w);
            break;
    
        case wimp_EOPEN:
            solitaire_open_window(&e->data.o);
            break;
    
        case wimp_ECLOSE:  /* Pass on close request */
            wimpt_noerr(wimp_close_wind(e->data.o.w));
            solitaire_window_open = FALSE;
            break;
          
        case wimp_EBUT:
            solitaire_button_pressed(&e->data);
            break;
        
        case wimp_EUSERDRAG:
            solitaire_userdrag();
            break;
            
        case wimp_EPTRLEAVE: case wimp_EPTRENTER:
            break;
            
        case wimp_ESEND:
            break;
        
        default:   /* Ignore any other event */
            break;
    }
}


/****************************** INITIALISATION ******************************/

/*  Initialise the program, returning TRUE if it was all OK.  */

static BOOL solitaire_initialise(void)
{
    /* RISC_OSlib initialisation */

    wimpt_init("Solitaire");        /* Main Wimp initialisation */
    res_init("solitaire");               /* Resources */
    resspr_init();                   /* Application sprites */
    template_init();                 /* Templates */
    dbox_init();                     /* Dialogue boxes */
  
    /* Create the main window, and declare its event handler */

    if (!solitaire_create_window("main", &solitaire_win_handle))
        return FALSE; /* Window creation failed */

    win_register_event_handler(solitaire_win_handle, solitaire_event_handler, 0);
  
    /* Create the menu tree */

    if (solitaire_menu = menu_new("solitaire", ">Info,Quit"), solitaire_menu == NULL)
        return FALSE; /* Menu create failed */
  
    /* Set up the icon on the icon bar, and declare its event handlers */

    baricon("!solitaire", (int)resspr_area(), solitaire_iconclick);

    if (!event_attachmenu(win_ICONBAR, solitaire_menu, solitaire_menuproc, 0))
        return FALSE; /* Unable to attach menu */
    
    /*  Unknown events required for USERDRAG  */
    
    win_claim_unknown_events(solitaire_win_handle);
    resetgame();
    
    /* All went ok */
    
    return TRUE;
}


/******************************* MAIN PROGRAM ********************************/

int main()
{
    if (solitaire_initialise())
    {
        /* The main event loop */
        
        while (TRUE)
            event_process();
    }
  
    return 0;
}

