/*
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
|          Program Name: WinSample                                                                                      |
|                Author: Michael Krämer                                                                                 |
|                        Michael@dk8pp.de               (private)                                                       |
|                        Michael.Kraemer@eu.necel.com   (business)                                                      |
|                  Date: 29.05.2011                                                                                     |
|              Language: Open Watcom C 1.9                                                                              |
|               Version: 0.2                                                                                            |
|        Program status: still testing                                                                                  |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
|  Purpose: Sample skeleton for Windows dialog                                                                          |
|                                                                                                                       |
|  History: V0.2 29.05.2011: adapted to stdint.h/stdbool.h. mytype.h removed                                            |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       Includes                                                                                                        |
+-----------------------------------------------------------------------------------------------------------------------+
*/
#include <stdio.h>
#include <windows.h>
#include <commctrl.h>
#include <time.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <tchar.h>
#include "stdbool.h"
#include "resource.h"
#include "WinLib.h"

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       Defines                                                                                                         |
+-----------------------------------------------------------------------------------------------------------------------+
*/
#define PARMSIZE 64             /* maximum size of a string or positional command line parameter */
#define STRINGSIZE 256          /* default size for strings */
#define SUSPENDBUTTON           /* define if a suspend button shall be supported */

#define MY_HKEY HKEY_CURRENT_USER // handle of registry key
#define MY_INI_SECT _T("WinSample") // Section name or registry key used by this program
#define MY_REG_KEY MY_REG_KEY_ROOT MY_INI_SECT // Registry key used by this program
#define PROGRAMNAME _T("Windows Dialog Skeleton")
#define PURPOSE _T("This program is a sample skeleton for creating Windows programs,\r\r\nwhich consist of just one dialog.\r\r\n\r\r\n")
#define PROGRAMVERSION 10       /* will be displayed as n.nn */
#define USER_THREAD_ENDED       401

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       structs and enums                                                                                               |
+-----------------------------------------------------------------------------------------------------------------------+
*/

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       global variables                                                                                                |
+-----------------------------------------------------------------------------------------------------------------------+
*/
HINSTANCE       MyInstance ;
HWND            MainWindow ;
static _TCHAR strng1[STRINGSIZE] ;
static _TCHAR strng2[STRINGSIZE] ;
static _TCHAR ini_filename[MAX_PATH+1] ;

/*
 Registry definitions
*/
static int32_t registry_x = 0 ;                         /* left edge of dialog */
static int32_t registry_y = 0 ;                         /* top edge of dialog */
static int32_t registry_width = 0 ;                     /* width of dialog */
static int32_t registry_height = 0 ;                    /* height of dialog */
static int32_t registry_version = PROGRAMVERSION ;      /* version of program which wrote the registry */
static _TCHAR registry_filename[STRINGSIZE+1] = _T("") ; /* filename for registry */
static INIVALUE inivalue[] = {{_T("Window"),   _T("PosX"),     REG_DWORD, REGFLAG_WRITEBACK, &registry_x,        1},
                              {_T("Window"),   _T("PosY"),     REG_DWORD, REGFLAG_WRITEBACK, &registry_y,        1},
                              {_T("Window"),   _T("Width"),    REG_DWORD, REGFLAG_WRITEBACK, &registry_width,    1},
                              {_T("Window"),   _T("Height"),   REG_DWORD, REGFLAG_WRITEBACK, &registry_height,   1},
                              {_T("Program"),  _T("Version"),  REG_DWORD, REGFLAG_NONE,      &registry_version,  1},
                              {_T("Settings"), _T("Filename"), REG_SZ,    REGFLAG_WRITEBACK, &registry_filename, STRINGSIZE}} ;
static MYINI myini = {MY_HKEY, MY_REG_KEY, MY_INI_SECT _T(".ini"), &inivalue[0], INI_NO, sizeof(inivalue)/sizeof(inivalue[0])} ;

/*
 Command line parameter definitions
*/
static _TCHAR posparm1[PARMSIZE+1] ;    /* positional parameter 1 */
static _TCHAR posparm2[PARMSIZE+1] ;    /* positional parameter 2 */
static _TCHAR strngp[PARMSIZE+1] ;      /* string parameter strng */

/* These are indexes into the CLO table below. Keep them in sync */
typedef enum {PARM_RC=0,
              PARM_RD,
              PARM_RR,
              PARM_RW,
              PARM_IF,
              PARM_ID,
              PARM_IR,
              PARM_IW,
              PARM_PP,
              PARM_PT,
              PARM_A,
              PARM_B,
              PARM_STRNG,
              PARM_POS1,
              PARM_POS2,
              PARM_LAST} parm_enum ;

CLO clo[PARM_LAST] = {{_T("rc"), PARM_SIMPLE, NULL, 0, false, _T("clear registry entries for this program")},
                      {_T("rd"), PARM_SIMPLE, NULL, 0, false, _T("disable use of the registry for this program\r\r\n")
                                                              _T("     (will use .ini file instead unless -id specified")},
                      {_T("rr"), PARM_SIMPLE, NULL, 0, false, _T("read initialization values from registry")},
                      {_T("rw"), PARM_SIMPLE, NULL, 0, false, _T("write initialization values back to registry")},
                      {_T("if"), PARM_STRING, ini_filename, MAX_PATH, false, _T("specify ini filename")},
                      {_T("id"), PARM_SIMPLE, NULL, 0, false, _T("disable use of ini file for this program")},
                      {_T("ir"), PARM_SIMPLE, NULL, 0, false, _T("read initialization values from ini file")},
                      {_T("iw"), PARM_SIMPLE, NULL, 0, false, _T("write initialization values back to ini file\r\r\n")
                                                              _T("     NOTE: avoid using registry and ini-file at the same time\r\r\n")
                                                              _T("     Exception: -rr -iw to export registry to ini-file and\r\r\n")
                                                              _T("                -ir -rw to export ini-file to registry")},
                      {_T("pp"), PARM_INT, NULL, 0, false,    _T("set process priority for dialog\r\r\n")
                                                              _T("       (0:idle, 1:normal, 2:high, 3:realtime)")},
                      {_T("pt"), PARM_INT, NULL, 0, false,    _T("set priority for processing thread\r\r\n")
                                                              _T("       (0:idle, 1:lowest, 2:below normal, 3:normal,\r\r\n")
                                                              _T("        4:above normal, 5:highest, 6:time critical)")},
                      {_T("a"),  PARM_INT, NULL, 0, false,    _T("test...")},
                      {_T("b"),  PARM_INT, NULL, 0, false,    _T("test...")},
                      {_T("strng"), PARM_STRING, &strngp, PARMSIZE+1, false, _T("test string")},
                      {NULL, PARM_POS, &posparm1, PARMSIZE+1, false, _T("test (first positional parameter)")},
                      {NULL, PARM_POS, &posparm2, PARMSIZE+1, false, _T("test (second positional parameter)")}} ;

uint32_t clo_size = PARM_LAST ;

static char errorparm[MAXPARMLENGTH] ;

/*
 Dialog coordinates definitions
*/
COORDS coordinates[] = {{0, 0, 0, 0, 0, 0},  /* first entry is reserved for main dialog */
#ifdef SUSPENDBUTTON
                        {BUT_SUSPEND, 0, 0, 0, 0, MOVE_X|MOVE_Y},
#endif
                        {DLG_STATUS, 0, 0, 0, 0, RESIZE_X|RESIZE_Y|MOVE_Y},
                        {DLG_OUTFILENAME, 0, 0, 0, 0, RESIZE_X},
                        {BUT_INFO, 0, 0, 0, 0, MOVE_Y},
                        {BUT_START, 0, 0, 0, 0, MOVE_X|MOVE_Y},
                        {BUT_EXIT, 0, 0, 0, 0, MOVE_X|MOVE_Y}
                       } ;
COORDS *sorted_coord[sizeof(coordinates)/sizeof(coordinates[0])-1] ;
uint32_t coord_size = sizeof(coordinates)/sizeof(coordinates[0]) ;
static bool validreg = false ;          /* valid data from registry retrieved */
static bool abort_thread = false ;      /* set true to abort the thread */
static bool thread_suspended = false ;
_TCHAR purpose[] = PURPOSE ;

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       Function prototypes                                                                                             |
+-----------------------------------------------------------------------------------------------------------------------+
*/
static INT_PTR CALLBACK WinSample(HWND window_handle, unsigned msg, WPARAM wparam, LPARAM lparam) ;
static BOOL CALLBACK DlgHelp(HWND window_handle, unsigned msg, WPARAM wparam, LPARAM lparam) ;
static DWORD WINAPI DoOperation(LPVOID lpParam) ;
static void EnableControls(bool enable) ;

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       WinMain                                                                                                         |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| MAIN program.                                                                                                         |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
int WINAPI WinMain(HINSTANCE this_inst, HINSTANCE prev_inst, LPSTR cmdline, int cmdshow)
{
uint32_t static volatile rv ;
bool startup ;
   startup = true ;
   errorparm[0] = '\0' ;
   getparms(cmdline, errorparm) ;
   if (clo[PARM_RC].found) clear_registry(&myini) ;
   myini.initype = INI_NO ;
   if (!clo[PARM_RD].found && !clo[PARM_RR].found && !clo[PARM_RW].found && !clo[PARM_IR].found && !clo[PARM_IW].found)
   { /* enable registry read/write if none of the previous parameters was found (as default and for compatibility) */
      myini.initype |= (INI_REGREAD + INI_REGWRITE) ;
   }
   if (clo[PARM_RD].found && !clo[PARM_RR].found && !clo[PARM_RW].found && !clo[PARM_IR].found && !clo[PARM_IW].found && !clo[PARM_ID].found)
   { /* enable ini file read/write if registry is disabled and none of the previous parameters was found
        use "-rd -id" to use neither registry nor ini file */
      myini.initype |= (INI_FILEREAD + INI_FILEWRITE) ;
   }
   if (clo[PARM_RR].found) myini.initype |= INI_REGREAD ;
   if (clo[PARM_RW].found) myini.initype |= INI_REGWRITE ;
   if (clo[PARM_IR].found) myini.initype |= INI_FILEREAD ;
   if (clo[PARM_IW].found) myini.initype |= INI_FILEWRITE ;
   if (clo[PARM_RD].found) myini.initype &= ~(INI_REGREAD + INI_REGWRITE) ;
   if (clo[PARM_ID].found) myini.initype &= ~(INI_FILEREAD + INI_FILEWRITE) ;
   if (clo[PARM_IF].found) myini.inifilename = ini_filename ;
   if (startup)
   { /* start windowed version */
      if (clo[PARM_PP].found) set_process_prio(clo[PARM_PP].value) ;
      MyInstance = this_inst ;
      InitCommonControls() ;
      rv = DialogBox(this_inst, _T("DLG_WINSAMPLE"), 0, (DLGPROC)WinSample) ;
   }
   return(0);
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       WinSample                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Processes messages for the DLG_WINSAMPLE dialog                                                                       |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
INT_PTR CALLBACK WinSample(HWND window_handle, unsigned msg, WPARAM wparam, LPARAM lparam)
{
OPENFILENAME outfilestruct ;

//FILE *outfile ;
_TCHAR static outfilename[256] = _T("") ;
_TCHAR static outfilename_no_path[256] ;
_TCHAR static def_out_ext[] = _T("CSV files\0*.csv\0All files\0*.*\0") ;
HICON static hIconS ;
HICON static hIconB ;
DWORD static dwThreadId, dwThrdParam = 1 ;
HANDLE static hThread ;
bool retcd ; /* return nonzero, if the message is processed here and zero, if it is not */
RECT rect ;
HFONT hFont ;
_TCHAR *out_msg ;

   retcd = false ; /* return false if we don't process the message */
   switch(msg)
   {
   case WM_INITDIALOG:
      retcd = true ;
      MainWindow = window_handle ;
      hIconS = (HICON) LoadImage(MyInstance, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
      hIconB = (HICON) LoadImage(MyInstance, MAKEINTRESOURCE(IDI_ICON2), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
      if (hIconS != NULL) SendMessage(MainWindow, WM_SETICON, ICON_SMALL, (LPARAM) hIconS) ;
      if (hIconB != NULL) SendMessage(MainWindow, WM_SETICON, ICON_BIG  , (LPARAM) hIconB) ;
//    ShowError(DLG_STATUS) ; /* display last error message */

// save original coordinates of dialog
      SaveCoordinates() ;

// Select font for controls (sample code)
      hFont = CreateFont(
          15,                  // logical height of font
          0,                   // logical average character width
          0,                   // angle of escapement
          0,                   // base-line orientation angle
          FW_NORMAL,           // font weight
          false,               // italic attribute flag
          false,               // underline attribute flag
          false,               // strikeout attribute flag
          ANSI_CHARSET,        // character set identifier
          OUT_DEFAULT_PRECIS,  // output precision
          CLIP_DEFAULT_PRECIS, // clipping precision
          DEFAULT_QUALITY,     // output quality
          DEFAULT_PITCH,       // pitch and family
          _T("Courier New")    // pointer to typeface name string
      ) ;

      SendMessage(GetDlgItem(MainWindow, DLG_OUTFILENAME), WM_SETFONT, (WPARAM) hFont, MAKELPARAM(false, 0));

      CheckRadioButton(MainWindow, RAD_T1, RAD_T4,RAD_T1) ;

      validreg = get_registry(&myini) ; // get registry content

      if (validreg)
      {
         MoveWindow(
         MainWindow,            // handle of window
         registry_x,            // horizontal position
         registry_y,            // vertical position
         registry_width,        // width
         registry_height,       // height
         true);                 // repaint flag
      }
      _tcscpy_s(outfilename, sizeof(outfilename), registry_filename) ;
      SetDlgItemText(MainWindow, DLG_OUTFILENAME, outfilename) ;

      /* display any illegal parameter */
      if (strlen(errorparm) != 0)
      {
         LoadString(MyInstance, STR_INVALIDPARM, strng1, STRINGSIZE);
         _stprintf_s(strng2, sizeof(strng2), strng1, errorparm) ;
         SetDlgItemText(MainWindow, DLG_STATUS, strng2) ;
      }
      _stprintf_s(strng2, sizeof(strng2), _T("%s - Version %u.%2.2u"), PROGRAMNAME, PROGRAMVERSION/100, PROGRAMVERSION%100) ;
      SendMessage(MainWindow, WM_SETTEXT, 0, (LPARAM) strng2);

#ifdef SUSPENDBUTTON
      EnableWindow(GetDlgItem(MainWindow, BUT_SUSPEND), false) ;
#endif
   break ;

   case WM_USER:
      retcd = true ;
      switch(LOWORD(wparam))
      {
      case USER_THREAD_ENDED:
      // output the results
         LoadString(MyInstance, STR_NORMAL_END, strng1, STRINGSIZE) ;
         out_msg = strng1 ;
/* Thread may have assembled a message. If so, use it */
         if ((void *) lparam != NULL) out_msg = (_TCHAR *) lparam ;
         SetDlgItemText(MainWindow, DLG_STATUS, out_msg) ;
         CloseHandle(hThread) ;
         hThread = NULL ;
         EnableControls(true) ;
#ifdef SUSPENDBUTTON
         EnableWindow(GetDlgItem(MainWindow, BUT_SUSPEND), false) ;
#endif
      break ;
      }
   break ;

   case WM_COMMAND:
      retcd = true ;
      switch(LOWORD(wparam))
      {
      case BUT_OUTPUT:
         outfilestruct.lStructSize = sizeof(OPENFILENAME) ;
         outfilestruct.hwndOwner = MainWindow ;
         outfilestruct.hInstance = 0 ;
         outfilestruct.lpstrFilter = def_out_ext ;
         outfilestruct.lpstrCustomFilter = NULL ;
         outfilestruct.nMaxCustFilter = 0 ;
         outfilestruct.nFilterIndex = 1 ;
         outfilestruct.lpstrFile = outfilename ;
         outfilestruct.nMaxFile = sizeof(outfilename) ;
         outfilestruct.lpstrFileTitle = outfilename_no_path ;
         outfilestruct.nMaxFileTitle = sizeof(outfilename_no_path) ;
         outfilestruct.lpstrInitialDir = NULL ;
         outfilestruct.lpstrTitle = _T("Sample data output file") ;
         outfilestruct.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT ;
         outfilestruct.nFileOffset = 0 ;
         outfilestruct.nFileExtension = 0 ;
         outfilestruct.lpstrDefExt = NULL ;
         outfilestruct.lCustData = 0 ;
         outfilestruct.lpfnHook = NULL ;
         outfilestruct.lpTemplateName = NULL ;
         if (GetSaveFileName(&outfilestruct) != 0)
         {
            SetDlgItemText(MainWindow, DLG_OUTFILENAME, outfilestruct.lpstrFile) ;
         }
      break ;

      case DLG_OUTFILENAME:
         GetDlgItemText(MainWindow, DLG_OUTFILENAME, outfilename, sizeof(outfilename)) ;
      break ;

      case BUT_INFO:
         _stprintf_s(strng1, sizeof(strng1), _T("%s - Version %u.%2.2u"), PROGRAMNAME, PROGRAMVERSION/100, PROGRAMVERSION%100) ;
         PrintProgramInfo(DLG_HELPLIST, strng1, _T(__DATE__), _T(__TIME__)) ;
      break ;

      case RAD_T1:
         CheckRadioButton(MainWindow, RAD_T1,RAD_T4,RAD_T1) ;
      break ;

#ifdef SUSPENDBUTTON
      case BUT_SUSPEND:
         if (!thread_suspended)
         {
            if (SuspendThread(hThread) != -1) thread_suspended = true ;
            SetDlgItemText(MainWindow, BUT_SUSPEND, _T("Resume")) ;
            LoadString(MyInstance, STR_SUSPENDED, strng1, STRINGSIZE) ;
            SetDlgItemText(MainWindow, DLG_STATUS, strng1) ;
            LoadString(MyInstance, STR_BUTABORT, strng1, STRINGSIZE) ;
            SetDlgItemText(MainWindow, BUT_START, strng1) ;
            EnableWindow(GetDlgItem(MainWindow, BUT_START), true) ;
         }
         else
         {
            if (ResumeThread(hThread) != -1) thread_suspended = false ;
            SetDlgItemText(MainWindow, BUT_SUSPEND, _T("Suspend")) ;
            LoadString(MyInstance, STR_WORKING, strng1, STRINGSIZE) ;
            SetDlgItemText(MainWindow, DLG_STATUS, strng1) ;
            LoadString(MyInstance, STR_BUTSTART, strng1, STRINGSIZE) ;
            SetDlgItemText(MainWindow, BUT_START, strng1) ;
            EnableWindow(GetDlgItem(MainWindow, BUT_START), false) ;
         }
      break ;
#endif

      case IDOK:
      case BUT_START:
         /* The start button becomes abort, if the thread is suspended */
         if (thread_suspended)
         {
            abort_thread = true ;
#ifdef SUSPENDBUTTON
            SendMessage(MainWindow, WM_COMMAND, BUT_SUSPEND, 0) ;
#endif
         }
         else
         {
/* Operation is done in a thread. Start it. */
            hThread = CreateThread(
                NULL,                        // no security attributes
                0,                           // use default stack size
                DoOperation,                 // thread function
                &dwThrdParam,                // argument to thread function
                0,                           // use default creation flags
                &dwThreadId);                // returns the thread identifier

        // Check the return value for success.
            if (hThread == NULL)
               LoadString(MyInstance, STR_THREADSTARTERROR, strng1, STRINGSIZE) ;
            else
            { /* Thread started properly */
               LoadString(MyInstance, STR_WORKING, strng1, STRINGSIZE) ;
               EnableControls(false) ;
               thread_suspended = false ;
#ifdef SUSPENDBUTTON
               EnableWindow(GetDlgItem(MainWindow, BUT_SUSPEND), true) ;
#endif
               if (clo[PARM_PT].found) set_thread_prio(hThread, clo[PARM_PT].value) ;
            }
            SetDlgItemText(MainWindow, DLG_STATUS, strng1) ;
         }
      break ;

      case IDABORT:
      case IDCANCEL:
      case BUT_EXIT:
         GetWindowRect(MainWindow, &rect) ; /* get current dialog coordinates */
         registry_x = rect.left ;
         registry_y = rect.top ;
         registry_width = rect.right - rect.left ;
         registry_height = rect.bottom - rect.top ;
         if (registry_x < 0) registry_x = 0 ;
         if (registry_y < 0) registry_y = 0 ;
         _tcscpy_s(registry_filename, sizeof(registry_filename), outfilename) ;
         registry_version = PROGRAMVERSION ;    /* version of program which wrote the registry */
         put_registry(&myini) ;                 /* store the dialog properties in registry */
         DestroyIcon(hIconS) ;                  /* Free icon memory */
         DestroyIcon(hIconB) ;                  /* Free icon memory */
         EndDialog(MainWindow, true) ;          /* exits the dialog box */
      break ;

      default:
         retcd = false ; /* we have not processed this command */
      break ;
      }
   break ;

   default:
      retcd = WinLibMessage(msg, wparam, lparam) ; /* WinLib processes some messages */
   break ;
   }

   return(retcd) ;
} /* WinSample */

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       void EnableControls(bool enable)                                                                                |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Enable or disable some critical controls during execution of thread.                                                  |
| Control gets enabled if enable is true, otherwise it gets disabled.                                                   |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void EnableControls(bool enable)
{
   EnableWindow(GetDlgItem(MainWindow, BUT_START), enable) ;
   EnableWindow(GetDlgItem(MainWindow, RAD_T1), enable) ;
   EnableWindow(GetDlgItem(MainWindow, RAD_T2), enable) ;
   EnableWindow(GetDlgItem(MainWindow, RAD_T3), enable) ;
   EnableWindow(GetDlgItem(MainWindow, RAD_T4), enable) ;
   EnableWindow(GetDlgItem(MainWindow, BUT_OUTPUT), enable) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       DoOperation(LPVOID lpParam)                                                                                     |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| This thread does whatever this program is supposed to do.                                                             |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
DWORD WINAPI DoOperation(LPVOID lpParam)
{
_TCHAR static strng_thread1[STRINGSIZE] ;
_TCHAR *retcd ;

   retcd = NULL ; /* NULL means no error */

//   for (vol1=0;((vol1<10) && !abort_thread);vol1++)
//      for (vol=0;vol<500000000;vol++) ;
//   Sleep(15000) ; /* in msec */
   if (abort_thread)
   {
      LoadString(MyInstance, STR_ABORTED, strng_thread1, STRINGSIZE) ;
      abort_thread = false ;
   }
   else
      LoadString(MyInstance, STR_SUCCESS, strng_thread1, STRINGSIZE);
   retcd = strng_thread1 ;

// Notify main window that the thread is finished
   SendMessage(MainWindow, WM_USER, USER_THREAD_ENDED, (LPARAM) retcd) ;

   return 0;
}


