/*      VERSION 0106
  dc.c    from cli.c
  desk calculator II  (Windows/DDE)

  working well--make into calculator                                  0044
  add error messages and size adjustments and colour                  0074
  final touches                                                       0091
  add intelligent screen update                                       0092
*/
#include  <float.h>
#include  <stdio.h>
#include  <windows.h>
#include  <dde.h>
#include  "evdde.h"
#define   nostic  fprintf(stdprn,
#define   TXx     CharWid*(curpos%ScrWid)
#define   TXy     CharHgt*curlin
#define   BS      0x08
#define   CR      0x0d
#define   sSIZ    127

int     curpos,     /* current col and bufpos */
        curlin,     /* current line */
        ScrWid,     /* screen bounds */
        ScrHgt,     /*   in chars    */
        CharWid=0,  /* character width */
        CharHgt=0;  /*   and height    */
BOOL    initted=FALSE;
static
char  ttbuf[sSIZ+1],  /* input buffer */
      *errS[]={       /* error elaborations */
        "stuperr","expression too complex","syntax error",
        "illegal variable","illegal function","divide by zero",
        "math library error"
        };
static
ATOM  aEval;    /* atom ID of EVAL */
static
HWND  Server;   /* window ID of server */

extern HWND hMain;
extern  Initialize(HANDLE,HANDLE,int);
extern  Paint(HWND,PPAINTSTRUCT,HDC);
extern  addline(HWND,char *,int);
extern  KillTop(void);

/* hpt
    diagnostic string print given handle
*/
static
hpt(s)
  HANDLE  s;
{
  LPSTR p;

  if(s==(HANDLE)NULL) return;
  for(p=(LPSTR)GlobalLock(s);*p;p++)
    fputc(*p,stdprn);
  fputc('\n',stdprn); fflush(stdprn);
  GlobalUnlock(s);
} /* hpt */

/* GetBuf
    allocate global buffer for data and copy to it
*/
static
HANDLE  GetBuf(s,siz)
  char  *s;
  unsigned  siz;
{
  HANDLE    g;
  LPSTR     ch;
  unsigned  i;

  LockData(0);
  g=GlobalAlloc(GMEM_SHARE,(DWORD)siz);
  if(g!=NULL &&
     (ch=(LPSTR)GlobalLock(g))!=NULL){
    for(i=0;i<siz;i++) *ch++=*s++;
    GlobalUnlock(g);
  }
  UnlockData(0);
  return g;
} /* GetBuf */

/* CharInput
    handle WM_CHAR messages
    uses CURPOS-1,CURLIN as screen coords
         SCRWID,SCRHGT as screen limits
*/
static
CharInput(hWnd,ch)
  HWND  hWnd;
  int   ch;
{
  HDC   hdc;
  long  lOld,lNew;
  RECT  rect;
  char  buf[2];

  buf[1]=0;
  hdc=GetDC(hWnd);
  SetBkMode(hdc,TRANSPARENT);
  SetTextColor(hdc,RGB(0,0,0));
  if(ch>31 && ch<127 && curpos<sSIZ){
    buf[0]=ttbuf[curpos++]=ch;
    ttbuf[curpos]=0;
    /* wraparound */
    if((curpos % ScrWid)==0){
      if(curlin==ScrHgt)
        ScrollWindow(hWnd,0,-CharHgt,(LPRECT)NULL,(LPRECT)NULL);
      curlin=(curlin<ScrHgt)?curlin+1:curlin;
      UpdateWindow(hMain);
    }
    TextOut(hdc,CharWid*((curpos%ScrWid)-1),TXy,(LPSTR)buf,1);
  }else
  if(ch==BS && curpos>0){
    buf[0]=' '; ttbuf[--curpos]=0;
    SetBkMode(hdc,OPAQUE);
    TextOut(hdc,TXx,TXy,(LPSTR)buf,1);
    SetBkMode(hdc,TRANSPARENT);
  }else
  if(ch==CR){
    ttbuf[curpos++]='\n'; ttbuf[curpos]=0; curpos=0;
    addline(hWnd,ttbuf,0);
  }
  SetBkMode(hdc,OPAQUE);
  ReleaseDC(hWnd,hdc);
} /* CharInput */

/* sysinit
    initialize main system parameters
*/
static
sysinit(){
  HDC         dc;
  TEXTMETRIC  tm;
  int         count;

  dc=GetDC(hMain);
  GetTextMetrics(dc,(TEXTMETRIC FAR *)&tm);
  CharWid=tm.tmAveCharWidth;
  CharHgt=tm.tmHeight + tm.tmExternalLeading;
  ScrHgt/=CharHgt; ScrWid/=CharWid;
  ReleaseDC(hMain,dc);
  for(count=0;count<sSIZ+1;count++) ttbuf[count]=0;
  curpos=curlin=0;
  initted=TRUE;
} /* sysinit */

/* Create
    establish contact with EVAL server and send request
*/
static
Create(hwnd,ServID,buf)
  HWND  hwnd;
  LPSTR ServID;
  char  *buf;
{
  HANDLE      gh;
  DDEREQUEST  ex;
  DWORD       d;

  Server=NULL;
  /* check for atom registered by EVAL */
  aEval=DDEAddAtom(ServID);
  /* broadcast 1st message to all processes
     Server will be set by ACK to server ID */
  SendMessage((HWND)-1,WM_DDE_INITIATE,hwnd,MAKELONG(NULL,aEval));
  DDEDeleteAtom(aEval);
  if(Server==NULL)
    MessageBox(hwnd,(LPSTR)"EVAL not loaded",(LPSTR)"ALculator",MB_OK);
  else{
    /* send the request */
    ex.expr=GetBuf(buf,strlen(buf)+1);
    ex.evalcode=0xee;
    if(ex.expr==(HANDLE)NULL) return;
    gh=GetBuf((char *)&ex,sizeof(DDEREQUEST));
    if(gh!=(HANDLE)NULL){
      d=MAKELONG(gh,aEval);
      PostMessage(Server,WM_DDE_POKE,hwnd,d);
    }else
      GlobalFree(ex.expr);
  }
} /* Create */

/* dde_data
    process incoming DATA messages
*/
static
dde_data(hwnd,lStuff)
  HWND  hwnd;   /* server handle */
  DWORD lStuff; /* xtra data */
{
  ATOM      aData;
  HANDLE    DDEdata;
  LPDDEDATA dde;
  char      buf[81];
  HDC       dc;
  int       color;

  /* check for specific server */
  if(hwnd==Server){
    aData=HIWORD(lStuff),DDEdata=LOWORD(lStuff);
    dde=(LPDDEDATA)GlobalLock(DDEdata);
    /* process data here */
    switch(dde->evalerr){
      case PARSok:
        sprintf(buf,"   %g",dde->evalue); color=1; break;
      case PARSmath:
        sprintf(buf,"   %g    %s\n",dde->evalue,errS[-dde->evalerr-1]);
        color=2; break;
      default:  /* fancy error processing */
        sprintf(buf,"%*c  %s",dde->errpos+1,'^',errS[-dde->evalerr-1]);
        color=4; break;
    }
    /* send result to screen manager */
    addline(hMain,buf,color);
    /* release data packet */
    GlobalUnlock(DDEdata);
    GlobalFree(DDEdata);
  }
} /* dde_data */

/* WndProc
    window class function
*/
long far PASCAL
WndProc(hWnd,message,wParam,lParam)
  HWND  hWnd;
  unsigned  message;
  WORD  wParam;
  DWORD lParam;
{
  ATOM        aCompare;
  BOOL        bAck;
  PAINTSTRUCT lp;

  switch(message){
    case WM_SIZE:
      /* adjust parms as window changes */
      if(wParam==SIZENORMAL){
        ScrWid=LOWORD(lParam);
        ScrHgt=HIWORD(lParam);
        if(initted){
          /* round fractional rows off */
          ScrHgt=(ScrHgt%CharHgt!=0?ScrHgt/CharHgt-1:ScrHgt/CharHgt);
          ScrWid/=CharWid;
          /* make some adjustments if getting smaller */
          if(ScrHgt<curlin){
            int   i;
            /* scroll buffer up */
            for(i=ScrHgt;i<curlin;i++) KillTop();
            curlin=ScrHgt;
          }
        }
      }
      break;
    case WM_PAINT:
      Paint(hWnd,&lp,BeginPaint(hWnd,(LPPAINTSTRUCT)&lp));
      EndPaint(hWnd,(LPPAINTSTRUCT)&lp);
      break;
    case WM_CHAR:
      CharInput(hWnd,wParam);
      SetCaretPos(TXx,TXy);
      if(wParam==CR) Create(hWnd,(LPSTR)DDEevalID,ttbuf);
      break;
    case WM_SETFOCUS:
      CreateCaret(hWnd,(HBITMAP)NULL,1,CharHgt);
      if(initted)  SetCaretPos(TXx,TXy);
      else         SetCaretPos(1,8);
      ShowCaret(hWnd);
      break;
    case WM_KILLFOCUS:
      HideCaret(hWnd);
      DestroyCaret();
      break;
    case WM_DDE_ACK:
      aCompare=HIWORD(lParam);  bAck=LOWORD(lParam);
      if(aCompare==aEval && bAck) Server=wParam;
      break;
    case WM_DDE_DATA:
      dde_data(wParam,lParam);
      break;
    default: return DefWindowProc(hWnd,message,wParam,lParam);
  }
  return 0L;
} /* WndProc */

/* WinMain
    main Windows entry point
*/
int PASCAL WinMain( hInstance, hPrevInstance, lpszCmdLine, cmdShow )
  HANDLE  hInstance, hPrevInstance;
  LPSTR   lpszCmdLine;
  int     cmdShow;
{
  MSG   msg;
  HWND  hWnd;
  HMENU hMenu;
  RECT  r;

  if(!Initialize(hInstance,hPrevInstance,cmdShow)) return FALSE;
  sysinit();
  /* Polling messages from event queue */
  while (GetMessage((LPMSG)&msg, NULL, 0, 0)) {
    TranslateMessage((LPMSG)&msg);
    DispatchMessage((LPMSG)&msg);
  }
  return (int)msg.wParam;
} /* WinMain */
