/******************************************************************************/
/*                                                                            */
/*  MSG_PROC - The callback for main-window message processing                */
/*                                                                            */
/******************************************************************************/

#define STRICT
#include <windows.h>
#include <commctrl.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include <malloc.h>
#include <new.h>
#include <float.h>

#include "const.h"
#include "classes.h"
#include "extern.h"
#include "funcdefs.h"
#include "julia.rh"

extern HINSTANCE hInst ;       // Global copy of instance handle
static HDC mag_dc = NULL ;     // Main window DC during magnification
static POINT mag_rect[5] ;     // For using PolyLine to draw rectangle
static int mag_top, mag_bottom, mag_left, mag_right ; // Displayed rectangle
static int mouse_row, mouse_col ; // Current mouse position

LRESULT WINAPI MsgProc( HWND hWnd, UINT cbmsg, WPARAM wParam, LPARAM lParam )
{
   int menu_state, ret_val ;
   char filename[512], msg[256] ;
   RECT rect ;

   switch( cbmsg ) {

      case WM_COMMAND :

         switch ( LOWORD ( wParam )) {

            case IDM_EXECUTE:
               if (disallow_menu)
                  return 0 ;
               if (RowResolution < 10)
                  MessageBox ( hWnd , "Number of rows must be at least 10" ,
                     "ERROR" , MB_ICONSTOP | MB_OK ) ;
               if (ColResolution < 10)
                  MessageBox ( hWnd , "Number of columns must be at least 10" ,
                     "ERROR" , MB_ICONSTOP | MB_OK ) ;
               if (DwellLimit <= 0)
                  MessageBox ( hWnd , "Dwell limit must be positive" ,
                     "ERROR" , MB_ICONSTOP | MB_OK ) ;
               else if (EscapeConstant < 2.0)
                  MessageBox ( hWnd , "Escape must be at least 2.0" ,
                     "ERROR" , MB_ICONSTOP | MB_OK ) ;
               else if (EscapeConstant > 99999.0)
                  MessageBox ( hWnd , "Escape must not exceed 99999" ,
                     "ERROR" , MB_ICONSTOP | MB_OK ) ;
               else if ((MagR <= 0.0)  ||  (MagI <= 0.0))
                  MessageBox ( hWnd , "Magnification must be positive" ,
                     "ERROR" , MB_ICONSTOP | MB_OK ) ;
               else if ((MagR > 1.e15)  ||  (MagI > 1.e15))
                  MessageBox ( hWnd , "Magnification must not exceed 1.e15" ,
                     "ERROR" , MB_ICONSTOP | MB_OK ) ;
               else if ((MaxR <= MinR)  ||  (MaxI <= MinI))
                  MessageBox ( hWnd , "Max must exceed min" ,
                     "ERROR" , MB_ICONSTOP | MB_OK ) ;
               else if ((Elevation < 0.0)  ||  (Elevation > 90.0))
                  MessageBox ( hWnd , "Elevation must be 0-90 degrees" ,
                     "ERROR" , MB_ICONSTOP | MB_OK ) ;
               else if ((Azimuth < 0.0)  ||  (Azimuth > 360.0))
                  MessageBox ( hWnd , "Elevation must be 0-360 degrees" ,
                     "ERROR" , MB_ICONSTOP | MB_OK ) ;
               else if ((CmapStart < 0.0)  ||  (CmapStart > 360.0))
                  MessageBox ( hWnd , "Color map start must be 0-360 degrees" ,
                     "ERROR" , MB_ICONSTOP | MB_OK ) ;
               else if (CmapNdwells < 1)
                  MessageBox ( hWnd , "Color map must have at least one dwell" ,
                     "ERROR" , MB_ICONSTOP | MB_OK ) ;
               else if ((CmapFloor < 0.0)  ||  (CmapFloor > 100.0))
                  MessageBox ( hWnd , "Color map floor must be 0-100 percent" ,
                     "ERROR" , MB_ICONSTOP | MB_OK ) ;
               else {
                  disallow_menu = 1 ;
                  execute () ;
                  disallow_menu = 0 ;
                  }
               return 0 ; // Case IDM_EXECUTE under WM_COMMAND ;

            case IDM_EXIT:
               PostMessage ( hWnd , WM_CLOSE , 0 , 0 ) ; // 'X' also posts WM_CLOSE
               return 0 ; // Case IDM_EXIT under WM_COMMAND ;

            case IDM_PARAMS:
               if (disallow_menu)
                  return 0 ;
               if (! DialogBox ( hInst , "DialogBoxParams" , hWnd , DialogParams ))
                  return 0 ;  // User canceled
               if (dwells != NULL) {
                  FREE ( dwells ) ;
                  dwells = NULL ;
                  }
               if (raw_image != NULL) {
                  FREE ( raw_image ) ;
                  raw_image = NULL ;
                  }
               if (dib != NULL) {   // If a device-independent-bitmap already exists,
                  delete dib ;      // Delete it
                  dib = NULL ;
                  }
               DisplayedObjectType = 0 ;
               return 0 ; // Case IDM_PARAMS under WM_COMMAND ;

            case IDM_COLORMAP:
               if (disallow_menu)
                  return 0 ;
               DialogBox ( hInst , "DialogBoxColorMap" , hWnd , DialogColorMap ) ;
               if (dib != NULL) { // If image is already displayed
                  if (CmapNcolors)
                     colormap () ;
                  else
                     bw_map () ;
                  delete dib ;
                  dib = new DIBimage ( RowResolution , ColResolution , 3 , raw_image ) ;
                  InvalidateRect ( hwndMain , NULL , TRUE ) ;
                  }
               else
                  MessageBox ( hWnd , "No image exists; parameters changed?" ,
                     "Information" , MB_OK ) ;
               return 0 ; // Case IDM_COLORMAP under WM_COMMAND ;

            case IDM_MAGNIFY:
               if (disallow_menu)
                  return 0 ;
               if (hwndMagnify == NULL  &&  dib != NULL)
                  hwndMagnify = CreateDialog ( hInst , "DialogBoxMagnify" ,
                                               hwndMain , DialogMagnify ) ;
               return 0 ; // Case IDM_PARAMS under WM_COMMAND ;

            case IDM_PRINT:
               if (disallow_menu)
                  return 0 ;
               ret_val = (int) DialogBox ( hInst , "DialogBoxPrintParams" ,
                                           hWnd , DialogPrintParams ) ;
               if (! ret_val)
                  return 0 ;  // User canceled
               if (initialize_for_printing ( hWnd )) // Err setting up printer?
                  return 0 ;
               disallow_menu = 1 ;
               dib->draw ( hWnd , 1 ) ;
               end_printing ( hWnd ) ;
               disallow_menu = 0 ;
               return 0 ;

            case IDM_SAVE_IMAGE:
               filename[0] = 0 ;
               if (! save_img_file ( hWnd , filename )) // Get file name
                  return 0 ;
               sprintf_s ( msg, "Save JULIA image file %s", filename ) ;
               audit ( msg ) ;
               disallow_menu = 1 ;
               if (write_bmp ( filename , 3 , RowResolution , ColResolution , raw_image ))
                  MessageBox ( hWnd , "Error writing image file... See JULIA.LOG" ,
                     "ERROR" , MB_ICONSTOP | MB_OK ) ;
               disallow_menu = 0 ;
               return 0 ;

            case IDM_ABOUT:
               if (disallow_menu)
                  return 0 ;
               DialogBox ( hInst , "DialogBoxAbout" , hWnd , DialogAbout ) ;
               return 0 ; // Case IDM_ABOUT under WM_COMMAND ;

            default:
               break ;
            }

      case WM_PAINT:
         if (! IsIconic ( hWnd ))
            paint_main_window ( hWnd ) ;
         else
            ValidateRect ( hWnd , NULL ) ;
         break ;

      case WM_SIZE:
//         InvalidateRect ( hWnd , NULL , TRUE ) ; // Not needed: CS_H/VREDRAW
         break ;

      case WM_LBUTTONDOWN:
      case WM_RBUTTONDOWN:
         if (dib == NULL)         // If no image is displayed
            break ;               // Ignore this press
         if (hwndMagnify == NULL) // If magnify not selected by user
            break ;               // Ignore this press
         if (mag_dc != NULL)      // If the right button is already down
            break ;               // Ignore this press
         mag_dc = GetDC ( hWnd ) ;   // We'll keep this during the entire magnification
         assert ( mag_dc != NULL ) ; // Then release it when the mouse button is released
         SetCapture ( hWnd ) ;       // Keep full control of the mouse
         SetMapMode ( mag_dc , MM_TEXT ) ; // Coordinates are in pixels
         SetROP2 ( mag_dc , R2_NOT ) ;     // Inverts colors so another invert restores
         GetClientRect ( hwndMain , &rect ) ;
         mouse_row = HIWORD ( lParam ) ; // Mouse is here relative to client window
         mouse_col = LOWORD ( lParam ) ;
         // Compute the client window coordinates of the magnification rectangle
         // Recall that the row increases for lower pixels (the image is inverted)
         mag_left = (int) ((MagMinR - MinR) / (MaxR - MinR) * (DisplayedCols-1)) ;
         mag_right = (int) ((MagMaxR - MinR) / (MaxR - MinR) * (DisplayedCols-1)) ;
         mag_bottom = (int) ((MagMinI - MinI) / (MaxI - MinI) * (DisplayedRows-1)) ;
         mag_top = (int) ((MagMaxI - MinI) / (MaxI - MinI) * (DisplayedRows-1)) ;
         mag_bottom = DisplayedRows - mag_bottom - 1 ;
         mag_top = DisplayedRows - mag_top - 1 ;
         // This is used to draw the polyline
         mag_rect[0].x = mag_left ;
         mag_rect[0].y = mag_top ;
         mag_rect[1].x = mag_right ;
         mag_rect[1].y = mag_top ;
         mag_rect[2].x = mag_right ;
         mag_rect[2].y = mag_bottom ;
         mag_rect[3].x = mag_left ;
         mag_rect[3].y = mag_bottom ;
         mag_rect[4].x = mag_left ;
         mag_rect[4].y = mag_top ;
         Polyline ( mag_dc , mag_rect , 5 ) ;
         show_magnif () ;
         // Put the info in the dialog box
         break ;   // Case WM_LBUTTONDOWN, WM_RBUTTONDOWN

      case WM_LBUTTONUP:
      case WM_RBUTTONUP:
         if (mag_dc == NULL)      // If we have not been doing a magnify
            break ;               // Ignore this release
         assert ( hwndMagnify != NULL ) ;
         assert ( dib != NULL ) ;
         // Drawing the same rectangle again erases it
         mag_rect[0].x = mag_left ;
         mag_rect[0].y = mag_top ;
         mag_rect[1].x = mag_right ;
         mag_rect[1].y = mag_top ;
         mag_rect[2].x = mag_right ;
         mag_rect[2].y = mag_bottom ;
         mag_rect[3].x = mag_left ;
         mag_rect[3].y = mag_bottom ;
         mag_rect[4].x = mag_left ;
         mag_rect[4].y = mag_top ;
         Polyline ( mag_dc , mag_rect , 5 ) ;
         ReleaseCapture () ;
         ReleaseDC ( hWnd , mag_dc ) ;
         mag_dc = NULL ;
         break ;   // Case WM_LBUTTONUP, WM_RBUTTONUP

      case WM_MOUSEMOVE:
         if (mag_dc == NULL)      // If we have not been doing a magnify
            break ;               // Ignore this move
         assert ( hwndMagnify != NULL ) ;
         assert ( dib != NULL ) ;
         // Drawing the same rectangle again erases it
         mag_rect[0].x = mag_left ;
         mag_rect[0].y = mag_top ;
         mag_rect[1].x = mag_right ;
         mag_rect[1].y = mag_top ;
         mag_rect[2].x = mag_right ;
         mag_rect[2].y = mag_bottom ;
         mag_rect[3].x = mag_left ;
         mag_rect[3].y = mag_bottom ;
         mag_rect[4].x = mag_left ;
         mag_rect[4].y = mag_top ;
         Polyline ( mag_dc , mag_rect , 5 ) ;
         // Compute the new real/imaginary coordinates corresponding to the mouse move
         MagMaxR += (MaxR - MinR) * (double) (LOWORD(lParam) - mouse_col) / (double) DisplayedCols ;
         MagMinI -= (MaxI - MinI) * (double) (HIWORD(lParam) - mouse_row) / (double) DisplayedRows ;
         if (wParam & MK_LBUTTON) { // Left button moves entire rectangle
            MagMinR += (MaxR - MinR) * (double) (LOWORD(lParam) - mouse_col) / (double) DisplayedCols ;
            MagMaxI -= (MaxI - MinI) * (double) (HIWORD(lParam) - mouse_row) / (double) DisplayedRows ;
            // Keep the rectangle within bounds
            if (MagMinR < MinR) {
               MagMaxR += MinR - MagMinR ;
               MagMinR = MinR ;
               }
            if (MagMaxR > MaxR) {
               MagMinR -= MagMaxR - MaxR ;
               MagMaxR = MaxR ;
               }
            if (MagMinI < MinI) {
               MagMaxI += MinI - MagMinI ;
               MagMinI = MinI ;
               }
            if (MagMaxI > MaxI) {
               MagMinI -= MagMaxI - MaxI ;
               MagMaxI = MaxI ;
               }
            }
         // Prevent user from shrinking rectangle too small
         // Or from going out of bounds
         if (wParam & MK_RBUTTON) {
            if ((MagMaxR - MagMinR) < 0.01 * (MaxR - MinR))
               MagMaxR = MagMinR + 0.01 * (MaxR - MinR) ;
            if ((MagMaxI - MagMinI) < 0.01 * (MaxI - MinI))
               MagMinI = MagMaxI - 0.01 * (MaxI - MinI) ;
            if (MagMaxR > MaxR)
               MagMaxR = MaxR ;
            if (MagMinI < MinI)
               MagMinI = MinI ;
            }
         if (MagMaxR > MaxR)
            MagMaxR = MaxR ;
         if (MagMinI < MinI)
            MagMinI = MinI ;
         if (MagMaxI > MaxI)
            MagMaxI = MaxI ;
         mouse_row = HIWORD ( lParam ) ; // Mouse is here relative to client window
         mouse_col = LOWORD ( lParam ) ;
         // Compute the new rectangle coordinates based on the real/imaginary rectangle
         // This avoids anomalies due to minor computation problems
         mag_left = (int) ((MagMinR - MinR) / (MaxR - MinR) * (DisplayedCols-1)) ;
         mag_right = (int) ((MagMaxR - MinR) / (MaxR - MinR) * (DisplayedCols-1)) ;
         mag_bottom = (int) ((MagMinI - MinI) / (MaxI - MinI) * (DisplayedRows-1)) ;
         mag_top = (int) ((MagMaxI - MinI) / (MaxI - MinI) * (DisplayedRows-1)) ;
         mag_bottom = DisplayedRows - mag_bottom - 1 ;
         mag_top = DisplayedRows - mag_top - 1 ;
         // Draw the new rectangle
         mag_rect[0].x = mag_left ;
         mag_rect[0].y = mag_top ;
         mag_rect[1].x = mag_right ;
         mag_rect[1].y = mag_top ;
         mag_rect[2].x = mag_right ;
         mag_rect[2].y = mag_bottom ;
         mag_rect[3].x = mag_left ;
         mag_rect[3].y = mag_bottom ;
         mag_rect[4].x = mag_left ;
         mag_rect[4].y = mag_top ;
         Polyline ( mag_dc , mag_rect , 5 ) ;
         show_magnif () ;
         break ;   // Case WM_MOUSEMOVE

      case WM_INITMENUPOPUP :
         if (lParam == MENU_FILE_POS) { // If the File popup menu
            menu_state = Executing ? MF_GRAYED : MF_ENABLED ;
            EnableMenuItem ( (HMENU) wParam , IDM_EXIT , menu_state ) ;
            EnableMenuItem ( (HMENU) wParam , IDM_EXECUTE , menu_state ) ;
            EnableMenuItem ( (HMENU) wParam , IDM_PRINT , menu_state ) ;
            EnableMenuItem ( (HMENU) wParam , IDM_SAVE_IMAGE , menu_state ) ;
            if (dib == NULL  ||  raw_image == NULL  ||  DisplayedObjectType == 0) {
               EnableMenuItem ( (HMENU) wParam , IDM_PRINT , MF_GRAYED ) ;
               EnableMenuItem ( (HMENU) wParam , IDM_SAVE_IMAGE , MF_GRAYED ) ;
               }
            }
         else if (lParam == MENU_PARAMS_POS) { // If the Parameters popup menu
            menu_state = Executing ? MF_GRAYED : MF_ENABLED ;
            EnableMenuItem ( (HMENU) wParam , IDM_PARAMS , menu_state ) ;
            EnableMenuItem ( (HMENU) wParam , IDM_COLORMAP , menu_state ) ;
            EnableMenuItem ( (HMENU) wParam , IDM_MAGNIFY , menu_state ) ;
            if (dib == NULL  ||  raw_image == NULL  ||  DisplayedObjectType > 2) {
               EnableMenuItem ( (HMENU) wParam , IDM_COLORMAP , MF_GRAYED ) ;
               EnableMenuItem ( (HMENU) wParam , IDM_MAGNIFY , MF_GRAYED ) ;
               }
            }
         break ;

      case WM_CLOSE:
         // Optionally confirm user's desire to quit here
         DestroyWindow ( hWnd ) ;
         return 0 ;

      case WM_DESTROY:
         // Cleanup window-specific things here
         PostQuitMessage ( 0 ) ;   // Ends message loop
         return 0 ;
      }

    return DefWindowProc ( hWnd , cbmsg , wParam , lParam ) ;
}
