#include <windows.h>
#include "util.h"

#ifndef ASSERT
#if defined(_DEBUG) || defined(DEBUG)
#define ASSERT(b) if(!b) MessageBox(NULL, "FAILED: #b", "ASSERT", MB_OK );
#else
#define ASSERT(b)
#endif
#endif

#define MAX_STRING_RESOURCE_LEN 1024
LPCSTR g_pszMsgBoxTitle = "HTML Help Dumper Tool";

int MsgBox(int idString, UINT nType)
{
    char szMsg[MAX_STRING_RESOURCE_LEN + 1];
    if (LoadString(GetModuleHandle(NULL), idString, szMsg,
            sizeof(szMsg)) == 0) {
        return 0;
    }
    return MessageBox(GetActiveWindow(), szMsg, g_pszMsgBoxTitle, nType);
}

int MsgBox(PCSTR pszMsg, UINT nType)
{
    return MessageBox(GetActiveWindow(), pszMsg, g_pszMsgBoxTitle, nType);
}

PCSTR FindFilePortion( PCSTR pszFile )
{
  PCSTR psz = strrchr(pszFile, '\\');
  if (psz)
    pszFile = psz + 1;
  psz = strrchr(pszFile, '/');
  if (psz)
    return psz + 1;
  psz = strrchr(pszFile, ':');
  return (psz ? psz + 1 : pszFile);
}

typedef enum { JAN=1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC } MONTHS;

int DaysInMonth(int nMonth, int nYear)
{
  switch( nMonth ) {
    case SEP: case APR: case JUN: case NOV:
      return 30;

    case FEB:
      return (nYear % 4) == 0 ? 29 : 28;      // handle leap year

    default:
      return 31;
  }
}

int JulianDate(int nDay, int nMonth, int nYear)
{
  int nDayOfYear = 0;
  int iMonth;

  for( iMonth = JAN ; iMonth < nMonth ; iMonth++ )
    nDayOfYear += DaysInMonth(iMonth, nYear);

  return( (nYear % 10) * 1000 + nDayOfYear + nDay );
}

#define YRMASK        0xFE00
#define YRSHIFT       9

#define MONMASK       0x01E0
#define MONSHIFT      5

#define DAYMASK       0x001F
#define DAYSHIFT      0

#define HRMASK        0xF800
#define HRSHIFT       11
#define MINMASK       0x07E0
#define MINSHIFT      5
#define SECMASK       0x001F
#define SECSHIFT      0

HRESULT FileTimeToDateTimeString( FILETIME FileTime, LPTSTR pszDateTime )
{
  HRESULT hr = S_FALSE;

  WORD wDosDate = 0;
  WORD wDosTime = 0;
  if( FileTimeToDosDateTime( &FileTime, &wDosDate, &wDosTime ) ) {
    DWORD dwDay    = (wDosDate & DAYMASK) >> DAYSHIFT;
    DWORD dwMonth  = (wDosDate & MONMASK) >> MONSHIFT;
    DWORD dwYear   = (((wDosDate & YRMASK) >> YRSHIFT) + 80) % 100;
    DWORD dwHour   = (wDosTime & HRMASK) >> HRSHIFT;
    DWORD dwMinute = (wDosTime & MINMASK) >> MINSHIFT;
    DWORD dwSecond = ((wDosTime & SECMASK) >> SECSHIFT) * 2;
    LPCSTR pszAMPM = NULL;

    if( dwHour >= 12 ) 
      pszAMPM = "PM";
    else
      pszAMPM = "AM";
    if( dwHour > 12 )
      dwHour -= 12;
    if( dwHour == 0 )
      dwHour = 12;

    wsprintf( pszDateTime, "%02d/%02d/%02d %02d:%02d %s", 
      dwMonth, dwDay, dwYear, dwHour, dwMinute, pszAMPM );

    hr = S_OK;
  }

  return hr;
}

int FileTimeToJulianDate( FILETIME FileTime )
{
  int iReturn = 0;

  WORD wDosDate = 0;
  WORD wDosTime = 0;
  if( FileTimeToDosDateTime( &FileTime, &wDosDate, &wDosTime ) ) {
    DWORD dwDay    = (wDosDate & DAYMASK) >> DAYSHIFT;
    DWORD dwMonth  = (wDosDate & MONMASK) >> MONSHIFT;
    DWORD dwYear   = ((wDosDate & YRMASK) >> YRSHIFT) + 1980;
    iReturn = JulianDate( dwDay, dwMonth, dwYear );
  }

  return iReturn;
}

///////////////////////////////////////////////////////////
//
// Get the windows directory for the system or the user
//
// Note, Windows NT Terminal Server has changed the system API
// of GetWindowsDirectory to return a per-user system directory.
// Inorder to determine this condtion we need to check kernel32
// for the GetSystemWindowsDirectory API and if it exists use
// this one instead.
//
UINT HHGetWindowsDirectory( LPSTR lpBuffer, UINT uSize, UINT uiType )
{
  UINT uiReturn = 0;
  PFN_GETWINDOWSDIRECTORY pfnGetUsersWindowsDirectory = NULL;
  PFN_GETWINDOWSDIRECTORY pfnGetSystemWindowsDirectory = NULL;

  // determine which system API to call for each case
  HINSTANCE hInst = LoadLibrary( "Kernel32" );
  if( !hInst )
    return uiReturn;

  pfnGetSystemWindowsDirectory = (PFN_GETWINDOWSDIRECTORY) GetProcAddress( hInst, "GetSystemWindowsDirectoryA" );
  pfnGetUsersWindowsDirectory = (PFN_GETWINDOWSDIRECTORY) GetProcAddress( hInst, "GetWindowsDirectoryA" );
  ASSERT( pfnGetUsersWindowsDirectory ); // if NULL then we have a bug!

  if( !pfnGetSystemWindowsDirectory ) {
    pfnGetSystemWindowsDirectory = pfnGetUsersWindowsDirectory;
  }


  if( uiType == HH_SYSTEM_WINDOWS_DIRECTORY )
    uiReturn = pfnGetSystemWindowsDirectory( lpBuffer, uSize );
  else if( uiType == HH_USERS_WINDOWS_DIRECTORY )
    uiReturn = pfnGetUsersWindowsDirectory( lpBuffer, uSize );
  else
    uiReturn = 0;

  FreeLibrary( hInst );
  return uiReturn;
}

LPSTR CatPath(LPSTR lpTop, LPCSTR lpTail)
{
    //
    // make sure we have a slash at the end of the first element
    //
    LPSTR p;

    p = lpTop + strlen(lpTop);
    p = CharPrev(lpTop,p);
    if (*p != '\\' && *p != '/')
    {
        strcat(lpTop,"\\");
    }

    //
    // strip any leading slash from the second element
    //

    while (*lpTail == '\\') lpTail = CharNext(lpTail);

    //
    // add them together
    //

    strcat(lpTop, lpTail);

    return lpTop;
}


#pragma data_seg(".text", "CODE")

static const char txtGlobal[] = "global.col";
static const char txtColReg[] = "hhcolreg.dat";
static const char txtHelp[]   = "help";
static const char txtHHDat[]  = "hh.dat";

#pragma data_seg()

///////////////////////////////////////////////////////////
//
// Get the help directory
//
// Note, this is always relative to the system's windows
// directory and not the user's windows directory.
// See HHGetWindowsDirectory for details on this.
//
UINT HHGetHelpDirectory( LPTSTR lpBuffer, UINT uSize )
{
  UINT uiReturn = 0;

  uiReturn = HHGetWindowsDirectory( lpBuffer, uSize );
  CatPath( lpBuffer, txtHelp );

  return uiReturn;
}

DWORD CreatePath(char *szPath)
{
   char szTmp[MAX_PATH],*p,*q,szTmp2[MAX_PATH];
   DWORD dwErr;

   strcpy(szTmp2,szPath);
   memset(szTmp,0,sizeof(szTmp));
   q = szTmp2;
   p = szTmp;

   while (*q)
   {
      if (*q == '/' || *q == '\\')
      {
         if (szTmp[1] == ':' && strlen(szTmp) <= 3)
         {
            if(IsDBCSLeadByte(*q))
         {
                *p++ = *q++;
            if(*q)
                    *p++ = *q++;
         }
         else
                *p++ = *q++;
            continue;
         }
         if (!::CreateDirectory(szTmp,0))
         {
            if ( (dwErr = GetLastError()) != ERROR_ALREADY_EXISTS)
               return(dwErr);
         }
      }
      if(IsDBCSLeadByte(*q))
     {
          *p++ = *q++;
          if(*q)
            *p++ = *q++;
     }
     else
          *p++ = *q++;
   }
   if (!::CreateDirectory(szTmp,0))
   {
            if ((dwErr = GetLastError()) != ERROR_ALREADY_EXISTS)
               return(dwErr);
   }

   return(FALSE);
}

///////////////////////////////////////////////////////////
//
// Get the full pathname to the global collections file
//
// Note, this is in always in the system's help directory.
//
UINT HHGetGlobalCollectionPathname( LPTSTR lpBuffer, UINT uSize, BOOL *pbNewPath )
{
  UINT uiReturn = 0;

  *pbNewPath = TRUE;
  uiReturn = HHGetHelpDataPath( lpBuffer );

  if (uiReturn != S_OK)
  {
     *pbNewPath = FALSE;
     uiReturn = HHGetHelpDirectory( lpBuffer, uSize );
     if( !IsDirectory(lpBuffer) )
        CreatePath( lpBuffer );
  }   
  CatPath( lpBuffer, txtColReg );

  return uiReturn;
}

BOOL IsDirectory( LPCSTR lpszPathname )
{
  DWORD dwAttribs = GetFileAttributes( lpszPathname );
  if( dwAttribs != (DWORD) -1 )
    if( dwAttribs & FILE_ATTRIBUTE_DIRECTORY )
      return TRUE;
  return FALSE;
}

static const char txtProfiles[]        = "Profiles";
static const char txtUser[]            = "Default User";
static const char txtAllUsers[]        = "All Users";
static const char txtApplicationData[] = "Application Data";
static const char txtMicrosoft[]       = "Microsoft";
static const char txtHTMLHelp[]        = "HTML Help";
typedef HRESULT (WINAPI *PFN_SHGETFOLDERPATH)( HWND hWnd, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath );
#ifndef CSIDL_FLAG_CREATE
#define CSIDL_COMMON_APPDATA 0x0023      
#define CSIDL_FLAG_CREATE 0x8000
#endif


///////////////////////////////////////////////////////////
//
// Get the full path to where the common help data files lives
//  hhcolreg.dat, *.chw and *.chs
//
// Note, if the subdirectories of the path does not exist
// we will create them
//
HRESULT HHGetHelpDataPath( LPSTR pszPath )
{
  HRESULT hResult = S_OK;
  PFN_SHGETFOLDERPATH pfnSHGetFolderPath = NULL;

  HINSTANCE hInst = LoadLibrary( "Shell32" );
  if( !hInst )
    return S_FALSE;

  pfnSHGetFolderPath = (PFN_SHGETFOLDERPATH) GetProcAddress( hInst, "SHGetFolderPathA" );

  // if this function does not exist then we need to similate the return path of
  // "%windir%\Profiles\All Users\Application Data"
  if( pfnSHGetFolderPath ) {
    // now call it
    hResult = pfnSHGetFolderPath( NULL, CSIDL_FLAG_CREATE | CSIDL_COMMON_APPDATA, NULL, 0, pszPath);
    if (pszPath[0] == NULL)
    {
       FreeLibrary( hInst );  // since we already have a copy of Shell32 loaded, free it
       return S_FALSE;
    }
  }
  else
  {
    FreeLibrary( hInst );  // since we already have a copy of Shell32 loaded, free it
    return S_FALSE;
  }
  FreeLibrary( hInst );  // since we already have a copy of Shell32 loaded, free it
      
  // append "Microsoft"
  CatPath( pszPath, txtMicrosoft );
  if( !IsDirectory(pszPath) )
    if( !CreateDirectory( pszPath, NULL ) )
      return S_FALSE;

  // append "HTML Help"
  CatPath( pszPath, txtHTMLHelp );
  if( !IsDirectory(pszPath) )
    if( !CreateDirectory( pszPath, NULL ) )
      return S_FALSE;

  return hResult;
}


///////////////////////////////////////////////////////////
//
// Get the full pathname to the user's data file
//
// Note, this is always relative to the users's windows
// directory and not the system's windows directory.
// See HHGetWindowsDirectory for details on this.
//
UINT HHGetUsersDataPathname( LPTSTR lpBuffer, UINT uSize )
{
  UINT uiReturn = 0;

  uiReturn = HHGetWindowsDirectory( lpBuffer, uSize, HH_USERS_WINDOWS_DIRECTORY );
  CatPath( lpBuffer, txtHHDat );

  return uiReturn;
}