/********************************************************************
 *                                                                  *
 *  MODULE    :  WVCODEWN.C                                         *
 *                                                                  *
 *  PURPOSE   : This file contains the window procs for coding      *
 *              status windows                                      *                                 
 *                                                                  *
 * Author: John S. Cooper (jcooper@netcom.com)                      *
 *   Date: Sept 30, 1993                                            *
 ********************************************************************/

/* 
 * $Id: wvcodewn.c 1.12 1996/08/13 04:37:28 dumoulin Exp $
 */

#include <windows.h>
#include <windowsx.h>
#include "wvglob.h"
#include "winvn.h"
#pragma hdrstop
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/* ------------------------------------------------------------------------
 *    Window Proc for block coding status window
 *  Simple fixed text display 
 *  Associated with TypCoded  (always currentCoded)
 *  Fixed size, only ever one on screen, destroyed on block coding complete
 *  To avoid redrawing the entire status window on each update,
 *  wParam in association with WM_PAINT decides what to draw this time:
 */
long FAR PASCAL
WinVnCodedBlockWndProc (hWnd, message, wParam, lParam)
	 HWND hWnd;
	 UINT message;
	 WPARAM wParam;
	 LPARAM lParam;
{
  HDC hDC;
  HMENU hMenu;
  HFONT hOldFont;
  PAINTSTRUCT ps;
  void DrawStatusFrame (HDC hDC);
  void DrawStatusName (HDC hDC);
  void DrawStatusLines (HDC hDC);
  void DrawStatusBytes (HDC hDC);
  void DrawStatusSeq (HDC hDC);
  void DrawStatusActivity (HDC hDC);

  switch (message) {
  case WM_CREATE:
	hMenu = GetSystemMenu (hWnd, FALSE);
	AppendMenu (hMenu, MF_SEPARATOR, 0, (LPSTR) NULL);
	AppendMenu (hMenu, MF_STRING, IDM_ALWAYSONTOP, "Always On Top");
	if (BlockCodingStatusAlwaysOnTop) {
	  SetWindowPos (hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
	  CheckMenuItem (hMenu, IDM_ALWAYSONTOP, MF_BYCOMMAND | MF_CHECKED);
	}
	else {
	  SetWindowPos (hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
	  CheckMenuItem (hMenu, IDM_ALWAYSONTOP, MF_BYCOMMAND | MF_UNCHECKED);
	}
	break;

  case WM_SYSCOMMAND:
	if (wParam == IDM_ALWAYSONTOP) {
	  hMenu = GetSystemMenu (hWnd, FALSE);
	  BlockCodingStatusAlwaysOnTop = !(GetMenuState (hMenu, IDM_ALWAYSONTOP,
												MF_BYCOMMAND) & MF_CHECKED);
	  RefreshCodedBlockWnd ();
	}
	else {
	  return (DefWindowProc (hWnd, message, wParam, lParam));
	}
	break;

  case WM_SIZE:
	{
	  RECT client;
	  if (wParam != SIZE_MINIMIZED) {
		GetWindowRect (hWnd, &client);
		MoveWindow (hWnd, client.left, client.top, STATUSWIDTH,
					STATUSHEIGHT, TRUE);
	  }
	  break;
	}

  case WM_PAINT:
	hDC = BeginPaint (hWnd, &ps);
	SetBkColor (hDC, StatusBackgroundColor);
	SetTextColor (hDC, StatusTextColor);
	hOldFont = SelectObject (hDC, hStatusFont);
	if (currentCoded == NULL)
	  DrawStatusFrame (hDC);
	else {
	  DrawStatusFrame (hDC);
	  DrawStatusName (hDC);
	  DrawStatusLines (hDC);
	  DrawStatusBytes (hDC);
	  DrawStatusSeq (hDC);
	  DrawStatusActivity (hDC);
	}
	SelectObject (hDC, hOldFont);
	EndPaint (hWnd, &ps);
	break;

  case WM_CLOSE:
	if (CodingState)
	  MessageBox (hWnd,
				  "Please wait until processing is complete",
				  "Cannot close status window", MB_OK | MB_ICONSTOP);
	else
	  DestroyWindow (hWnd);

	break;

  default:
	return (DefWindowProc (hWnd, message, wParam, lParam));
  }

  return (0);
}

void
RefreshCodedBlockWnd ()
{
  HMENU hMenu;

  if (hCodedBlockWnd) {
	hMenu = GetSystemMenu (hCodedBlockWnd, FALSE);

	if (BlockCodingStatusAlwaysOnTop) {
	  SetWindowPos (hCodedBlockWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
	  CheckMenuItem (hMenu, IDM_ALWAYSONTOP, MF_BYCOMMAND | MF_CHECKED);
	}
	else {
	  SetWindowPos (hCodedBlockWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
	  CheckMenuItem (hMenu, IDM_ALWAYSONTOP, MF_BYCOMMAND | MF_UNCHECKED);
	}
	SetHandleBkBrush (hCodedBlockWnd, hStatusBackgroundBrush);
	if (!IsMinimized (hCodedBlockWnd)) {
	  SendMessage (hCodedBlockWnd, WM_SIZE, 0, 0L);		/* force a good resize */
	}
	InvalidateRect (hCodedBlockWnd, NULL, TRUE);
  }
}
/* ------------------------------------------------------------------------
 *   Routines for drawing coding block status window
 *   2 columns, 3 lines
 *
 *   HTITLE1       HTEXT1        HTITLE2  HTEXT2
 *   !             !             !        !
 *   Retrieving    ___________   Sequence ___________   <- YOFFSET
 *   Lines Read    __________    Activity ___________   <- YOFFSET+YSPACE
 *   Bytes Decoded __________                           <- YOFFSET+2*YSPACE
 */
#define HTITLE1 (3*StatusCharWidth)
#define HTITLE2 (45*StatusCharWidth)
#define HTEXT1	(22*StatusCharWidth)
#define HTEXT2	(57*StatusCharWidth)
#define YOFFSET (StatusLineHeight)
#define YSPACE	(int)(StatusLineHeight*1.5)

void
DrawStatusFrame (HDC hDC)
{
  if (CodingState >= ATTACH_PROCESSING) {
	TextOut (hDC, HTITLE1, YOFFSET, "Processing", 10);
	TextOut (hDC, HTITLE1, YOFFSET + YSPACE, "Lines sent", 10);
	TextOut (hDC, HTITLE1, YOFFSET + 2 * YSPACE, "Bytes sent", 10);
	TextOut (hDC, HTITLE2, YOFFSET, "Sequence", 8);
  }
  else {
	TextOut (hDC, HTITLE1, YOFFSET, "Retrieving", 10);
	TextOut (hDC, HTITLE1, YOFFSET + YSPACE, "Lines read", 10);
	TextOut (hDC, HTITLE1, YOFFSET + 2 * YSPACE, "Bytes decoded", 13);
	TextOut (hDC, HTITLE2, YOFFSET, "Sequence", 8);
  }
  TextOut (hDC, HTITLE2, YOFFSET + YSPACE, "Activity", 8);
}

void
DrawStatusName (HDC hDC)
{
  char temp[MAXINTERNALLINE];
  RECT aRect;

  if (currentCoded->name[0] == '\0' && currentCoded->ident[0] == '\0') {
	if (CodingState >= ATTACH_PROCESSING)
	  strcpy (temp, "Article text");
	else
	  strcpy (temp, "Name unknown");
  }
  else {
	if (currentCoded->name[0] != '\0')	// len must be <= 20    
	 {
	  NameWithoutPath (temp, currentCoded->name);	// cut off any path

	}
	else {
	  NameWithoutPath (temp, currentCoded->ident);	//cut off any path

	}
  }

  // be sure to erase old name 
  SetRect (&aRect, HTEXT1, YOFFSET, HTITLE2 - 1, YOFFSET + YSPACE - 1);
  ExtTextOut (hDC, HTEXT1, YOFFSET, ETO_OPAQUE | ETO_CLIPPED, &aRect,
			  temp, lstrlen (temp), (LPINT) NULL);
}
void
DrawStatusLines (HDC hDC)
{
  int len;
  RECT aRect;

  len = sprintf (str, "%lu", currentCoded->numLines);
  SetRect (&aRect, HTEXT1, YOFFSET + YSPACE, HTITLE2 - 1, YOFFSET + 2 * YSPACE - 1);
  ExtTextOut (hDC, HTEXT1, YOFFSET + YSPACE, ETO_OPAQUE | ETO_CLIPPED,
			  &aRect, str, len, (LPINT) NULL);
}

void
DrawStatusBytes (HDC hDC)
{
  int len;
  RECT aRect;

  len = sprintf (str, "%lu", currentCoded->numBytes);
  TextOut (hDC, HTEXT1, YOFFSET + 2 * YSPACE, str, len);
  SetRect (&aRect, HTEXT1, YOFFSET + 2 * YSPACE, HTITLE2 - 1, YOFFSET + 3 * YSPACE - 1);
  ExtTextOut (hDC, HTEXT1, YOFFSET + 2 * YSPACE, ETO_OPAQUE | ETO_CLIPPED,
			  &aRect, str, len, (LPINT) NULL);
}

void
DrawStatusSeq (HDC hDC)
{
  int len;
  RECT aRect;

  if (currentCoded->sequence == -1)
	len = sprintf (str, "%s", "Unknown");
  else
	len = sprintf (str, "%d", currentCoded->sequence);

  SetRect (&aRect, HTEXT2, YOFFSET, STATUSWIDTH - 1, YOFFSET + YSPACE - 1);
  ExtTextOut (hDC, HTEXT2, YOFFSET, ETO_OPAQUE | ETO_CLIPPED,
			  &aRect, str, len, (LPINT) NULL);
}

void
DrawStatusActivity (HDC hDC)
{
  COLORREF backColor, textColor;
  int len;
  SIZE size;
  RECT aRect;

  switch (CodingState) {
  case DECODE_SKIPPING:
	backColor = RGB (100, 0, 0);	// red

	textColor = RGB (255, 255, 255);
	strcpy (str, "Skipping");
	break;

  case DECODE_GET_TABLE:
	backColor = RGB (0, 128, 128);	// purple?

	textColor = RGB (255, 255, 255);
	strcpy (str, "Getting table");
	break;

  case DECODE_PROCESSING:
	backColor = RGB (0, 128, 0);	// green

	textColor = RGB (0, 0, 0);
	strcpy (str, "Decoding");
	break;

  case ATTACH_PROCESSING:
	backColor = RGB (0, 128, 0);	// green

	textColor = RGB (0, 0, 0);
	strcpy (str, "Encoding");
	break;

  case ATTACH_SENDING:
	backColor = RGB (128, 128, 0);	// yellow

	textColor = RGB (0, 0, 0);
	strcpy (str, "Sending");
	break;

  case ATTACH_READFILE:
	backColor = RGB (0, 128, 0);	// green

	textColor = RGB (0, 0, 0);
	strcpy (str, "Reading file");
	break;

  case ATTACH_WAITING:
	backColor = RGB (100, 0, 0);	// red

	textColor = RGB (255, 255, 255);
	strcpy (str, "Waiting");
	break;
  }

  len = lstrlen (str);
  // Create colored rectangle behind text
  SetRect (&aRect, HTEXT2, YOFFSET + YSPACE, HTEXT2 + 20 * StatusCharWidth,
		   YOFFSET + YSPACE + StatusLineHeight + 1);

  SetTextColor (hDC, textColor);
  SetBkColor (hDC, backColor);

  // center the text in the rectangle
  GetTextExtentPoint (hDC, str, len, &size);
  ExtTextOut (hDC, HTEXT2 + (20 * StatusCharWidth - size.cx) / 2,
			  YOFFSET + YSPACE, ETO_OPAQUE | ETO_CLIPPED, &aRect,
			  str, len, (LPINT) NULL);

  SetTextColor (hDC, StatusTextColor);
  SetBkColor (hDC, StatusBackgroundColor);

//  prevState = CodingState;
}

/* ------------------------------------------------------------------------
 *    Window Proc for Coding status window
 *  Simple text display window w/ scroll bars
 *  Associated with TypDecodeThread
 *  One per decode thread, variable size, user must delete it
 *
 *  Mostly copied from Petzold
 */
long FAR PASCAL
WinVnCodingWndProc (hWnd, message, wParam, lParam)
	 HWND hWnd;
	 UINT message;
	 WPARAM wParam;
	 LPARAM lParam;
{
  static int cxClient, cyClient, nVscrollPos, nVscrollMax, nHscrollPos, nHscrollMax;
  int rangeTemp;
  RECT client;
  HDC hDC;
  int i, x, y, nPaintBeg, nPaintEnd, nVscrollInc, nHscrollInc;
  PAINTSTRUCT ps;
  TypTextBlock *ThisText;
  int ThisTextNum;
  int CtrlState;
  HFONT hOldFont;

  for (ThisTextNum = 0; ThisTextNum < NumStatusTexts; ThisTextNum++)
	if (CodingStatusText[ThisTextNum]->hTextWnd == hWnd)
	  break;

  if ((NumStatusTexts != 0) &&
	  (ThisTextNum == NumStatusTexts))
	ThisTextNum = NumStatusTexts - 1;	// new status window

  ThisText = CodingStatusText[ThisTextNum];

  switch (message) {
  case WM_CREATE:
//      nVscrollPos = nHscrollPos = 0;
	break;

  case WM_SIZE:
	GetClientRect (hWnd, &client);
	cxClient = RectWidth (client);;
	cyClient = RectHeight (client);
	// nVscrollMax is # lines that won't fit in client y area
	rangeTemp = ((int) (ThisText->numLines + 2) * (int) StatusLineHeight - cyClient) / (int) StatusLineHeight;
	nVscrollMax = max (0, rangeTemp);
	nVscrollPos = min (nVscrollPos, nVscrollMax);

	SetScrollRange (hWnd, SB_VERT, 0, nVscrollMax, FALSE);
	SetScrollPos (hWnd, SB_VERT, nVscrollPos, TRUE);

	// nHscrollMax is # chars that won't fit in client x area
	rangeTemp = ((int) (ThisText->maxLineLen + 2) * (int) StatusCharWidth - cxClient) / (int) StatusCharWidth;
	nHscrollMax = max (0, rangeTemp);
	nHscrollPos = min (nHscrollPos, nHscrollMax);

	SetScrollRange (hWnd, SB_HORZ, 0, nHscrollMax, FALSE);
	SetScrollPos (hWnd, SB_HORZ, nHscrollPos, TRUE);
	break;

  case WM_VSCROLL:
	switch (wParam) {
	case SB_TOP:
	  nVscrollInc = -nVscrollPos;
	  break;

	case SB_BOTTOM:
	  nVscrollInc = nVscrollMax - nVscrollPos;
	  break;

	case SB_LINEUP:
	  nVscrollInc = -1;
	  break;

	case SB_LINEDOWN:
	  nVscrollInc = 1;
	  break;

	case SB_PAGEUP:
	  nVscrollInc = min (-1, -cyClient / (int) StatusLineHeight);
	  break;

	case SB_PAGEDOWN:
	  nVscrollInc = max (1, cyClient / (int) StatusLineHeight);
	  break;

	case SB_THUMBTRACK:
	  nVscrollInc = LOWORD (lParam) - nVscrollPos;
	  break;

	default:
	  nVscrollInc = 0;
	}
	nVscrollInc = max (-nVscrollPos,
					   min (nVscrollInc, nVscrollMax - nVscrollPos));

	if (nVscrollInc != 0) {
	  nVscrollPos += nVscrollInc;
	  ScrollWindow (hWnd, 0, -1 * (int) StatusLineHeight * nVscrollInc, NULL, NULL);
	  SetScrollPos (hWnd, SB_VERT, nVscrollPos, TRUE);

	  UpdateWindow (hWnd);
	}
	break;

  case WM_HSCROLL:
	switch (wParam) {
	case SB_LINEUP:
	  nHscrollInc = -1;
	  break;

	case SB_LINEDOWN:
	  nHscrollInc = 1;
	  break;

	case SB_PAGEUP:
	  nHscrollInc = -8;
	  break;

	case SB_PAGEDOWN:
	  nHscrollInc = 8;
	  break;

	case SB_THUMBPOSITION:
	  nHscrollInc = LOWORD (lParam) - nHscrollPos;
	  break;
	default:
	  nHscrollInc = 0;
	}
	nHscrollInc = max (-nHscrollPos,
					   min (nHscrollInc, nHscrollMax - nHscrollPos));

	if (nHscrollInc != 0) {
	  nHscrollPos += nHscrollInc;
	  ScrollWindow (hWnd, -1 * (int) StatusCharWidth * nHscrollInc, 0, NULL, NULL);
	  SetScrollPos (hWnd, SB_HORZ, nHscrollPos, TRUE);

	}
	break;

  case WM_PAINT:
	if (ThisText->numLines == 0)
	  break;

	hDC = BeginPaint (hWnd, &ps);
	SetBkColor (hDC, StatusBackgroundColor);
	SetTextColor (hDC, StatusTextColor);
	hOldFont = SelectObject (hDC, hStatusFont);

	nPaintBeg = max (0, (nVscrollPos + ps.rcPaint.top / (int) StatusLineHeight - 1));
	nPaintEnd = min ((int) ThisText->numLines,
				(nVscrollPos + ps.rcPaint.bottom / (int) StatusLineHeight));

	SetTextAlign (hDC, TA_LEFT | TA_TOP);

	for (i = nPaintBeg; i < nPaintEnd; i++) {
	  x = (int) StatusCharWidth *(1 - nHscrollPos);
	  y = (int) StatusLineHeight *(1 - nVscrollPos + i);

	  TextOut (hDC, x, y, TextBlockLine (ThisText, i),
			   lstrlen (TextBlockLine (ThisText, i)));
	}

/*      GetClientRect (hWnd, &client);
   cyClient = client.bottom - client.top;
   cxClient = client.right - client.left;
 */
	// nVscrollMax is # lines that won't fit in client y area
	rangeTemp = ((int) (ThisText->numLines + 2) * (int) StatusLineHeight - cyClient) / (int) StatusLineHeight;
	nVscrollMax = max (0, rangeTemp);
	SetScrollRange (hWnd, SB_VERT, 0, nVscrollMax, TRUE);
	// nHscrollMax is # chars that won't fit in client x area
	rangeTemp = ((int) (ThisText->maxLineLen + 2) * (int) StatusCharWidth - cxClient) / (int) StatusCharWidth;
	nHscrollMax = max (0, rangeTemp);
	SetScrollRange (hWnd, SB_HORZ, 0, nHscrollMax, TRUE);

	SelectObject (hDC, hOldFont);
	EndPaint (hWnd, &ps);
	break;

  case WM_KEYDOWN:
	/* See if this key should be mapped to a scrolling event
	   * for which we have programmed the mouse.  If so,
	   * construct the appropriate mouse call and call the mouse code.
	 */

	CtrlState = GetKeyState (VK_CONTROL) < 0;
	for (i = 0; i < NUMKEYS; i++) {
	  if (wParam == key2scroll[i].wVirtKey &&
		  CtrlState == key2scroll[i].CtlState) {
		SendMessage (hWnd, key2scroll[i].iMessage,
					 key2scroll[i].wRequest, 0L);
	  }
	}
	break;

  case WM_CLOSE:
	if (CodingState && ThisText->IsBusy)
	  MessageBox (hWnd,
				  "Please wait until en/decoding is complete",
				  "Cannot close status window", MB_OK | MB_ICONSTOP);
	else
	  DestroyWindow (hWnd);
	break;

  case WM_DESTROY:
	for (i = ThisTextNum; i < NumStatusTexts; i++)
	  CodingStatusText[i] = CodingStatusText[i + 1];
	FreeTextBlock (ThisText);
	NumStatusTexts--;
	break;

  default:
	return (DefWindowProc (hWnd, message, wParam, lParam));
  }

  return (0);
}
