/*===========================================================================*/
/*                                                                           */
/* File    : ICO.C                                                           */
/*                                                                           */
/* Purpose : Main routine for the IKE icon editor.                           */
/*                                                                           */
/* History :                                                                 */
/*                                                                           */
/* (C) Copyright 1991 Marc Adler/Magma Systems     All Rights Reserved       */
/*===========================================================================*/

#include <memory.h>
#include <string.h>
#include <windows.h>
#include "ico.h"

HWND   hWndMain;
HWND   hWndEdit;
HWND   hWndDebug;
HWND   hWndActualImage;
HANDLE hInst = 0;
HANDLE hAccTable;

ICONHEADER IconHeader;
ICONDIRECTORY IconDir;

ICONINFO IconInfo =
{
  NULL, NULL, 0, '\0'
};

BYTE rgb[16][3] =
{ /* B     G     R   */
  { 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x80 },
  { 0x00, 0x80, 0x00 },
  { 0x00, 0x80, 0x80 },
  { 0x80, 0x00, 0x00 },
  { 0x80, 0x00, 0x80 },
  { 0x80, 0x80, 0x00 },
  { 0x80, 0x80, 0x80 },
  { 0xC0, 0xC0, 0xC0 },
  { 0x00, 0x00, 0xFF },
  { 0x00, 0xFF, 0x00 },
  { 0x00, 0xFF, 0xFF },
  { 0xFF, 0x00, 0x00 },
  { 0xFF, 0x00, 0xFF },
  { 0xFF, 0xFF, 0x00 },
  { 0xFF, 0xFF, 0xFF }
};



/****************************************************************************/
/*                                                                          */
/* Function : WinMain()                                                     */
/*                                                                          */
/* Purpose  : Entry point for a Windows app.                                */
/*                                                                          */
/* Returns  :                                                               */
/*                                                                          */
/****************************************************************************/
int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
  HANDLE hInstance;
  HANDLE hPrevInstance;
  LPSTR lpCmdLine;
  int nCmdShow;
{
  HWND hWnd;
  MSG  msg;
  char szFileName[80];

  if (!hPrevInstance)		/* Has application been initialized? */
    if (!ICOViewInit(hInstance))
      return (NULL);		/* Exits if unable to initialize     */
  hInst = hInstance;		/* Saves the current instance	     */

  /*
    Create the main window.
  */
  hWnd = CreateWindow("ICOView",
		      "ICOView",
		      WS_OVERLAPPEDWINDOW,
		      CW_USEDEFAULT,
		      CW_USEDEFAULT,
#ifdef DEBUG
		      600,
#else
                      520,
#endif
		      424,
		      NULL,
		      NULL,
		      hInstance,
		      NULL);
  if (!(hWndMain = hWnd))
    return (NULL);

#ifdef DEBUG
  /*
    Create a debugging window.
  */
  hWndDebug = CreateWindow("ICODbg",
		      "ICODbg",
		      WS_OVERLAPPEDWINDOW | WS_CHILD | 
                      WS_VISIBLE | WS_CLIPSIBLINGS,
		      280, 0, 400, 400,
		      hWnd,
		      16,
		      hInstance,
		      NULL);
#endif


  /*
    Create the icon editing window
  */
  hWndEdit = CreateWindow("IconEdit",
  		          (LPSTR) "Edit",
		          WS_CHILD | WS_BORDER | WS_VISIBLE,
		          5, 2,
                          32*8 + 2*GetSystemMetrics(SM_CXBORDER),
                          32*8 + 2*GetSystemMetrics(SM_CYBORDER),
		          hWnd,
		          1,
		          hInstance,
		          (LPSTR) NULL);
  if (!hWndEdit)
  {
    MessageBox(hWnd, "Can't create hWndEdit", NULL, MB_OK);
    return NULL;
  }


  /*
    Create the window which will hold the actual icom image (32x32)
  */
  hWndActualImage = CreateWindow("IconImage",
         		          (LPSTR) NULL,
		                  WS_CHILD | WS_BORDER | WS_VISIBLE,
		                  300, 100,
                                  32 + 2*GetSystemMetrics(SM_CXBORDER),
                                  32 + 2*GetSystemMetrics(SM_CYBORDER),
		                  hWnd,
		                  2,
		                  hInstance,
		                  (LPSTR) NULL);
  if (!hWndActualImage)
  {
    MessageBox(hWnd, "Can't create hWndActualImage", NULL, MB_OK);
    return NULL;
  }

  /*
    Initialize the toolbox
  */
  ToolboxInit(hWndMain);

  /*
    The Paste option is initially disabled since there is nothing in
    the paste buffer. (An enhancement would be to make the paste buffer
    shared between instances.)
  */
  EnableMenuItem(GetMenu(hWndMain), IDM_PASTE, 
                 MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);

  /*
    Create a blank icon to start off with.
  */
  NewIcon();

  /*
    Read in any icon file which may have been passed on the command line.
  */
  if (lpCmdLine && *lpCmdLine)
  {
    lstrcpy(szFileName, lpCmdLine);
    if (strchr(szFileName, '.') == NULL)
      strcat(szFileName, ".ico");
    ICORead(hWndEdit, szFileName);
  }

  /*
    Main message loop...
  */
  while (GetMessage(&msg, NULL, NULL, NULL))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return (msg.wParam);
}



/****************************************************************************/
/*                                                                          */
/* Function : ICOViewInit()                                                 */
/*                                                                          */
/* Purpose  : Registers all of the classes used by the icon editor.         */
/*                                                                          */
/* Returns  : TRUE if successful, FALSE if not.                             */
/*                                                                          */
/****************************************************************************/
BOOL ICOViewInit(hInstance)
  HANDLE hInstance;
{
  HANDLE hMemory;
  WNDCLASS *pWndClass;
  BOOL bSuccess;

  hMemory = LocalAlloc(LPTR, sizeof(WNDCLASS));

  pWndClass = (PWNDCLASS) LocalLock(hMemory);
  pWndClass->hCursor       = LoadCursor(NULL, IDC_ARROW);
  pWndClass->hIcon         = LoadIcon(hInstance, "IKE");
  pWndClass->lpszMenuName  = (LPSTR) "IconEdit";
  pWndClass->lpszClassName = (LPSTR) "ICOView";
  pWndClass->hbrBackground = GetStockObject(WHITE_BRUSH);
  pWndClass->hInstance     = hInstance;
  pWndClass->style         = NULL;
  pWndClass->lpfnWndProc   = ICOViewWndProc;
  bSuccess = RegisterClass(pWndClass);
  if (!bSuccess)
    return FALSE;

  pWndClass->hCursor       = (HICON) NULL;
  pWndClass->hIcon         = LoadIcon(NULL, IDI_APPLICATION);
  pWndClass->lpszMenuName  = (LPSTR) NULL;
  pWndClass->lpszClassName = (LPSTR) "IconEdit";
  pWndClass->hbrBackground = GetStockObject(WHITE_BRUSH);
  pWndClass->hInstance     = hInstance;
  pWndClass->style         = NULL;
  pWndClass->lpfnWndProc   = ICOEditWndProc;
  bSuccess = RegisterClass(pWndClass);
  if (!bSuccess)
    return FALSE;

  pWndClass->hCursor       = LoadCursor(NULL, IDC_ARROW);
  pWndClass->hIcon         = LoadIcon(NULL, IDI_APPLICATION);
  pWndClass->lpszMenuName  = (LPSTR) NULL;
  pWndClass->lpszClassName = (LPSTR) "IconImage";
  pWndClass->hbrBackground = GetStockObject(WHITE_BRUSH);
  pWndClass->hInstance     = hInstance;
  pWndClass->style         = NULL;
  pWndClass->lpfnWndProc   = ICOImageWndProc;
  bSuccess = RegisterClass(pWndClass);
  if (!bSuccess)
    return FALSE;

  pWndClass->hCursor       = LoadCursor(NULL, IDC_ARROW);
  pWndClass->hIcon         = LoadIcon(NULL, IDI_APPLICATION);
  pWndClass->lpszMenuName  = (LPSTR) NULL;
  pWndClass->lpszClassName = (LPSTR) "ToolBox";
  pWndClass->hbrBackground = GetStockObject(WHITE_BRUSH);
  pWndClass->hInstance     = hInstance;
  pWndClass->style         = NULL;
  pWndClass->lpfnWndProc   = ToolBoxWndProc;
  pWndClass->cbWndExtra    = sizeof(HANDLE);
  bSuccess = RegisterClass(pWndClass);
  if (!bSuccess)
    return FALSE;

#ifdef DEBUG
  pWndClass->hCursor       = (HICON) NULL;
  pWndClass->hIcon         = LoadIcon(NULL, IDI_APPLICATION);
  pWndClass->lpszMenuName  = (LPSTR) NULL;
  pWndClass->lpszClassName = (LPSTR) "IcoDbg";
  pWndClass->hbrBackground = GetStockObject(WHITE_BRUSH);
  pWndClass->hInstance     = hInstance;
  pWndClass->style         = NULL;
  pWndClass->lpfnWndProc   = ICODebugWndProc;
  bSuccess = RegisterClass(pWndClass);
  if (!bSuccess)
    return FALSE;
#endif

  LocalUnlock(hMemory);
  LocalFree(hMemory);
  return bSuccess;
}


/****************************************************************************/
/*                                                                          */
/* Function : NewIcon()                                                     */
/*                                                                          */
/* Purpose  : Initializes a brand new icon.                                 */
/*                                                                          */
/* Returns  : Nothing.                                                      */
/*                                                                          */
/****************************************************************************/
VOID PASCAL NewIcon(void)
{
  LPSTR lpBits;
  LPBITMAPINFO lpbmi;

  /*
    Set up the icon header
  */
  IconHeader.icoReserved      = 0;
  IconHeader.icoResourceType  = 1;
  IconHeader.icoResourceCount = 1;

  /*
    Set up a directory entry for a single icon
  */
  IconDir.Width        = 32;
  IconDir.Height       = 32;
  IconDir.ColorCount   = 16;
  IconDir.bReserved1   = 0;
  IconDir.bReserved2   = 0;
  IconDir.bReserved3   = 0;
  IconDir.icoDIBSize   = 744;
  IconDir.icoDIBOffset = 22;

  /*
    Set up the icon info structure
  */
  if (IconInfo.hBitmapBits)
    GlobalFree(IconInfo.hBitmapBits);
  if (IconInfo.hBitmapInfo)
    GlobalFree(IconInfo.hBitmapInfo);
  IconInfo.hIcon         = NULL;
  IconInfo.hBitmapBits   = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, 32L*32L);
  IconInfo.fFlags        = 0;
  IconInfo.szFilename[0] = '\0';

  /*
    The initial pixmap contains all FF's (ie - it's all white)
  */
  lpBits = GlobalLock(IconInfo.hBitmapBits);
  _fmemset(lpBits, 0xFF, 32*32);
  GlobalUnlock(IconInfo.hBitmapBits);

  IconInfo.hBitmapInfo = GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, 
          (DWORD) sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD));
  lpbmi = (LPBITMAPINFO) GlobalLock(IconInfo.hBitmapInfo);
  lpbmi->bmiHeader.biSize          = (long) sizeof(BITMAPINFOHEADER);
  lpbmi->bmiHeader.biWidth         = IconDir.Width;
  lpbmi->bmiHeader.biHeight        = IconDir.Height;
  lpbmi->bmiHeader.biPlanes        = 1;
  lpbmi->bmiHeader.biBitCount      = ColorCountToPlanes(IconDir.ColorCount);
  lpbmi->bmiHeader.biCompression   = BI_RGB;
  lpbmi->bmiHeader.biSizeImage     = 0L;
  lpbmi->bmiHeader.biXPelsPerMeter = 0;
  lpbmi->bmiHeader.biYPelsPerMeter = 0;
  lpbmi->bmiHeader.biClrUsed       = 0;
  lpbmi->bmiHeader.biClrImportant  = 0;

  /*
    Initialize the colors
  */
  if (IconDir.ColorCount == 2)
  {
    _fmemcpy((LPSTR) &lpbmi->bmiColors[0], rgb[0], 3);
    _fmemcpy((LPSTR) &lpbmi->bmiColors[1], rgb[7], 3);
  }
  else
  {
    int i;
    for (i = 0;  i < 16;  i++)
      _fmemcpy((LPSTR) &lpbmi->bmiColors[i],   rgb[i], 3);
  }
  GlobalUnlock(IconInfo.hBitmapInfo);

  /*
    No undo info yet...
  */
  EnableMenuItem(GetMenu(hWndMain), IDM_UNDO, 
                 MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
}




/****************************************************************************/
/*                                                                          */
/*                    UTILITY ROUTINES                                      */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

/****************************************************************************/
/*                                                                          */
/* Function : ColorCountToPlanes()                                          */
/*                                                                          */
/* Purpose  : Given a number of colors, returns the number of color planes  */
/*            needed to represent those colors (log2(Colors))               */
/*                                                                          */
/* Returns  : The number of planes in the pixmap.                           */
/*                                                                          */
/****************************************************************************/
int PASCAL ColorCountToPlanes(int nClr)
{
  switch (nClr)
  {
    case 2 :  return 1;
    case 8 :  return 3;
    case 16:  return 4;
    default:  return 1;
  }
}

/****************************************************************************/
/*                                                                          */
/* Function : SetIconPixel()                                                */
/*                                                                          */
/* Purpose  : Sets the nibble in the pixmap which corresponds to location   */
/*            (x,y) to the passed color.                                    */
/*                                                                          */
/* Returns  : Zippo.                                                        */
/*                                                                          */
/****************************************************************************/
VOID PASCAL SetIconPixel(int x, int y, int iColor)
{
  LPSTR lpBits, lpByte;
  int   nPixelsPerByte = 2;

  if (IconInfo.hBitmapBits == NULL)
    return;

  /*
    Dereference the pixmap and point to the proper location
  */
  lpBits = GlobalLock(IconInfo.hBitmapBits);
  lpByte = lpBits + ((31-y) * IconDir.Width / nPixelsPerByte) + 
                    (x / nPixelsPerByte);

  /*
    Set the left or right nibble, depending if the x value is odd or even.
  */
  if (x & 1)
    *lpByte = (*lpByte & 0xF0) | (BYTE) iColor;
  else
    *lpByte = (*lpByte & 0x0F) | ((BYTE) iColor << 4);
  GlobalUnlock(IconInfo.hBitmapBits);
}


/****************************************************************************/
/*                                                                          */
/* Function : GetIconPixel()                                                */
/*                                                                          */
/* Purpose  : Reads the pixel at location [x,y].                            */
/*                                                                          */
/* Returns  : The color of the pixel at location [x,y].                     */
/*                                                                          */
/****************************************************************************/
int PASCAL GetIconPixel(int x, int y)
{
  LPSTR lpBits, lpByte;
  int   nPixelsPerByte = 2;

  if (IconInfo.hBitmapBits == NULL)
    return 0;

  /*
    Dereference the pixmap and point to the proper location
  */
  lpBits = GlobalLock(IconInfo.hBitmapBits);
  lpByte = lpBits + ((31-y) * IconDir.Width / nPixelsPerByte) + 
                    (x / nPixelsPerByte);

  /*
    If it's an odd column, get the left nibble, else the right one.
  */
  if (x & 1)
    x = (*lpByte & 0xF0) >> 4;
  else
    x = (*lpByte & 0x0F);

  GlobalUnlock(IconInfo.hBitmapBits);
  return x;
}


/****************************************************************************/
/*                                                                          */
/* Function : IconPlotLine()                                                */
/*                                                                          */
/* Purpose  : This is a callback for the LineDDA(). We can set the          */
/*             pixel at (x,y) to the passed color.                          */
/*                                                                          */
/* Returns  : Zilch.                                                        */
/*                                                                          */
/****************************************************************************/
VOID FAR PASCAL IconPlotLine(int x, int y, LPSTR lpData)
{
  if ((x /= 8) > 31)
    x = 31;
  if ((y /= 8) > 31)
    y = 31;
  SetIconPixel(x, y, LOWORD(((LONG) lpData)));
}



/****************************************************************************/
/*                                                                          */
/* Function : DebugBitmap()                                                 */
/*                                                                          */
/* Purpose  : Dumps a hex representation of the pixmap to the debugging     */
/*            window.                                                       */
/*                                                                          */
/* Returns  : Nada.                                                         */
/*                                                                          */
/****************************************************************************/
#ifdef DEBUG
DebugBitmap(HWND hWnd, HANDLE hBits)
{
  int   x, y;
  LPSTR p;
  char  buf[8];
  HDC   hDC;
  TEXTMETRIC tm;

  p = GlobalLock(hBits);
  hDC = GetDC(hWnd);
  GetTextMetrics(hDC, (LPTEXTMETRIC) &tm);

  for (y = 0;  y < 32;  y++)
  {
    for (x = 0;  x < 16;  x++)
    {
      sprintf(buf, "%02x ", (unsigned char) *p++);
      TextOut(hDC, x*3 * tm.tmAveCharWidth, y * (tm.tmHeight),
                   (LPSTR) buf, 3);
    }
  }

  GlobalUnlock(hBits);
  ReleaseDC(hWnd, hDC);
}
#endif 
