/* (PD) 2001 The Bitzi Corporation
 * Please see file COPYING or http://bitzi.com/publicdomain 
 * for more info.
 *
 * $Id: gui_win32.c,v 1.2 2001/04/13 18:23:37 robert Exp $
 */
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include "resource.h"
#include "bitcollider.h"
#include "dirsearch.h"
#include "list.h"
#include "gui_win32.h"

#define MAX_OFN_SIZE     4096
#define SMALL_FILE_SIZE (512 * 1024)

static HWND         hDetails, hDialog = NULL;
static HWND         hFileProgress, hOverallProgress, hDetailsList;
static HWND         hCurrentFile, hNumFiles, hTotalFiles, hSkippedFiles;
static HWND         hCloseWindow, hDontShowWindow;
static HINSTANCE    hInstance;
static BOOL         bDetailsShown = false, bRunning = false;
static BOOL         bCloseWindow = false, bDontShowWindow = true;
static int          iNumProcessed, iNumSkipped, iNumTotal;
static Bitcollider *bc = NULL;
static char         szSubmitURL[MAX_PATH];

void chop_string(HDC hdc, char *str, int width);
void set_current_file(const char *fileName);
void browse_file(Bitcollider *bc, HWND hparent);
void load_settings(void);
void save_settings(void);

BOOL CALLBACK main_proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK details_proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK about_proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);

INT WINAPI DllMain (HINSTANCE hInst,
                    ULONG ul_reason_being_called,
                    LPVOID lpReserved)
{
    switch (ul_reason_being_called)
    {
        case DLL_PROCESS_ATTACH:
            hInstance = hInst;
            break;

        case DLL_THREAD_ATTACH:
            break;

        case DLL_THREAD_DETACH:
            break;

        case DLL_PROCESS_DETACH:
            break;
    }

    return 1;                 
}

HWND init_gui(void)
{

   InitCommonControls();

   bc = NULL;

   hDialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_MAINDIALOG), NULL, main_proc);
   if (hDialog == NULL)
      return hDialog;
   hDetails = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DETAILSDIALOG), NULL, details_proc);

   hFileProgress = GetDlgItem(hDialog, IDC_FILEPROGRESS);
   hOverallProgress = GetDlgItem(hDialog, IDC_OVERALLPROGRESS);
   hCurrentFile = GetDlgItem(hDialog, IDC_CURRENTFILE);
   hNumFiles = GetDlgItem(hDialog, IDC_NUMPROCESSED);
   hTotalFiles = GetDlgItem(hDialog, IDC_NUMTOPROCESS);
   hSkippedFiles = GetDlgItem(hDialog, IDC_NUMSKIPPED);
   hDetailsList = GetDlgItem(hDetails, IDC_DETAILSLIST);
   hCloseWindow = GetDlgItem(hDialog, IDC_CLOSECHECK);
   hDontShowWindow = GetDlgItem(hDialog, IDC_DONTSHOWCHECK);

   load_settings();
   SendMessage(hDontShowWindow, BM_SETCHECK, bDontShowWindow, 0);
   SendMessage(hCloseWindow, BM_SETCHECK, bCloseWindow, 0);

   return hDialog;
}

void win32_progress_callback(int percentComplete, const char *fileName, const char *message)
{
    char *ptr, text[255];
    char *temp;

    /* The callback function gets called with percentComplete = -1 when the
       bitprinting starts and with -2 when the bitprinting completes */
    if (percentComplete == -1)
    {
       SendMessage(hFileProgress, PBM_SETPOS, 0, 0);   
       SendMessage(hOverallProgress, PBM_SETPOS, 0, 0);   

       SetWindowText(hNumFiles, "0 files processed");
       SetWindowText(hSkippedFiles, "0 files skipped");
       sprintf(text, "%d files to process", iNumTotal);
       SetWindowText(hTotalFiles, text);

       DragAcceptFiles(hDialog, false);
       SetWindowText(GetDlgItem(hDialog, IDC_OKCANCEL), "&Cancel");
       return;
    }

    if (percentComplete == -2)
    {
       SendMessage(hFileProgress, PBM_SETPOS, 0, 0);   
       SendMessage(hOverallProgress, PBM_SETPOS, 0, 0);   
       SetWindowText(hTotalFiles, "0 files to process");

       set_current_file("Current File:");
       DragAcceptFiles(hDialog, true);
       EnableWindow(GetDlgItem(hDialog, IDC_OKCANCEL), true);
       SetWindowText(GetDlgItem(hDialog, IDC_OKCANCEL), "&Close");
       return;
    }

    SendMessage(hFileProgress, PBM_SETPOS, percentComplete, 0);   
    if (fileName)
    {
       int count;

       ptr = strrchr(fileName, '\\');
       set_current_file((ptr) ? ptr+1 : fileName);

       temp = malloc(strlen(fileName) + 20);
       sprintf(temp, "%s: Analyzing", fileName);
       SendMessage(hDetailsList, LB_INSERTSTRING, -1, (LPARAM)temp);
       count = SendMessage(hDetailsList, LB_GETCOUNT, 0, 0);
       SendMessage(hDetailsList, LB_SETTOPINDEX, count - 1, 0);
       free(temp);
    }

    if (percentComplete == 100 && message)
    {
       iNumProcessed++;
       if (iNumTotal > 0)
           SendMessage(hOverallProgress, PBM_SETPOS, iNumProcessed * 100 / iNumTotal, 0);   
    }

    if (percentComplete == 0 && message)
       iNumSkipped++;

    if (message)
    {
       int   index, len;

       sprintf(text, "%d files processed", iNumProcessed);
       SetWindowText(hNumFiles, text);
       sprintf(text, "%d files skipped", iNumSkipped);
       SetWindowText(hSkippedFiles, text);

       index = SendMessage(hDetailsList, LB_GETCOUNT, 0, 0) - 1;
       len = SendMessage(hDetailsList, LB_GETTEXTLEN,  index, 0) + 4 + strlen(message);
       temp = malloc(len);
       SendMessage(hDetailsList, LB_GETTEXT, index, (LPARAM)temp);
       ptr = strrchr(temp, ':');
       if (ptr)
          strcpy(ptr + 2, message);
       else
          sprintf(temp, "%s: %s", temp, message);
       SendMessage(hDetailsList, LB_DELETESTRING, index, 0);
       SendMessage(hDetailsList, LB_INSERTSTRING, -1, (LPARAM)temp);
       SendMessage(hDetailsList, LB_SETTOPINDEX, index, 0);
       free(temp);
    }
}

BOOL CALLBACK main_proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
		case WM_INITDIALOG:
        {
                RECT rect;
                HDC  hDc;
                int  maxX, maxY;

                hDc = GetDC(NULL);
                maxX = GetDeviceCaps(hDc, HORZRES);
                maxY = GetDeviceCaps(hDc, VERTRES);
                ReleaseDC(NULL, hDc);

                GetWindowRect(hDlg, &rect);
                SetWindowPos(hDlg, NULL, 
                    (maxX / 2) - ((rect.right - rect.left) / 2),
                    (maxY / 2) - ((rect.bottom - rect.top) / 2), 0, 0,
                    SWP_NOZORDER | SWP_NOSIZE);

                DragAcceptFiles(hDlg, true);

				return TRUE;
        }
		case WM_COMMAND:
			if (LOWORD(wParam) == IDC_OKCANCEL) 
			{
                if (bRunning)
                {
                   EnableWindow(GetDlgItem(hDialog, IDC_OKCANCEL), false);
                   if (bc)
                      set_exit(bc, true);
                }
                else
                {
                   save_settings();
                   EndDialog(hDlg, LOWORD(wParam));
				   EndDialog(hDetails, LOWORD(wParam));
                   PostQuitMessage(0);
                }
				return TRUE;
			}
            else
			if (LOWORD(wParam) == IDC_DETAILS) 
			{
                ShowWindow(hDetails, bDetailsShown ? SW_HIDE : SW_SHOW);
                bDetailsShown = !bDetailsShown;
				return TRUE;
			}
            else
			if (LOWORD(wParam) == IDC_ABOUT) 
			{
                if (GetKeyState(VK_SHIFT) < 0)
                {  
                   ShowWindow(hDetails, bDetailsShown ? SW_HIDE : SW_SHOW);
                   bDetailsShown = !bDetailsShown;
                }
                else
                   DialogBox(hInstance, MAKEINTRESOURCE(IDD_ABOUTBOX), NULL, about_proc);
			}
            else
  			if (LOWORD(wParam) == IDC_BROWSE) 
			{
                browse_file(NULL, hDlg);
				return TRUE;
			}
            else
			if (LOWORD(wParam) == IDC_DONTSHOWCHECK && HIWORD(wParam) == BN_CLICKED)
            {
                bDontShowWindow = SendMessage(hDontShowWindow, BM_GETCHECK, 0, 0);
            }
            else
			if (LOWORD(wParam) == IDC_CLOSECHECK && HIWORD(wParam) == BN_CLICKED)
            {
                bCloseWindow = SendMessage(hCloseWindow, BM_GETCHECK, 0, 0);
            }

			break;
        case WM_CLOSE:

            save_settings();
            EndDialog(hDlg, LOWORD(wParam));
	        EndDialog(hDetails, LOWORD(wParam));
            PostQuitMessage(0);
			return TRUE;

        case WM_DROPFILES:
        {
            unsigned  count, i;
            List     *list;
            char      fileName[MAX_PATH];
            DWORD     dwThreadId;
            HDROP     hDrop;
            
            hDrop = (HDROP)wParam;
            
            list = create_list();
            add_to_list(list, "[filedrop]");
            count = DragQueryFile (hDrop, -1, NULL, 0);
            for(i = 0; i < count; i++)
            {
               if ( 0 == DragQueryFile ( hDrop, i, fileName, MAX_PATH ))
               {
                  break;
               }
               add_to_list(list, strdup(fileName));
            }
            
            if (CreateThread(NULL, 0, BitcolliderThread, list, 0, &dwThreadId) == NULL)
                MessageBox(NULL, "Cannot create thread.", "Bitcollider", MB_OK);
            
            break;
        }
         
	}
    return FALSE;
}

BOOL CALLBACK details_proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
		case WM_INITDIALOG:
        {
                RECT rect, mainRect;
                HDC  hDc;
                int  maxX;

                hDc = GetDC(NULL);
                maxX = GetDeviceCaps(hDc, HORZRES);
                ReleaseDC(NULL, hDc);

                GetWindowRect(hDialog, &mainRect);
                GetWindowRect(hDlg, &rect);
                SetWindowPos(hDlg, NULL, 
                    (maxX / 2) - ((rect.right - rect.left) / 2),
                    mainRect.bottom, 0, 0,
                    SWP_NOZORDER | SWP_NOSIZE);
				return TRUE;
        }
		case WM_COMMAND:
			if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 
			{
                ShowWindow(hDlg, SW_HIDE);
                bDetailsShown = false;
				return TRUE;
			}
			break;
	}
    return FALSE;
}

BOOL CALLBACK about_proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
		case WM_INITDIALOG:
        {
                RECT rect;
                HDC  hDc;
                int  maxX, maxY;
                char text[128];

                hDc = GetDC(NULL);
                maxX = GetDeviceCaps(hDc, HORZRES);
                maxY = GetDeviceCaps(hDc, VERTRES);
                ReleaseDC(NULL, hDc);

                GetWindowRect(hDlg, &rect);
                SetWindowPos(hDlg, NULL, 
                    (maxX / 2) - ((rect.right - rect.left) / 2),
                    (maxY / 2) - ((rect.bottom - rect.top) / 2), 0, 0,
                    SWP_NOZORDER | SWP_NOSIZE);

                DragAcceptFiles(hDlg, true);

                get_agent_string(text);
                SetWindowText(GetDlgItem(hDlg, IDC_VERSIONSTRING), text);

				return TRUE;
        }
		case WM_COMMAND:
			if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 
			{
                EndDialog(hDlg, 0);
			}
			break;
	}
    return FALSE;
}

void set_current_file(const char *fileName)
{
    char *temp = strdup(fileName);
    HDC   hdc;
    RECT  rect;

    hdc = GetDC(hCurrentFile);
    GetWindowRect(hCurrentFile, &rect);
    chop_string(hdc, temp, rect.right - rect.left);
    ReleaseDC(hCurrentFile, hdc);
    SetWindowText(hCurrentFile, temp);
    free(temp);
}

void chop_string(HDC hdc, char *str, int width)
{
    const char       ellipsis[] = "...";
    SIZE             sizeString;
    SIZE             sizeEllipsis;
    NONCLIENTMETRICS ncm;
    HFONT            font, oldFont;

    ncm.cbSize = sizeof(NONCLIENTMETRICS);
    SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
    font = CreateFontIndirect(&ncm.lfMessageFont);
    oldFont = SelectObject(hdc, font);
    
    GetTextExtentPoint32(hdc, str, strlen(str), &sizeString);       
    if(sizeString.cx <= width)
    {
        SelectObject(hdc, oldFont);
        DeleteObject(font);
        return;
    }

    GetTextExtentPoint32(hdc, ellipsis, strlen(ellipsis), &sizeEllipsis);
    while(strlen(str) > 1)
    {
        str[strlen(str) - 2] = 0;

        GetTextExtentPoint32(hdc, str, strlen(str), &sizeString);
        if ((sizeString.cx + sizeEllipsis.cx) <= width ||
            strlen(str) == 1)
        {
             strcat(str, ellipsis);
             break;
        }
    }
    SelectObject(hdc, oldFont);
    DeleteObject(font);
}

void browse_file(Bitcollider *bc, HWND hparent)
{
    OPENFILENAME  openFile;
    char         *fileName;

    fileName = malloc(MAX_OFN_SIZE);
    memset(fileName, 0, MAX_OFN_SIZE);
    memset(&openFile, 0, sizeof(OPENFILENAME));
    openFile.lStructSize       = sizeof(OPENFILENAME);
    openFile.hwndOwner         = hparent;
    openFile.lpstrFilter       = "All files (*.*)\0*.*\0\0\0";
    openFile.lpstrCustomFilter = NULL;
    openFile.nMaxCustFilter    = 0;
    openFile.nFilterIndex      = 1;
    openFile.lpstrFile         = fileName;
    openFile.nMaxFile          = MAX_OFN_SIZE;
    openFile.lpstrFileTitle    = NULL;
    openFile.nMaxFileTitle     = 0;
    openFile.lpstrInitialDir   = "";
    openFile.lpstrTitle        = "Select files to Bitcollide";
    openFile.Flags             = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER;
    openFile.nFileOffset       = 0;
    openFile.nFileExtension    = 0;
    openFile.lpstrDefExt       = NULL;

    if (GetOpenFileName(&openFile))
    {
        char  *ptr, temp[MAX_PATH];
        int    i;
        List  *list;
        DWORD  dwThreadId;

        list = create_list();
        add_to_list(list, "[filedrop]");

        ptr = fileName;
        for(i = 0; ; i++)
        {
            if (strlen(ptr) == 0)
               break;

            if (i > 0)
            {
                sprintf(temp, "%s\\%s", fileName, ptr);
                add_to_list(list, strdup(temp));
            }

            ptr += strlen(ptr) + 1;
        }
        if (i == 1)
        {
            add_to_list(list, strdup(fileName));
        }

        if (CreateThread(NULL, 0, BitcolliderThread, list, 0, &dwThreadId) == NULL)
            MessageBox(NULL, "Cannot create thread.", "Bitcollider", MB_OK);
    }

    free(fileName);
}


DWORD WINAPI BitcolliderThread(LPVOID arg)
{
    BitcolliderSubmission *submission;
    int                    argIndex = 1, pass;
    b_bool                 analyzeAll = false;
    b_bool                 recurse = false;
    b_bool                 autoSubmit = true;
    b_bool                 ret;
    b_bool                 fileDrop = false;
    BrowserEnum            browser = eBrowserNetscape;
    char                   fileName[MAX_PATH], *ptr;
    HWND                   hWnd = hDialog;
    FileType               type;
    List                  *list = (List *)arg;
    int                    fileCount = 0;  

    iNumProcessed = iNumSkipped = iNumTotal = 0;

    bc = bitcollider_init(false);
    if (!bc)
    {
        MessageBox(hWnd, "Cannot create bitcollider.", "bitcollider", MB_OK);
        return 0;
    }

    submission = create_submission(bc);
    if (!submission)
    {
        MessageBox(hWnd, "Cannot create submission.", "bitcollider", MB_OK);
        bitcollider_shutdown(bc);
        return 0;
    }

    if (autoSubmit)
       set_auto_submit(submission, true);

    //if (checkAsExt[0])
    //   set_check_as(submission, checkAsExt);

    bRunning = true;
    for(pass = 0; pass < 2; pass++)
    {
        if (pass > 0)
        {
           iNumTotal = fileCount;
           set_progress_callback(bc, win32_progress_callback);
           win32_progress_callback(-1, NULL, NULL);
           set_preview(bc, false);
        }
        else
           set_preview(bc, true);

        /* Check the file type and existence and then print out error info
           or analyze the file or dir */
        for(argIndex = 0; ; argIndex++)
        {
           ptr = return_item(list, argIndex);
           if (ptr == NULL)
           {
              if (argIndex == 0)
                 ShowWindow(hDialog, SW_SHOW);
              break;
           }

           if (strcmp(ptr, "[filedrop]") == 0)
           {
              fileDrop = true;
              continue;
           }

           strcpy(fileName, ptr);

           type = check_file_type(fileName);
           if (type == eOther)
           {
               if (pass > 0)
                  win32_progress_callback(0, fileName, "is not a regular file. Skipping.");
               continue;
           }

           if (argIndex == 0 && pass == 0)
           {
               if (bDontShowWindow && type == eFile && num_items_in_list(list) == 1)
               {
                   WIN32_FILE_ATTRIBUTE_DATA data;
                   GetFileAttributesEx(fileName, GetFileExInfoStandard,  &data);
                   if (data.nFileSizeHigh > 0 || 
                       data.nFileSizeHigh == 0 && data.nFileSizeLow > SMALL_FILE_SIZE)
                      ShowWindow(hDialog, SW_SHOW);
               }
               else
                   ShowWindow(hDialog, SW_SHOW);
           }

           if (type == eFile)
           {
               fileCount++;
               ret = analyze_file(submission, fileName, false);
               if (!ret)
               {
                   if (pass > 0)
                       win32_progress_callback(0, fileName, "cannot analyze file.");
               }
           }
           else
           {
               int ret;

               ret = recurse_dir(submission, fileName, analyzeAll, recurse);
               if (ret < 0)
               {
                  if (pass > 0)
                     win32_progress_callback(0, fileName, "path or file not found. Skipping.");
               }

               fileCount += ret;
           }
        }
        if (pass > 0)
           win32_progress_callback(-2, NULL, NULL);
    }
    bRunning = false;

    if (get_num_bitprints(submission) > 0 && !bc->exitNow)
    {
       if (!submit_submission(submission, *szSubmitURL ? szSubmitURL : NULL, browser))
       {
           win32_progress_callback(0, fileName, "Submission failed");
           bitcollider_shutdown(bc);
       }
       if (bCloseWindow && !bc->exitNow && !fileDrop)
           PostMessage(hDialog, WM_CLOSE, 0, 0);
    }

    delete_submission(submission);
    bitcollider_shutdown(bc);
    bc = NULL;

    /* If this was a file drop, then delete the list, since the memory for it
       was allocated in this DLL */
    if (fileDrop)
       delete_list(list);

    return 0;
}

void load_settings(void)
{
    HKEY  hKey;
    int   ret;
    DWORD size, value, type;

    if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Bitzi", 0, KEY_READ, &hKey) != ERROR_SUCCESS)
    {
        return;
    }

    size = MAX_PATH;
    ret = RegQueryValueEx(hKey, "SubmitURL", NULL, &type, (LPBYTE)&szSubmitURL, &size);
    if (ret != ERROR_SUCCESS)
       szSubmitURL[0] = 0;

    size = sizeof(DWORD);
    ret = RegQueryValueEx(hKey, "CloseWindow", NULL, &type, (LPBYTE)&value, &size);
    if (ret == ERROR_SUCCESS)
        bCloseWindow = value;


    size = sizeof(DWORD);
    ret = RegQueryValueEx(hKey, "ShowWindow", NULL, &type, (LPBYTE)&value, &size);
    if (ret == ERROR_SUCCESS)
        bDontShowWindow = value;

    RegCloseKey(hKey);
}

void save_settings(void)
{       
    HKEY  hKey;
    int   ret;
    DWORD action;

    ret = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Bitzi", 0, NULL, 
                         REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, &action);
    if (ret != ERROR_SUCCESS)
       return;
    
    RegSetValueEx(hKey, "CloseWindow", 0, REG_DWORD, (LPBYTE)&bCloseWindow, sizeof(b_bool));
    RegSetValueEx(hKey, "ShowWindow", 0, REG_DWORD, (LPBYTE)&bDontShowWindow, sizeof(b_bool));
 
    RegCloseKey(hKey);
}