#include <stdio.h>

//---------------------------------------------------------------------
#include <windows.h>   // required for all Windows applications
#include "install_resource.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "install_utils.h"
#include "cmt_system.h"
//---------------------------------------------------------------------

//------------------------------------------------------
static cmt_string dummy;
//------------------------------------------------------

//---------------------------------------------------------------------
//
//    Class definitions
//
//---------------------------------------------------------------------

//---------------------------------------------------------------------
class Install;
//---------------------------------------------------------------------

//---------------------------------------------------------------------
class Install
{
public:
  static Install& instance ();
  BOOL initialize (HINSTANCE instance,
                   HINSTANCE previous_instance, 
                   int show_mode);
  BOOL run ();
	
  const char* m_application_name;
  const char* m_title;
	
  HINSTANCE m_instance;
  HWND m_window;
	
  HWND m_dialog;

private:
	
  static LRESULT CALLBACK process_messages (HWND window,
                                            UINT message_type,
                                            WPARAM parameter1,
                                            LPARAM parameter2);
  static LRESULT CALLBACK handle_dialog (HWND dialog,
                                         UINT message_type,
                                         WPARAM parameter1,
                                         LPARAM parameter2);
	
  Install ();
  void init (HWND window);
  void install ();
  void uninstall ();

  HANDLE* m_objects;
  int m_object_count;

  WEdit m_root;
  WEdit m_version;
  WEdit m_config;
  WEdit m_site;
  WList m_path;
};
//---------------------------------------------------------------------



//---------------------------------------------------------------------
int APIENTRY WinMain (HINSTANCE instance,
                      HINSTANCE previous_instance,
                      LPSTR command_line,
                      int show_mode)
  //---------------------------------------------------------------------
{
  Install& install = Install::instance ();
	
  install.initialize (instance, previous_instance, show_mode);
	
  return install.run ();
	
  UNREFERENCED_PARAMETER (command_line);
  return (0);
}



//---------------------------------------------------------------------
//
//    Class implementations
//
//---------------------------------------------------------------------

//---------------------------------------------------------------------
Install& Install::instance ()
{
  static Install the_instance;
  return (the_instance);
}

BOOL Install::initialize (HINSTANCE instance,
                          HINSTANCE previous_instance,
                          int show_mode)
{
  WNDCLASS  window_class;
	
  // Fill in window class structure with parameters that describe the
  // main window.
	
  if (!previous_instance) 
    {
      window_class.style         = CS_OWNDC;
      window_class.lpfnWndProc   = (WNDPROC) process_messages;
      window_class.cbClsExtra    = 0;
      window_class.cbWndExtra    = 0;
      window_class.hInstance     = instance;
      window_class.hIcon         = LoadIcon (instance, m_application_name);
      window_class.hCursor       = LoadCursor (NULL, IDC_ARROW);
      window_class.hbrBackground = (HBRUSH) (COLOR_MENU+1);
      window_class.lpszMenuName  = "IDR_MENU";
      window_class.lpszClassName = m_application_name;
		
      if (!RegisterClass (&window_class)) return (FALSE);
    }
	
  m_instance = instance;
	
  m_window = CreateWindow (m_application_name,
                           m_title,
                           WS_OVERLAPPEDWINDOW,
                           CW_USEDEFAULT, 0, 440, 590,
                           NULL,
                           NULL,
                           m_instance,
                           NULL);
	
  // If window could not be created, return "failure"
	
  if (!m_window)
    {
      return (FALSE);
    }
	
  ShowWindow (m_window, show_mode);
  UpdateWindow (m_window);
	
  return (TRUE);
}

BOOL Install::run ()
{
  MSG message;
  DWORD result;
  HANDLE objects[1];
  int count = 0;
	
  while (TRUE)
    {
      result = MsgWaitForMultipleObjects (count, objects, FALSE, 
                                          INFINITE, QS_ALLINPUT);
      if (result == (WAIT_OBJECT_0 + count))
        {
          while (PeekMessage (&message, NULL, 0, 0, PM_REMOVE))
            {
              if ((m_dialog == NULL) ||
                  !IsDialogMessage (m_dialog, &message))
                {
                  if (!TranslateAccelerator (message.hwnd, NULL, &message))
                    {
                      if (message.message == WM_QUIT) return (0);
						
                      TranslateMessage (&message);
                      DispatchMessage (&message);
                    }
                }
            }
        }
      else
        {
          // For future async objects
        }
    }
	
  return (message.wParam);
}

Install::Install () : m_application_name ("install"), m_title ("install")
{
  m_dialog = 0;
  m_objects = 0;
  m_object_count = 0;
}

void Install::init (HWND window)
{
  m_window = window;

  m_dialog = CreateDialog (m_instance, 
                           MAKEINTRESOURCE(IDD_DIALOG1), 
                           m_window,
                           (DLGPROC) handle_dialog);

  m_root.init (m_dialog, IDC_ROOT);
  m_version.init (m_dialog, IDC_VERSION);
  m_config.init (m_dialog, IDC_CONFIG);
  m_site.init (m_dialog, IDC_SITE);
  m_path.init (m_dialog, IDC_PATH);

  cmt_string root = CmtSystem::pwd ();
  cmt_string v;
  cmt_string p;

  for (;;)
    {
      CmtSystem::basename (root, v);
      CmtSystem::dirname (root, root);
      CmtSystem::basename (root, p);
      if ((p == "Cmt") || (p == "CMT") || (p == "cmt")) break;
    }

  CmtSystem::dirname (root, root);

  m_root.set (root);
  m_version.set (v);
  m_config.set ("VisualC");
  m_site.set (CmtSystem::get_cmt_site ());
	
  CmtSystem::cmt_string_vector paths;
  CmtSystem::cmt_string_vector filtered_paths;
  CmtSystem::cmt_string_vector path_sources;

  CmtSystem::get_cmt_paths (paths, path_sources);
	
  for (int i = 0; i < paths.size (); i++)
    {
      const cmt_string& s = paths[i];
      if (s == root) continue;
      cmt_string& f = filtered_paths.add ();
      f = s;
    }

  m_path.set (filtered_paths);
}

void Install::install ()
{
  cmt_string root    = m_root.get ();
  cmt_string version = m_version.get ();
  cmt_string config  = m_config.get ();
  cmt_string site    = m_site.get ();
  CmtSystem::cmt_string_vector paths;
  m_path.get (paths);
	
  LONG status;
  HKEY key = 0;
  HKEY subkey = 0;
  DWORD disposition;
	
  status = RegCreateKeyEx (HKEY_LOCAL_MACHINE, "Software\\CMT", 0, "", 
                           REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, &disposition);
  if (status == ERROR_SUCCESS)
    {
      DWORD length;
		
      length = root.size ();
      status = RegSetValueEx (key, "root", 0, REG_SZ, (LPBYTE) root.c_str (), length);
		
      length = version.size ();
      status = RegSetValueEx (key, "version", 0, REG_SZ, (LPBYTE) version.c_str (), length);
		
      length = config.size ();
      status = RegSetValueEx (key, "config", 0, REG_SZ, (LPBYTE) config.c_str (), length);
		
      length = site.size ();
      status = RegSetValueEx (key, "site", 0, REG_SZ, (LPBYTE) site.c_str (), length);
		
      status = RegDeleteKey (key, "path");
		
      status = RegCreateKeyEx (key, "path", 0, "", 
                               REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &subkey, &disposition);
      if (status == ERROR_SUCCESS)
        {
          DWORD index = 0;
          char name[256];
          char temp[256];
			
          for (;;)
            {
              DWORD name_length = sizeof (name) - 1;
              DWORD length = sizeof (temp) - 1;
              DWORD type;
              status = RegEnumValue (subkey, index,
                                     name, &name_length, 0, &type,
                                     (LPBYTE) temp, &length);
              if ((status == ERROR_SUCCESS) ||
                  (status == 234))
                {
                  RegDeleteValue (subkey, name);
                }
				
              if (status == 259)
                {
                  break;
                }
				
              index++;
            }

          for (int i = 0; i < paths.size (); i++)
            {
              const cmt_string& s = paths[i];

              sprintf (temp,  "#%d", i);
				
              length = s.size ();
              status = RegSetValueEx (subkey, temp, 0, REG_SZ, (LPBYTE) s.c_str (), length);
            }
			
          status = RegCloseKey (subkey);
        }
		
      status = RegCloseKey (key);
    }

  cmt_string mgr = root;
  mgr += "\\CMT\\";
  mgr += version;
  mgr += "\\mgr";

  CmtSystem::cd (mgr);
  CmtSystem::execute ("INSTALL");
}

void Install::uninstall ()
{
  LONG status;
  HKEY key = 0;
  HKEY subkey = 0;
	
  status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\CMT", 0, KEY_READ, &key);
  if (status == ERROR_SUCCESS)
    {
      status = RegDeleteKey (key, "root");
      status = RegDeleteKey (key, "version");
      status = RegDeleteKey (key, "config");
      status = RegDeleteKey (key, "site");
      status = RegDeleteKey (key, "path");
      status = RegCloseKey (key);
    }

  status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software", 0, KEY_READ, &key);
  if (status == ERROR_SUCCESS)
    {
      status = RegDeleteKey (key, "CMT");
      status = RegCloseKey (key);
    }
}

LRESULT CALLBACK Install::process_messages (HWND window,
                                            UINT message_type,
                                            WPARAM parameter1,
                                            LPARAM parameter2)
{
  Install& install = Install::instance ();
	
  int menu_item;
  static HPEN blue_pen;
  static HPEN red_pen;
	
  SetLastError (0);
	
  switch (message_type) 
    {
    case WM_CREATE:
      {
        HDC context;
        LOGFONT log_font;
        HFONT font;
				
        context = GetDC (window);
				
        memset (&log_font, 0, sizeof(LOGFONT));
        log_font.lfHeight = -72;
        strcpy ((LPSTR) &(log_font.lfFaceName), "arial");
				
        font = CreateFontIndirect (&log_font); 
        SelectObject (context, font);
				
        blue_pen = CreatePen (PS_SOLID, 1, RGB(0,0,255));
        red_pen  = CreatePen (PS_SOLID, 1, RGB(255,0,0));
      }
			
      install.init (window);
			
      break;
		
    case WM_COMMAND:
      menu_item = LOWORD (parameter1);
			
      switch (menu_item) 
        {
        case ID_FILE_QUIT:
          DestroyWindow (window);
          break;
        default:
          return (DefWindowProc (window, message_type, 
                                 parameter1, parameter2));
        }
      break;
    case WM_DESTROY:
      PostQuitMessage (0);
      break;
    default:
      return (DefWindowProc (window, message_type, parameter1, parameter2));
    }
  return (0);
}
  
LRESULT CALLBACK Install::handle_dialog (HWND dialog,
                                         UINT message_type,
                                         WPARAM parameter1,
                                         LPARAM parameter2)
{
  Install& install = Install::instance ();
	
  int menu_item;
	
  switch (message_type) 
    {
    case WM_CREATE:
      break;
    case WM_SETFONT:
      break;
    case WM_INITDIALOG:
      {
        int x = 0;
        int y = 0;
        int width;
        int height;
        RECT rectangle;
				
        GetWindowRect (dialog, &rectangle);
				
        width = rectangle.right - rectangle.left;
        height = rectangle.bottom - rectangle.top;
				
        MoveWindow (install.m_window, x, y, width, height, TRUE);
      }
			
      break;
    case WM_COMMAND:
      menu_item = LOWORD (parameter1);
			
      switch (menu_item)
        {
        case IDC_INSTALL:
          install.install ();
          break;
        case IDC_UNINSTALL:
          install.uninstall ();
          break;
        case IDC_EDITPATH:
          {
            ListEditor editor (install.m_path);
            editor.run (install.m_instance, install.m_dialog);
          }
          break;
        case IDC_EXIT:
          DestroyWindow (install.m_window);
          break;
        }
			
      break;
    case WM_ACTIVATE:
      menu_item = LOWORD (parameter1);
      break;
    default:
      return (FALSE);
    }
  return (TRUE);
}
//---------------------------------------------------------------------

