/*
 * whistory.c -- Move list window for WinBoard
 * $Id$
 *
 * Copyright 1995 Free Software Foundation, Inc.
 *
 * ------------------------------------------------------------------------
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * ------------------------------------------------------------------------
 */

#include "config.h"

#include <windows.h> /* required for all Windows applications */
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <io.h>
#include <fcntl.h>
#include <math.h>
#include <commdlg.h>
#include <dlgs.h>

#include "common.h"
#include "winboard.h"
#include "frontend.h"
#include "backend.h"
#include "analyst.h"

/* Module globals */
int historyX, historyY, historyW, historyH;

/* Imports from winboard.c */
extern HINSTANCE hInst;
extern HWND hwndMain;
void HistoryPopDown P((void));

#define HISTORY_TIMER_ID 80

struct History{
  HWND dialog;
  char up;
  char moveblack;
  unsigned long flags[MAX_MOVES];
  int  idletimer;

  int lastline;
  int lastselected;
  int lastmove; 
  char last_has_tactic;
  char last_has_mate;

  int first;// necessary?
  int current;
  int process_click;
};

extern char *commentList[];
struct History history={0};

static int FlagB(int line)
{
    return history.flags[line] & 0x80000000;
}
static int FlagW(int line)
{
    return history.flags[line] & 0x40000000;
}
static int FlagComment(int line)
{
    return history.flags[line] & 0x20000000;
}
static int FlagMoveNum(int line)
{
    return history.flags[line] & 0x0000ffff;
}
static void SetBWFlag(int line)
{
    history.flags[line] &= 0x3fffffff;
    history.flags[line] |= 0xC0000000;
}
static void SetBFlag(int line)
{
    history.flags[line] &= 0x3fffffff;
    history.flags[line] |= 0x80000000;
}
static void SetWFlag(int line)
{
    history.flags[line] &= 0x3fffffff;
    history.flags[line] |= 0x40000000;
}
static void SetCommentFlag(int line)
{
    history.flags[line] &= 0xDfffffff;
    history.flags[line] |= 0x20000000;
}
static void SetMoveNumFlag(int movenum, int line)
{
    history.flags[line] &= 0xffff0000;
    history.flags[line] |= movenum;
}
static void 
AddMoveW(int movenum, char movelist[][2*MOVE_LEN])
{
    // movenum is the last move (1-based)
   char line[MOVE_LEN+5];
   history.lastline++;
   if (appData.debugMode)
     sprintf(line,"line %d (%d) %0.3d. %s", history.lastline, movenum-1, movenum/2+1,movelist); 
   else
     sprintf(line,"%d. %s", movenum/2+1,movelist); 
   SendDlgItemMessage(history.dialog, OPT_HistoryText, 
                    LB_ADDSTRING, (WPARAM) 0, 
                    (LPARAM) line);


   history.lastmove = movenum;

   // we store which move (0-based) is in the beginning of this line
   SetMoveNumFlag(movenum-1, history.lastline);
   SetWFlag(history.lastline);
}
static void 
AddMoveB(int movenum, char movelist[][2*MOVE_LEN])
{
   char line[MOVE_LEN+5];
   history.lastline++;
   if (appData.debugMode)
     sprintf(line,"line %d (%d) %0.3d. ... %s", history.lastline, movenum-1, movenum/2, movelist); 
   else
     sprintf(line,"%d. ... %s", movenum/2, movelist); 
   SendDlgItemMessage(history.dialog, OPT_HistoryText, 
                    LB_ADDSTRING, (WPARAM) 0, 
                    (LPARAM) line);


   history.lastmove = movenum;

   // we store which move (0-based) is in the beginning of this line
   SetMoveNumFlag(movenum-1, history.lastline);
   SetBFlag(history.lastline);
}
static void 
AddMoveBW(int movenum, char movelist[][2*MOVE_LEN])
{
    // movenum is the last move (1-based)
   char line[MOVE_LEN+5];
   history.lastline++;
   if (appData.debugMode)
    sprintf(line,"line %d (%d) %0.3d. %s %s",history.lastline, movenum-2, movenum/2,movelist-1,movelist);
   else
    sprintf(line,"%d. %s %s",movenum/2,movelist-1,movelist);
  
   SendDlgItemMessage(history.dialog, OPT_HistoryText, 
                    LB_ADDSTRING, (WPARAM) 0, 
                    (LPARAM) line);
   history.lastmove = movenum;

   // we store which move (0-based) is in the beginning of this line
   SetMoveNumFlag(movenum-2, history.lastline);
   SetBWFlag(history.lastline);
}
static void 
DeleteLastLine()
{
    SendDlgItemMessage(history.dialog, OPT_HistoryText, 
                    LB_DELETESTRING, (WPARAM) history.lastline, 
                    (LPARAM) 0);
    history.lastline--;

}
static int 
HasComment(int movenum)
{
    return commentList[movenum]!=0;
}
static void 
AddComment(int movenum)
{
    char *comment = commentList[movenum];
    char buf[100];
    char *str = &buf[0];
    strncpy(buf,comment,98);
    buf[98]=0;
    while (*str) {
        if (*str == '\n' || *str == '\r') 
            *str = ' ';
        str++;
    }

    SendDlgItemMessage(history.dialog, OPT_HistoryText, 
                    LB_ADDSTRING, (WPARAM) 0, 
                    (LPARAM) buf);
    history.lastline++;
    SetMoveNumFlag(movenum, history.lastline);
    SetCommentFlag(history.lastline);

}
static void 
AddMove(int movenum, char movelist[][2*MOVE_LEN])
{
    // movenum is 1 based!
    if (!(movenum & 1) ) // even moves 
    {
        if (movenum > 0 && HasComment(movenum-1))
        {
            AddMoveB(movenum, movelist);
        }
        else
        {
            DeleteLastLine();
            AddMoveBW(movenum, movelist);
        }
        if (HasComment(movenum))
            AddComment(movenum);
    }
    else
    {
        AddMoveW(movenum, movelist);
        if (HasComment(movenum))
            AddComment(movenum);
    }

}
static void 
InitHistory()
{   
    int i;
 
    if (history.dialog)
        SendDlgItemMessage(history.dialog, OPT_HistoryText, LB_RESETCONTENT, 0, 0);
    history.lastline = -1;
    history.lastmove = 0;
    history.lastselected = -1;
    history.process_click = 0;
    for (i=0; i < MAX_MOVES; i++)
    {
        history.flags[i] = 0;
    }
}
static void 
HighlightLine(int current_move)
{
    int i;
    int is_black = current_move & 1;
    int sel_line = history.lastline;

    
    for (i = 0; i < history.lastline; i++)
    {
        if (FlagComment(i))
            continue;
        if (FlagMoveNum(i)==current_move)
        {
            sel_line = i;
            break;
        }
        else if (is_black&&FlagMoveNum(i)==current_move-1 && FlagB(i))
        {
            sel_line = i;
        }
    }
    if (sel_line!= history.lastselected)
    {
        history.lastselected = sel_line;
        SendDlgItemMessage(history.dialog, OPT_HistoryText, LB_SETCURSEL, sel_line, 0);

    }
}

void
HistorySet(char movelist[][2*MOVE_LEN], int first, int last, int current)
{
    int i;

    // check if processing mouse click or  beginning of game
    if (history.process_click || current < 0) return;

    if (last < history.lastmove // are we truncating
        || (first==0 && last==0 && current==-1))
    {
        InitHistory();
    }

    if (last > history.lastmove)
    {   
        for (i = history.lastmove + 1; i <= last; i++)
        {
            AddMove(i, &movelist[i-1]);
        }
        history.lastselected = history.lastline;
        SendDlgItemMessage(history.dialog, OPT_HistoryText, LB_SETCURSEL, history.lastselected, 0);
    }
    
    HighlightLine(current);

    
}

static void 
HistoryClick(int n)
{
    int move_num = FlagMoveNum(n);
    history.process_click = 1;

    if (FlagComment(n))
    {
        
    }
    else
    {
        if (history.moveblack && FlagB(n) && FlagW(n))
            move_num+=2; // go to black moves
        else
            move_num+=1;// go to white moves
    }
    ToNrEvent(move_num); 
    history.process_click = 0;
}
static void 
CheckListSelection()
{
    if (history.dialog)
    {
        int sel = SendDlgItemMessage(history.dialog, OPT_HistoryText, LB_GETCURSEL, 0, 0);
        if (sel>=0 && sel!=history.lastselected) {
            history.lastselected = sel;
            HistoryClick(sel);
        }

    }
}

void 
ReinitHistory()
{
    InitHistory();
    if (currentMove > 0)
    {
        BackwardEvent();
        ForwardEvent();
    }
    else
    {
        ForwardEvent();
        BackwardEvent();
    }
}

LRESULT CALLBACK
HistoryDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
  static HANDLE hwndText;
  static int sizeX, sizeY;
  int newSizeX, newSizeY;
  MINMAXINFO *mmi;
  RECT rect;

  switch (message) {
  case WM_INITDIALOG: 
    /* Initialize the dialog items */

    if (!history.dialog) {
      history.dialog = hDlg;
      CheckDlgButton(hDlg,IDC_WHITE_MOVE, 1);
      history.idletimer = SetTimer(history.dialog, (UINT) HISTORY_TIMER_ID,
                                     (UINT) 300 /*millisec*/, NULL);
      GetClientRect(hDlg, &rect);
      sizeX = rect.right;
      sizeY = rect.bottom;
      if (historyX != CW_USEDEFAULT) {
        WINDOWPLACEMENT wp;
        EnsureOnScreen(&historyX, &historyY);
        wp.length = sizeof(WINDOWPLACEMENT);
        wp.flags = 0;
        wp.showCmd = SW_SHOW;
        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
        wp.rcNormalPosition.left = historyX;
        wp.rcNormalPosition.right = historyX + historyW;
        wp.rcNormalPosition.top = historyY;
        wp.rcNormalPosition.bottom = historyY + historyH;
        SetWindowPlacement(hDlg, &wp);

        GetClientRect(hDlg, &rect);
        newSizeX = rect.right;
        newSizeY = rect.bottom;
        ResizeEditPlusButtons(hDlg, GetDlgItem(hDlg, OPT_HistoryText), sizeX, sizeY,
                  newSizeX, newSizeY);
        sizeX = newSizeX;
        sizeY = newSizeY;
      }

    }
    return FALSE;
    
   case WM_SIZE:
    newSizeX = LOWORD(lParam);
    newSizeY = HIWORD(lParam);
    ResizeEditPlusButtons(hDlg, GetDlgItem(hDlg, OPT_HistoryText),
      sizeX, sizeY, newSizeX, newSizeY);
    sizeX = newSizeX;
    sizeY = newSizeY;
    break;

  case WM_GETMINMAXINFO:
    /* Prevent resizing window too small */
    mmi = (MINMAXINFO *) lParam;
    mmi->ptMinTrackSize.x = 100;
    mmi->ptMinTrackSize.y = 100;
    break;
  case WM_TIMER:
    switch (wParam) {
        case HISTORY_TIMER_ID:
            CheckListSelection();
        break;
    }
  break;

  case WM_COMMAND:
    switch (LOWORD(wParam)) {
      
    case OPT_HistoryReinit:
        ReinitHistory();
    break;

    case IDCANCEL:
      HistoryPopDown();
    return TRUE;

    case IDC_BLACK_MOVE:
        history.moveblack = 1;
        history.lastselected = -1;
    break;

 
    case IDC_WHITE_MOVE:
        history.moveblack = 0;
        history.lastselected = -1;
    break;      
    
    default:
      return FALSE;
    }
    return TRUE;

  default:
    break;
  }
  return FALSE;
}

BOOLEAN IsHistoryWindUp()
{
    return history.up;
}
HWND HistoryDialog()
{
    return history.dialog;
}

VOID HistoryPopUp()
{
  FARPROC lpProc;
  
  CheckMenuItem(GetMenu(hwndMain), IDM_ShowHistory, MF_CHECKED);

  if (history.dialog) {
    SendMessage(history.dialog, WM_INITDIALOG, 0, 0);
    if (!history.up) ShowWindow(history.dialog, SW_SHOW);
  } else {
    lpProc = MakeProcInstance((FARPROC)HistoryDialogProc, hInst);
    CreateDialog(hInst, MAKEINTRESOURCE(DLG_History),
      hwndMain, (DLGPROC)lpProc);
    FreeProcInstance(lpProc);
  }
  history.up = TRUE;
}

VOID HistoryPopDown(void)
{
  CheckMenuItem(GetMenu(hwndMain), IDM_ShowHistory, MF_UNCHECKED);
  if (history.dialog) ShowWindow(history.dialog, SW_HIDE);
  history.up = FALSE;
}


VOID HistoryHighlight(int index)
{
  if (history.dialog == NULL) return;
  SendDlgItemMessage(history.dialog, OPT_HistoryText, 
    LB_SETCURSEL, index - 1, 0);
}


VOID HistoryDestroy()
{
  HistoryPopDown();
}

VOID ShowHistoryProc()
{
  if (history.up) {
    HistoryPopDown();
  } else {
    HistoryPopUp();
  }
}
