/*===========================================================================*/
/*                                                                           */
/* File    : CARDFILE.C                                                      */
/*                                                                           */
/* Purpose :                                                                 */
/*                                                                           */
/* History :                                                                 */
/*                                                                           */
/* (C) Copyright 1990 Marc Adler/Magma Systems     All Rights Reserved       */
/*===========================================================================*/

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <fcntl.h>
#include <io.h>
#include <sys\types.h>
#include <sys\stat.h>

#include "window.h"
#include "cardfile.h"

HWND hWndCardfile;
HWND hCurrCard;
HWND hActiveEdit = NULLHWND;
BYTE szSearchString[256] = { '\0' };
CARDFILE CurrCardFile;
BYTE  szCurrFileName[13];
LIST  *CurrIndexList = NULL;
LIST  *CurrRecordList = NULL;
BOOL  fCardfileState = 0;


main(argc, argv)
  INT  argc;
  char **argv;
{
  MSG       msg;
  PWNDCLASS pwndClass;

  /*
    Initialize the window system - this *must* be done once in every appl
  */
  WinInit();
  WinUseSysColors(NULLHWND, TRUE);

  /*
    Register the card class
  */
  pwndClass = (PWNDCLASS) emalloc(sizeof(WNDCLASS));
  pwndClass->szClassName = "Card";
  pwndClass->szBaseClass = "Normal";
  pwndClass->pfnWndProc  = CardWndProc;
  pwndClass->cbWndExtra  = sizeof(HWND) * 2;
  RegisterClass(pwndClass);
  MyFree(pwndClass);

  /*
    Create the main window and set the main window procedure
  */
  hWndCardfile = CreateWindow("Normal",       /* class */
                       "CARDFILE",     /* title */
                       WS_CAPTION | WS_SYSMENU | WS_MINMAXBOX |
                       WS_BORDER  | WS_VSCROLL,
                       0,0, 80,25,     /* x,y,width,height */
                       SYSTEM_COLOR,   /* attr */
                       0,              /* id */
                       NULLHWND,       /* hParent */
                       NULLHWND,       /* hMenu */
                       0,              /* hInst */
                       0L);            /* lpParam */
  WinSetWinProc(hWndCardfile, CardfileWndProc);
  BuildMenu(hWndCardfile);

  /*
    Display the main window
  */
  ShowWindow(hWndCardfile, TRUE);
  SetFocus(hWndCardfile);

  /*
    Open the cardfile specified on the command line, or create a new card
  */
  if (argc > 1)
    CardFileOpen(argv[1]);
  else
    CardCreate();

  /*
    Main message loop
  */
  while (GetMessage(&msg) != WM_QUIT)
  {
    TranslateAccelerator(&msg);
    DispatchMessage(&msg);
  }

  exit(0);
}


/*****************************************************************************/
/*                                                                           */
/*  BuildMenu - create the main menu bar and all of the pulldowns            */
/*                                                                           */
/*****************************************************************************/
BuildMenu(hParent)
  HWND hParent;
{
  WORD  hMod;
  HMENU hBar;
  HACCEL hAccel;

  /*
    Load the main menubar in from the external resource file DESKTOP.RES.
    Alternatively, we can also do a bunch of CreateMenu()'s, but using
    a resource file lets us modify our menus more easily.
  */
  hMod = OpenResourceFile("CARDFILE");
  if (!hMod)
  {
    printf("Can't load main menu\n");
    exit(1);
  }
  hBar = LoadMenu(hMod, ID_CARDFILE, hParent);
  SetMenu(hParent, hBar);

  /*
    Load in the accelerator table and attach it to the main window
  */
  hAccel = LoadAccelerators(hMod, ID_CARDFILE);
  if (hAccel)
  {
    AcceleratorSetFocus(hAccel);
    AcceleratorSetWnd(hParent);
  }

  CloseResourceFile(hMod);
}


int CardCreate()
{
  int rc;
  int nRecs;
  RECT r;
  RECORD *pRec;

  /*
    MS Cardfile will place the card in alphabetical order
  */
  pRec = (RECORD *) emalloc(sizeof(RECORD));
  ListAdd(&CurrRecordList, ListCreate((char *) pRec));
  CurrCardFile.iCurrRecord = CurrCardFile.nRecords++;

  pRec->hWnd = hCurrCard =
         CreateWindow("Card",          /* class */
                       NULL,           /* title */
                       0L,
                       2,VideoInfo.length-12, 50,10,
                       SYSTEM_COLOR,   /* attr */
                       0,              /* id */
                       hWndCardfile,   /* hParent */
                       NULLHWND,       /* hMenu */
                       0,              /* hInst */
                       0L);            /* lpParam */

  /*
    Hide the card if we are showing the list
  */
  if (fCardfileState & STATE_VIEWLIST)
    ShowWindow(hCurrCard, FALSE);

  /*
    Disable refreshing, so that the WM_SETFOCUS message sent by the
    dialog box mgr at the end of the dialog processing does not
    cause CardFileAdjustWindows() to do any refreshing.
  */
  fCardfileState |= STATE_NOREFRESH;
  rc = GenericDialogDriver(DLG_INDEX, IndexDialogProc);
  fCardfileState &= ~STATE_NOREFRESH;

  CardAlphabetize(CurrCardFile.iCurrRecord);
  SetFocus(hCurrCard);

  return rc;
}


int CardFileAdjustWindows()
{
  int    i, nTotal;
  int    iHeight;
  RECORD *pRec;
  WORD   attr;
  RECT   rClient;

  nTotal = 0;

  if (fCardfileState & STATE_VIEWLIST)
  {
    attr = WinGetAttr(hWndCardfile);
    GetClientRect(hWndCardfile, &rClient);
    iHeight = RECT_HEIGHT(rClient);

    WinClear(hWndCardfile);

    if (CurrCardFile.iCurrRecord < CurrCardFile.iTopLine)
    {
      CurrCardFile.iTopLine = CurrCardFile.iCurrRecord;
    }
    while (CurrCardFile.iCurrRecord >= CurrCardFile.iTopLine + iHeight)
    {
      CurrCardFile.iTopLine = min(CurrCardFile.nRecords-1,
                                  CurrCardFile.iTopLine + iHeight);
    }

    for (i = CurrCardFile.iTopLine;  i < CurrCardFile.nRecords;  i++)
    {
      pRec = (RECORD *) ListGetNth(CurrRecordList, i)->data;
      WinPuts(hWndCardfile, nTotal, 0, pRec->szIndex, 
              (i == CurrCardFile.iCurrRecord) ? MAKE_HIGHLITE(attr) : attr);
      if (++nTotal >= iHeight)
        break;
    }
  }
  else
  {
    for (i = CurrCardFile.iCurrRecord;  i < CurrCardFile.nRecords;  i++)
    {
      pRec = (RECORD *) ListGetNth(CurrRecordList, i)->data;
      WinMove(pRec->hWnd, (VideoInfo.length-11) - (nTotal*2), nTotal+1);
      nTotal++;
    }

    for (i = 0;  i < CurrCardFile.iCurrRecord;  i++)
    {
      pRec = (RECORD *) ListGetNth(CurrRecordList, i)->data;
      WinMove(pRec->hWnd, (VideoInfo.length-11) - (nTotal*2), nTotal+1);
      nTotal++;
    }

    for (i = CurrCardFile.iCurrRecord - 1;  i >= 0;  i--)
    {
      pRec = (RECORD *) ListGetNth(CurrRecordList, i)->data;
      WinToTop(pRec->hWnd);
    }
    for (i = CurrCardFile.nRecords - 1;  i > CurrCardFile.iCurrRecord;  i--)
    {
      pRec = (RECORD *) ListGetNth(CurrRecordList, i)->data;
      WinToTop(pRec->hWnd);
    }

    pRec = (RECORD *) ListGetNth(CurrRecordList, CurrCardFile.iCurrRecord)->data;
    BringWindowToTop(pRec->hWnd);
    WinDrawAllWindows();
  }

  SetScrollRange(hWndCardfile, SB_VERT, 1, CurrCardFile.nRecords, TRUE);
  SetScrollPos(hWndCardfile, SB_VERT, CurrCardFile.iCurrRecord+1, TRUE);

  return TRUE;
}

int CardHwndToIndex(hWnd)
  HWND hWnd;
{
  int   i;
  RECORD *pRec;

  for (i = 0;  i < CurrCardFile.nRecords;  i++)
  {
    pRec = (RECORD *) ListGetNth(CurrRecordList, i)->data;
    if (pRec->hWnd == hWnd)
      return i;
  }
  return -1;
}


/*
  This is called when we click on a card in the stack, or when we
  explicitly set a card to the current one
*/
int CardBringToTop(hWnd)
  HWND hWnd;
{
  int idx;

  if (fCardfileState & STATE_NOREFRESH)
    return TRUE;

  if ((idx = CardHwndToIndex(hWnd)) == -1)
    return FALSE;

  CurrCardFile.iCurrRecord = idx;
  CardFileAdjustWindows();
  return TRUE;
}


int CardAlphabetize(iRecord)
  int iRecord;
{
  int  i;
  LIST *p, *pPrev, *pCurrListItem;
  RECORD *pCurrRec, *pRec;

  if (CurrCardFile.nRecords <= 1)
    return TRUE;

  /*
    Search the linked list for the current item, then unhook it from
    the linked list.
  */
  for (i=0,  p=CurrRecordList;  p && i < iRecord;  p=p->next)
    pPrev = p;
  if (p == CurrRecordList)
    CurrRecordList = p->next;
  else
    pPrev->next = p->next;

  /*
    Save the list member as the current item
  */
  pCurrListItem = p;
  pCurrRec = (RECORD *) pCurrListItem->data;

  /*
    Locate the proper place to insert the string
  */
  for (p = CurrRecordList;  p;  p = p->next)
  {
    pRec = (RECORD *) p->data;
    if (strcmp(pRec->szIndex, pCurrRec->szIndex) <= 0)
      break;
  }
  ListInsert(&CurrRecordList, pCurrListItem, p);

  return TRUE;
}


int CardDelete()
{
  LIST *pCurrListItem;
  RECORD *pCurrRec;

  pCurrListItem = ListGetNth(CurrRecordList, CurrCardFile.iCurrRecord);
  pCurrRec = (RECORD *) pCurrListItem->data;

  DestroyWindow(pCurrRec->hWnd);

  CurrCardFile.nRecords--;
  CurrCardFile.iCurrRecord = min(CurrCardFile.iCurrRecord, CurrCardFile.nRecords-1);

  ListDelete(&CurrRecordList, pCurrListItem);

  pCurrListItem = ListGetNth(CurrRecordList, CurrCardFile.iCurrRecord);
  pCurrRec = (RECORD *) pCurrListItem->data;
  SetFocus(pCurrRec->hWnd);

  return TRUE;
}


int CardDuplicate()
{
  RECORD *pNewRec, *pCurrRec;
  BYTE   *szBuf;

  szBuf = emalloc(4096);
  pCurrRec = (RECORD*)ListGetNth(CurrRecordList,CurrCardFile.iCurrRecord)->data;

  pNewRec = (RECORD *) emalloc(sizeof(RECORD));
  ListAdd(&CurrRecordList, ListCreate((char *) pNewRec));
  CurrCardFile.nRecords++;

  pNewRec->hWnd = hCurrCard =
         CreateWindow("Card",          /* class */
                       NULL,           /* title */
                       0L,
                       2,VideoInfo.length-12, 50,10,
                       SYSTEM_COLOR,   /* attr */
                       0,              /* id */
                       hWndCardfile,   /* hParent */
                       NULLHWND,       /* hMenu */
                       0,              /* hInst */
                       0L);            /* lpParam */

  GetWindowText(GetWindowWord(pCurrRec->hWnd, GWW_HWNDINDEX), szBuf, 4096);
  SetWindowText(GetWindowWord(pNewRec->hWnd,  GWW_HWNDINDEX), szBuf);
  GetWindowText(GetWindowWord(pCurrRec->hWnd, GWW_HWNDEDIT), szBuf, 4096);
  SetWindowText(GetWindowWord(pNewRec->hWnd,  GWW_HWNDEDIT), szBuf);
  MyFree(szBuf);

  CardAlphabetize(CurrCardFile.iCurrRecord);
  SetFocus(hCurrCard);
  return TRUE;
}



int CardFileShowCards(bShow)
  int bShow;
{
  RECORD *pRec;
  int    i;

  for (i = 0;  i < CurrCardFile.nRecords;  i++)
  {
    pRec = (RECORD *) ListGetNth(CurrRecordList, i)->data;
    ShowWindow(pRec->hWnd, (BOOL) bShow);
  }
  return TRUE;
}


int CardFileSearch(pszPattern)
  char *pszPattern;
{
  int    i, iOffset;
  int    iPatLen = strlen(pszPattern);
  RECORD *pRec;
  BYTE   *szBuf;
  BYTE   *pMatch = NULL;

  int    iFoundRec = -1;
  RECORD *pFoundRec = NULL;

  if (!pszPattern || !pszPattern[0])
    return FALSE;

  szBuf = emalloc(4096);
  i = CurrCardFile.iCurrRecord;

  do
  {
    pRec = (RECORD *) ListGetNth(CurrRecordList, i)->data;
    GetWindowText(GetWindowWord(pRec->hWnd, GWW_HWNDEDIT), szBuf, 4096);

    iOffset = (i == CurrCardFile.iCurrRecord)
       ? SendMessage(GetWindowWord(pRec->hWnd, GWW_HWNDEDIT), EM_GETSEL, 0, 0L)
       : 0;

    if ((pMatch = strstr(szBuf + iOffset, pszPattern)) != NULL)
    {
      iFoundRec = i;
      pFoundRec = pRec;
      break;
    }

    if (++i == CurrCardFile.nRecords)
      i = 0;
  } while (i != CurrCardFile.iCurrRecord);

  /*
    Search the current card once more
  */
  if (!pMatch)
  {
    pRec = (RECORD *) ListGetNth(CurrRecordList, i)->data;
    GetWindowText(GetWindowWord(pRec->hWnd, GWW_HWNDEDIT), szBuf, 4096);
    pMatch = strstr(szBuf, pszPattern);
    if (pMatch)
    {
      iFoundRec = i;
      pFoundRec = pRec;
    }
  }

  if (!pMatch)
  {
    MessageBox("The string", pszPattern, "was not found", "Error", MB_OK);
  }
  else
  {
    if (iFoundRec != CurrCardFile.iCurrRecord)
    {
      CurrCardFile.iCurrRecord = iFoundRec;
      CardFileAdjustWindows();
    }
    SendMessage(GetWindowWord(pFoundRec->hWnd, GWW_HWNDEDIT), EM_SETSEL, 0,
                MAKELONG(pMatch - szBuf, pMatch - szBuf + iPatLen));
  }

  MyFree(szBuf);
  return TRUE;
}


int CardFileSearchForLetter(chStart)
  int chStart;
{
  int    i;
  int    iFoundRec = -1;
  RECORD *pRec;

  chStart = toupper(chStart);

  for (i = (CurrCardFile.iCurrRecord == CurrCardFile.nRecords-1) ? 0 : CurrCardFile.iCurrRecord+1;
       iFoundRec < 0; 
       i = (i == CurrCardFile.nRecords-1) ? 0 : i + 1)
  {
    pRec = (RECORD *) ListGetNth(CurrRecordList, i)->data;
    if (toupper(pRec->szIndex[0]) == chStart)
      iFoundRec = i;
    if (i == CurrCardFile.iCurrRecord)
      break;
  }

  if (iFoundRec >= 0 && iFoundRec != CurrCardFile.iCurrRecord)
  {
    CurrCardFile.iCurrRecord = iFoundRec;
    CardFileAdjustWindows();
  }
  return (iFoundRec >= 0);
}
