/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include <windows.h>
#include <windowsx.h>
#include <direct.h>
#include <stdio.h>

#include "resource.h"

extern HINSTANCE hInst;

#define ITEM_BITMAPWIDTH     16
#define ITEM_BITMAPHEIGHT    16
#define ITEM_LEFTMARGIN       4
#define ITEM_GAP              4

static HWND hWndDirPicker;
static HICON hIconDrives[5];
static HICON hIconFolders[3];
static LPSTR lpszStringToReturn;
static char szUNCRoot[256] = "";

UINT DriveType(UINT iType);

static void fillComboBox(HWND hWnd) 
{
  HWND hWndCB = GetDlgItem(hWnd, ID_COMBO_DIR);
  HWND hWndTempLB = GetDlgItem(hWnd, ID_LISTTEMP_DIR);
  if(hWndCB == NULL)
    return;
  ComboBox_ResetContent(hWndCB);
  ListBox_ResetContent(hWndTempLB);
  ListBox_Dir(hWndTempLB, DDL_DRIVES|DDL_EXCLUSIVE, (LPSTR)"*");

  int iDriveCount = ListBox_GetCount(hWndTempLB);
  int iCurDrive=_getdrive() - 1;

  char szDrive[16];
  char szItem[80];

  for (int i = 0; i < iDriveCount;  i++) 
  {
    ListBox_GetText(hWndTempLB, i, szDrive);
    CharLower(szDrive);
    int iDrive = szDrive[2] - 'a';
    char szRoot[16];
    sprintf(szRoot, "%c:\\", szDrive[2]);

    int iType = DriveType(iDrive);

    if(iType < 2)
      continue;

    //Start the item string with the drive letter, colon, and two spaces
    sprintf(szItem, "%c%s", szDrive[2], ": ");

    if((iType == DRIVE_FIXED) || (iType == DRIVE_RAMDISK)) 
    { // get volume ID
      char szVolumeID[80];
      DWORD dwMaxLength;
      DWORD dwSysFlags;
      GetVolumeInformation(szRoot,             // address of root directory of the file system 
                           szVolumeID,         // address of name of the volume 
                           sizeof(szVolumeID), // length of lpVolumeNameBuffer 
                           NULL,               // address of volume serial number 
                           &dwMaxLength,       // address of system's maximum filename length
                           &dwSysFlags,        // address of file system flags 
                           NULL,               // address of name of file system 
                           NULL);              // length of lpFileSystemNameBuffer 

      CharLower(szVolumeID);
      lstrcat(szItem, szVolumeID);
    }

    //For network drives, go grab the \\server\share for it.
    if(DRIVE_REMOTE == iType) 
    {
      char szNet[64];
      szNet[0] = '\0';
      DWORD dwSizeOfszNet = sizeof(szNet);

      sprintf(szDrive, "%c:", szDrive[2]);
      CharUpper(szDrive);
      WNetGetConnection(szDrive, szNet, &dwSizeOfszNet);
      CharLower(szNet);
      lstrcat(szItem, szNet);
    }

    int index = ComboBox_AddString(hWndCB, szItem);
    ComboBox_SetItemData(hWndCB, index, MAKELONG(iDrive, iType));
    if(iDrive == iCurDrive)
      ComboBox_SetCurSel(hWndCB, index);
    if(szUNCRoot[0] != '\0')
      ComboBox_SetCurSel(hWndCB, -1);
  }
}

static void fillTempLBWithDirs(HWND hWndTempLB, LPSTR lpszDir) 
{
  BOOL bDone = FALSE;
  WIN32_FIND_DATA ffdataStruct;

  char szPath[_MAX_PATH];
  char szFileName[_MAX_PATH];
  lstrcpy(szPath, lpszDir);
  if(szPath[lstrlen(szPath) - 1] == '\\')
    szPath[lstrlen(szPath) - 1] = '\0';
  lstrcpy(szFileName, szPath);
  lstrcat(szFileName, "\\*");
  HANDLE handle = FindFirstFile(szFileName, &ffdataStruct);
  if(handle == INVALID_HANDLE_VALUE) 
  {
    FindClose(handle);
    return;
  }
  while(!bDone) 
  {
    lstrcpy(szFileName, szPath);
    lstrcat(szFileName, "\\");
    lstrcat(szFileName, ffdataStruct.cFileName);
    if(ffdataStruct. dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 
    {
      char szStringToAdd[_MAX_PATH + 2];
      lstrcpy(szStringToAdd, "[");
      lstrcat(szStringToAdd, ffdataStruct.cFileName);
      lstrcat(szStringToAdd, "]");
      CharLower(szStringToAdd);
      ListBox_AddString(hWndTempLB, szStringToAdd);
    }
    bDone = !FindNextFile(handle, &ffdataStruct);
  }
  FindClose(handle);
}

static void fillListBox(HWND hWnd, LPSTR lpszDir) 
{
  HWND hWndLB = GetDlgItem(hWnd, ID_LIST_DIR);
  HWND hWndTempLB = GetDlgItem(hWnd, ID_LISTTEMP_DIR);
  HWND hWndEdit = GetDlgItem(hWnd, ID_EDIT_DIR);
  if((hWndLB == NULL) || (lpszDir == NULL))
    return;
  
  int iLastChar = lstrlen(lpszDir);
  if(lpszDir[iLastChar - 1] == '\\')
    lpszDir[iLastChar - 1] = '\0';

  SetWindowRedraw(hWndLB, FALSE);
  ListBox_ResetContent(hWndLB);
  ListBox_ResetContent(hWndTempLB);

  LPSTR lpszLast;
  lpszLast = CharLower(lpszDir);

  SetWindowText(hWndLB, lpszDir);

  char szDir[_MAX_DIR];
  char szFullDir[_MAX_DIR];
  sprintf(szFullDir, "%s", lpszDir);
  sprintf(szDir, "%s\\*.*", lpszDir);

  BOOL bFirst = TRUE;
  char ch;
  int index;
  while (TRUE) 
  {
    LPSTR lpsz;
    if((lpszDir[0] == '\\') && (lpszDir[1] == '\\') && bFirst)
      lpsz = strchr(lpszLast + lstrlen(szUNCRoot), '\\');
    else
      lpsz = strchr(lpszLast, '\\');
    if(lpsz != NULL) {
      if (bFirst)
        ch = *(++lpsz);
      else
        ch = *lpsz;
      *lpsz = 0;
    } 
    else 
    {
      //If we're looking at a drive only, then append a backslash
      if (lpszLast == lpszDir && bFirst)
        lstrcat(lpszLast, "\\");
    }
    //Add the drive string--includes the last one where lpsz == NULL
    index = ListBox_AddString(hWndLB, lpszLast);

    UINT i = (NULL != lpsz) ? ID_ICON_FOLDEROPEN : ID_ICON_OPENSELECT;
    ListBox_SetItemData(hWndLB, index, MAKELONG(index, i));

    if(NULL == lpsz)
      break;

      //Restore last character.
    *lpsz = ch;
    lpsz += (bFirst) ? 0 : 1;

    bFirst=FALSE;
    lpszLast = lpsz;
  }
  int indent = index + 1;

  //Get available directories
  fillTempLBWithDirs(hWndTempLB, lpszDir);

  int itemCount = ListBox_GetCount(hWndTempLB);

  for (int i = 0; i < itemCount; i++) {
    index = ListBox_GetText(hWndTempLB, i, lpszDir);
    //Skip directories beginning with . (skipping . and ..)
    if(lpszDir[1] == '.')
      continue;
    //Remove the ending ']'
    iLastChar = lstrlen(lpszDir);
    lpszDir[iLastChar - 1] = '\0';
    //Add the string to the real directory list.
    index = ListBox_AddString(hWndLB, lpszDir + 1);
    ListBox_SetItemData(hWndLB, index, MAKELONG(indent, ID_ICON_FOLDERCLOSED));
  }
  //Force a listbox repaint.
  SetWindowRedraw(hWndLB, TRUE);
  InvalidateRect(hWndLB, NULL, TRUE);

  if(szFullDir[lstrlen(szFullDir) - 1] == ':')
    lstrcat(szFullDir, "\\");
  Edit_SetText(hWndEdit, szFullDir);

  GetScrollRange(hWndLB, SB_VERT, (LPINT)&i, (LPINT)&index);

  if(!(i == 0 && index == 0))
    ListBox_SetTopIndex(hWndLB, max((int)(index - 2), 0));

  ListBox_SetCurSel(hWndLB, indent - 1);
}

static void onDrawItem(LPDRAWITEMSTRUCT lpdis, BOOL bDrive) 
{
  if((int)lpdis->itemID < 0)
    return;

  char szItem[_MAX_DIR];
  DWORD dwItemData;

  if(bDrive) 
  {
    dwItemData = ComboBox_GetItemData(lpdis->hwndItem, lpdis->itemID);
    ComboBox_GetLBText(lpdis->hwndItem, lpdis->itemID, szItem);
  } 
  else 
  {
    dwItemData = ListBox_GetItemData(lpdis->hwndItem, lpdis->itemID);
    ListBox_GetText(lpdis->hwndItem, lpdis->itemID, szItem);
  }

  if(lpdis->itemAction & (ODA_DRAWENTIRE | ODA_SELECT)) 
  {
    COLORREF colorText;
    COLORREF colorBack;
    if(lpdis->itemState & ODS_SELECTED) 
    {
      colorText = SetTextColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
      colorBack = SetBkColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHT));
    }
    HICON hIcon;
    int indent = 0;
    if(bDrive) 
    {
      int iType=(int)HIWORD(dwItemData);
      switch (iType) 
      {
        case DRIVE_REMOVABLE:
          hIcon = hIconDrives[0];
          break; 
        case DRIVE_FIXED:
          hIcon = hIconDrives[1];
          break;
        case DRIVE_REMOTE:
          hIcon = hIconDrives[2];
          break; 
        case DRIVE_CDROM:
          hIcon = hIconDrives[3];
          break; 
        case DRIVE_RAMDISK:
          hIcon = hIconDrives[4];
          break; 
      }

    } 
    else 
    {
      int iconID = (int)HIWORD(lpdis->itemData);
      switch (iconID) 
      {
        case ID_ICON_FOLDERCLOSED:
          hIcon = hIconFolders[0];
          break;
        case ID_ICON_FOLDEROPEN:
          hIcon = hIconFolders[1];
          break;
        case ID_ICON_OPENSELECT:
          hIcon = hIconFolders[2];
          break;
      }
      indent = 4 * (1 + LOWORD(lpdis->itemData));
    }

    ExtTextOut(lpdis->hDC, 
               lpdis->rcItem.left + ITEM_LEFTMARGIN + ITEM_BITMAPWIDTH + ITEM_GAP + indent,
               lpdis->rcItem.top,
               ETO_OPAQUE | ETO_CLIPPED,
               &lpdis->rcItem,
               szItem,
               lstrlen(szItem),
               NULL);

    BOOL res = DrawIcon(lpdis->hDC, 
                        lpdis->rcItem.left + ITEM_LEFTMARGIN + indent,
                        lpdis->rcItem.top, 
                        hIcon);

    if(lpdis->itemState & ODS_SELECTED) 
    {
      SetTextColor(lpdis->hDC, colorText);
      SetBkColor(lpdis->hDC, colorBack);
    }
  }
  if((lpdis->itemAction & ODA_FOCUS) || (lpdis->itemState & ODS_FOCUS))
    DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
}

static void fillUNCRootArray(LPSTR lpsz) 
{
  char szCurDir[_MAX_PATH];
  _getcwd(szCurDir, sizeof(szCurDir));
  lstrcpy(szUNCRoot, lpsz);
  if(szUNCRoot[lstrlen(szUNCRoot) - 1] == '\\')
    szUNCRoot[lstrlen(szUNCRoot) - 1] = '\0';
  for(;;) 
  {
    LPSTR lptemp = strrchr(szUNCRoot, '\\');
    if(lptemp == NULL)
      break;
    *lptemp = '\0';
    if(_chdir(szUNCRoot) == -1) 
    {
      *lptemp = '\\';
      break;
    }
  }
  _chdir(szCurDir);
}

static void onInitDialog(HWND hWnd, LPSTR lpsz) 
{
  hWndDirPicker = hWnd;
  lpszStringToReturn = lpsz;
  
  hIconDrives[0] = LoadIcon(hInst, MAKEINTRESOURCE(ID_ICON_DRIVEFLOPPY));
  hIconDrives[1] = LoadIcon(hInst, MAKEINTRESOURCE(ID_ICON_DRIVEHARD));
  hIconDrives[2] = LoadIcon(hInst, MAKEINTRESOURCE(ID_ICON_DRIVENETWORK));
  hIconDrives[3] = LoadIcon(hInst, MAKEINTRESOURCE(ID_ICON_DRIVECDROM));
  hIconDrives[4] = LoadIcon(hInst, MAKEINTRESOURCE(ID_ICON_DRIVERAM));

  hIconFolders[0] = LoadIcon(hInst, MAKEINTRESOURCE(ID_ICON_FOLDERCLOSED));
  hIconFolders[1] = LoadIcon(hInst, MAKEINTRESOURCE(ID_ICON_FOLDEROPEN));
  hIconFolders[2] = LoadIcon(hInst, MAKEINTRESOURCE(ID_ICON_OPENSELECT));
  
  if(lpsz[0] == '\0') 
    _getcwd(lpsz, _MAX_PATH);
  else if(lpsz[lstrlen(lpsz) - 1] == ':')
    lstrcat(lpsz, "\\");

  int ret = _chdir(lpsz);
  if(ret == -1) 
  {
    char szText[_MAX_PATH + 80];
    sprintf(szText, "The specified directory %s\ncannot be found", lpsz);
    MessageBox(GetParent(hWnd), szText, "Choose Directory", MB_ICONEXCLAMATION|MB_OK);
    _getcwd(lpsz, _MAX_PATH);
  }
  if((lpsz[0] == '\\') && (lpsz[1] == '\\'))
    fillUNCRootArray(lpsz);
  fillListBox(hWnd, lpsz);
  fillComboBox(hWnd);
}

static void shutDialog(HWND hWnd) 
{
  szUNCRoot[0] = '\0';
}

static void onCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify) 
{
  char szCurDir[_MAX_PATH];
  switch(id) 
  {
    case ID_LIST_DIR:
      if(codeNotify == LBN_DBLCLK) 
      {
        int index = ListBox_GetCurSel(hWndCtl);
        DWORD dwItemData = ListBox_GetItemData(hWndCtl, index);

        if(HIWORD(dwItemData) == ID_ICON_OPENSELECT) 
        {
          shutDialog(hWnd);
          char szString[_MAX_PATH];
          Edit_GetText(GetDlgItem(hWndDirPicker, ID_EDIT_DIR), szString, sizeof(szString));
          lstrcpy(lpszStringToReturn, szString);
          EndDialog(hWnd, IDOK);
          break;
        }

        ListBox_GetText(hWndCtl, index, szCurDir);

        char szDir[_MAX_DIR];
        LPSTR lpsz;
        if((HIWORD(dwItemData) == ID_ICON_FOLDEROPEN) && (index != 0)) 
        {
          GetWindowText(hWndCtl, szDir, sizeof(szDir));
          lpsz=_fstrstr(szDir, szCurDir);
          *(lpsz + lstrlen(szCurDir)) = '\0';
          lstrcpy(szCurDir, szDir);
        }
        if (_chdir(szCurDir) == 0) 
        {
          _getcwd(szCurDir, _MAX_PATH);
          fillListBox(hWndDirPicker, szCurDir);
        }
      }
      break;
    case ID_COMBO_DIR:
      if(codeNotify == CBN_SELCHANGE) 
      {
        char szDrive[80];
        int index = ComboBox_GetCurSel(hWndCtl);
        if(index == CB_ERR)
          break;
        ComboBox_GetLBText(hWndCtl, index, szDrive);

        int iCurDrive = _getdrive();
Retry:
        HCURSOR hCursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
        SetCapture(hWndDirPicker);
        if((0 == _chdrive((int)(szDrive[0] - 'a' + 1))) && (NULL != _getcwd(szCurDir, _MAX_PATH))) 
        {
          fillListBox(hWndDirPicker, szCurDir);
          ListBox_SetTopIndex(GetDlgItem(hWndDirPicker, ID_LIST_DIR), 0);
          SetCursor(hCursorOld);
          ReleaseCapture();
          break;
        }
        SetCursor(hCursorOld);
        ReleaseCapture();

        char szText[80];        
        sprintf(szText, "Cannot read drive %c:", szDrive[0]);
        if(IDRETRY == MessageBox(hWndDirPicker, szText, "Choose Directory", MB_ICONEXCLAMATION|MB_RETRYCANCEL))
          goto Retry;
        
        //Changing drives failed so restore drive and selection
        _chdrive(iCurDrive);

        sprintf(szDrive, "%c:", (char)(iCurDrive + 'a' - 1));
        index = ComboBox_SelectString(hWndCtl, -1, szDrive);
      }
      break;
    case IDOK:
      shutDialog(hWnd);
      char szString[_MAX_PATH];
      Edit_GetText(GetDlgItem(hWndDirPicker, ID_EDIT_DIR), szString, sizeof(szString));
      lstrcpy(lpszStringToReturn, szString);
      EndDialog(hWnd, IDOK);
      break;
    case IDCANCEL:
      shutDialog(hWnd);
      lpszStringToReturn[0] = '\0';
      EndDialog(hWnd, IDCANCEL);
      break;
    default:
      break;
  }
}

static BOOL CALLBACK DirPickDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 
{
  switch(msg) {
    case WM_INITDIALOG:
      onInitDialog(hWnd, (LPSTR)lParam);
      break;
    case WM_COMMAND:
      HANDLE_WM_COMMAND(hWnd, wParam, lParam, onCommand);
      break;
    case WM_MEASUREITEM: 
    {
      static int cyItem = -1;      //Height of a listbox item
      LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
      if(cyItem == -1) 
      {
        HFONT hFont = (HFONT)SendMessage(hWnd, WM_GETFONT, 0, 0L);
        if(hFont == NULL)
          hFont = GetStockFont(SYSTEM_FONT);
        HDC hDC = GetDC(hWnd);
        HFONT hFontOld = SelectFont(hDC, hFont);
        TEXTMETRIC tm;
        GetTextMetrics(hDC, &tm);
        cyItem = max(ITEM_BITMAPHEIGHT, tm.tmHeight);
        SelectFont(hDC, hFontOld);
        ReleaseDC(hWnd, hDC);
      }

      lpmis->itemHeight = cyItem;
    }
      break;
    case WM_DRAWITEM:
      onDrawItem((LPDRAWITEMSTRUCT)lParam, ((UINT)wParam == ID_COMBO_DIR));
      return TRUE; // to prevent default action in listbox (drawing focus)
    default:
      return FALSE;
  }
  return TRUE;
}

/*
 * DriveType
 *
 * Purpose:
 *  Augments the Windows API GetDriveType with a call to the CD-ROM
 *  extensions to determine if a drive is a floppy, hard disk, CD-ROM,
 *  RAM-drive, or networked  drive.
 *
 * Parameters:
 *  iDrive          UINT containing the zero-based drive index
 *
 * Return Value:
 *  UINT            One of the following values describing the drive:
 *                  DRIVE_FLOPPY, DRIVE_HARD, DRIVE_CDROM, DRIVE_RAMDISK,
 *                  DRIVE_NETWORK.
 *
 * Copyright (c)1992 Kraig Brockschmidt, All Right Reserved
 * Compuserve:  70750,2344
 * Internet  :  kraigb@microsoft.com
 *
 */
UINT DriveType(UINT iDrive) 
{
  //Validate possible drive indices
  if((0 > iDrive)  || (25 < iDrive))
    return (UINT)-1;

  static char path[] = "d:\\";
  path[0] = 'a' + iDrive;
  int iType = GetDriveType(path);  

  /*
   * Under Windows NT, GetDriveType returns complete information
   * not provided under Windows 3.x which we now get through other
   * means.
   */

  return iType;
}

BOOL PickupDirectory(HWND hWndOwner, LPSTR lpszString) 
{
  if(hWndOwner == NULL)
    hWndOwner = GetDesktopWindow();
  int ret = DialogBoxParam(hInst, MAKEINTRESOURCE(ID_DIALOG_CHOOSEDIR), hWndOwner, DirPickDlgProc, (LPARAM)lpszString);
  return (ret == IDOK);
}
