#include "priv.h"
#include <iethread.h>
#include "winlist.h"
#include "htregmng.h"
#include "resource.h"
#include "inetnot.h"

#include <mluisupp.h>

// Inststub uses kernel string funcions and unbounded buffer functions
#undef lstrcpyn
#undef lstrcmp
#undef lstrcpy
#undef lstrcmpi

#undef  StrCpyW
#define lstrcpy    StrCpyW
#define lstrcpyn   StrCpyNW
#define lstrcmp    StrCmpW
#define lstrcmpi   StrCmpIW

// need these defined before including <runonce.c> which is included by <inststub.h>
BOOL g_fCleanBoot = FALSE;
BOOL g_fEndSession = FALSE;
#include <inststub.h>

#undef lstrcpyn
#undef lstrcmp
#undef lstrcpy
#undef lstrcmpi

#define lstrcpyn       Do_not_use_lstrcpyn_use_StrCpyN
#define lstrcmp        Do_not_use_lstrcmp_use_StrCmp
#define lstrcpy        Do_not_use_lstrcpy_use_StrCpyN
#define lstrcmpi       Do_not_use_lstrcmpi_use_StrCmpI

#define StrCpyW        Do_not_use_StrCpyW_use_StrCpyNW

#ifdef UNIX
#include "unixstuff.h"
#endif

/* Old install stub API (no parameters) for compatibility for a few builds */
EXTERN_C void RunInstallUninstallStubs(void)
{
    RunInstallUninstallStubs2(NULL);
}


void IERevokeClassFactoryObject(void);

#ifndef POSTPOSTSPLIT
// This value will be initialized to 0 only when we are under IExplorer.exe
UINT g_tidParking = 0;
#endif


#define DM_FAVORITES 0

#ifdef BETA_WARNING
#pragma message("buidling with time bomb enabled")
void DoTimebomb(HWND hwnd)
{
    SYSTEMTIME st;
    GetSystemTime(&st);

    //
    // Revision History:
    //  End of October, 1996
    //  April, 1997
    //  September, 1997 (for beta-1)
    //  November 15th, 1997 (for beta-2)
    //
    if (st.wYear > 1997 || (st.wYear==1997 && st.wMonth > 11) ||
            (st.wYear==1997 && st.wMonth == 11 && st.wDay > 15))
    {
        TCHAR szTitle[128];
        TCHAR szBeta[512];

        MLLoadShellLangString(IDS_CABINET, szTitle, ARRAYSIZE(szTitle));
        MLLoadShellLangString(IDS_BETAEXPIRED, szBeta, ARRAYSIZE(szBeta));

        MessageBox(hwnd, szBeta, szTitle, MB_OK);
    }
}
#else
#define DoTimebomb(hwnd)
#endif


/*----------------------------------------------------------
Purpose: Initialize the favorites folder if it doesn't exist.

Returns: --

Cond:    As a side-effect, SHGetSpecialFolderPath calls Ole
         functions.  So this function must be called after
         OleInitialize has been called.

Note:    This is only really required on win95 / NT4 in
         browser only mode.  The shell32.dll that ships
         with IE4 can handle CSIDL_FAVORITES with fCreate=TRUE.
*/
void InitFavoritesDir()
{
    TCHAR szPath[MAX_PATH];

    if (!SHGetSpecialFolderPath(NULL, szPath, CSIDL_FAVORITES, TRUE))
    {
        TCHAR szFavorites[80];

        TraceMsg(DM_FAVORITES, "InitFavoritesDir -- no favorites");

        // if this failed, that means we need to create it ourselves
        GetWindowsDirectory(szPath, ARRAYSIZE(szPath));
        MLLoadString(IDS_FAVORITES, szFavorites, ARRAYSIZE(szFavorites));
        PathCombine(szPath, szPath, szFavorites);
        SHCreateDirectory(NULL, szPath);

        HKEY hkExplorer;
        if (RegCreateKey(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER, &hkExplorer) == ERROR_SUCCESS)
        {
            HKEY hkUSF;

            if (RegCreateKey(hkExplorer, TEXT("User Shell Folders"), &hkUSF) == ERROR_SUCCESS)
            {
                BOOL f;

                TraceMsg(DM_FAVORITES, "InitFavoritesDir -- created in %s", szPath);

                RegSetValueEx(hkUSF, TEXT("Favorites"), 0, REG_SZ, (LPBYTE)szPath, (1 + lstrlen(szPath)) * SIZEOF(TCHAR));
                f = SHGetSpecialFolderPath(NULL, szPath, CSIDL_FAVORITES, TRUE);
                TraceMsg(DM_FAVORITES, "InitFavoritesDir -- cached at %d %s", f, szPath);

                ASSERT(f);
                RegCloseKey(hkUSF);
            }

            RegCloseKey(hkExplorer);
        }
    }
}


#ifdef ENABLE_CHANNELS
//
// Copy ChanBarSetAutoLaunchRegValue from browseui.
//
//extern void ChanBarSetAutoLaunchRegValue(BOOL fAutoLaunch);
void ChanBarSetAutoLaunchRegValue(BOOL fAutoLaunch)
{
    SHRegSetUSValue(TEXT("Software\\Microsoft\\Internet Explorer\\Main"), 
                    TEXT("Show_ChannelBand"), REG_SZ, 
                    fAutoLaunch ? TEXT("yes") : TEXT("no"),
                    sizeof(fAutoLaunch ? TEXT("yes") : TEXT("no")), 
                    SHREGSET_HKCU | SHREGSET_FORCE_HKCU);
}

#endif  // ENABLE_CHANNELS

STDAPI SHCreateSplashScreen(ISplashScreen ** pSplash);
typedef BOOL (*PFNISDEBUGGERPRESENT)(void);
void CUrlHistory_CleanUp();

//
// Mean Time To Failure check routines
//
#define REG_STR_IE    TEXT("SOFTWARE\\Microsoft\\Internet Explorer\\Main")
#define REG_STR_CLEAN TEXT("CleanBrowserShutdown")

LONG GetSessionCount();

void WriteCleanBrowserShutdown(BOOL fClean)
{
    HKEY hkey;
    DWORD dwValue = (DWORD)fClean;

    //
    // Only do MTTF on the first browser session
    //
    if (GetSessionCount() != 0)
        return;
    
    if (RegOpenKeyEx(HKEY_CURRENT_USER, REG_STR_IE, 0, KEY_WRITE, &hkey) 
        == ERROR_SUCCESS)
    {
        RegSetValueEx(hkey, REG_STR_CLEAN, 
            0, REG_DWORD, (LPBYTE)&dwValue, sizeof(dwValue));
        RegCloseKey(hkey);
    }
}

BOOL ReadCleanBrowserShutdown()
{
    HKEY hkey;
    DWORD dwValue = 1; // default to clean shutdown
    DWORD dwSize;

    //
    // Only do MTTF on the first browser session
    //
    if (GetSessionCount() != 0)
        return TRUE;

    if (RegOpenKeyEx(HKEY_CURRENT_USER, REG_STR_IE, 0, KEY_READ, &hkey) 
        == ERROR_SUCCESS)
    {
        RegQueryValueEx(hkey, REG_STR_CLEAN, NULL, NULL, (LPBYTE)&dwValue, &dwSize);
        RegCloseKey(hkey);
    }
    return (BOOL)dwValue;
}

void _TweakCurrentDirectory()
{
#ifndef UNIX
    TCHAR szPath[MAX_PATH];
    if (SHGetSpecialFolderPath(NULL, szPath, CSIDL_DESKTOPDIRECTORY, FALSE))
        SetCurrentDirectory(szPath);
#endif
}

BOOL _IsDebuggerPresent()
{
    static BOOL bDebugger = -1;
    if (bDebugger == -1)
    {
        bDebugger = FALSE;
        // See if a debugger is present and bail on splash screen
        // so we don't get in the way of people... This api is only
        // present on NT...
        if (g_fRunningOnNT)
        {
            PFNISDEBUGGERPRESENT pfndebugger = (PFNISDEBUGGERPRESENT)GetProcAddress(GetModuleHandle(TEXT("KERNEL32")), "IsDebuggerPresent");
            if (pfndebugger)
                bDebugger = pfndebugger();
        }
    }
    return bDebugger;
}

PCWSTR IEGetArgs(PCWSTR pszCmd)
{
    if (*pszCmd == TEXT('\"'))
    {
        //  just strip the first quoted string
        while (*++pszCmd)
        {
            if (*pszCmd == L'\"')
            {
                pszCmd++;
                break;
            }
        }
    }
    else
    {
         while (*pszCmd > TEXT(' '))
            pszCmd++;
    }

    // strip the leading spaces
    while (*pszCmd && *pszCmd <= L' ')
        pszCmd++;

    return pszCmd;
}

PCWSTR EatIExploreArgs(PCWSTR pszArgs)
{
    //  this switches match the switches that are conumed/checked
    //  in iexplore\mainloop.cpp.  most params are in SHParseIECommandLine
    //  but these few apply only to IExplore.exe
    static const PCWSTR s_pszEatArgs[] = 
    {
        L"-eval",
        L"-new",
        L"-nowait"
    };

    pszArgs = IEGetArgs(pszArgs);

    for (int i = 0; i < ARRAYSIZE(s_pszEatArgs); i++)
    {
        int cch = lstrlenW(s_pszEatArgs[i]);
        if (0 == StrCmpNIW(s_pszEatArgs[i], pszArgs, cch))
        {
            if (!pszArgs[cch] || pszArgs[cch] == L' ')
                pszArgs += cch;
            //  strip the spaces
            while (pszArgs[0] == L' ')
                pszArgs++;

            //  start over, we need to go through this list until we get them all
            i = -1;
        }

    }
    return pszArgs;
}

BOOL g_fBrowserOnlyProcess = FALSE;

// this entry is used by IEXPLORE.EXE to run the browser in a separate process. this is the
// standard setting, but browsers (IE) can also be run in the same process if BrowseInSeparateProcess
// is turned off (for better perf, worse stability)

STDAPI_(int) IEWinMain(LPSTR pszCmdLine, int nCmdShow)
{
    //  this flag indicates that this
    //  browser is running in its own process
    //  and is not integrated with the shell
    //  even if it is running on an intgrated shell
    g_fBrowserOnlyProcess = TRUE;

    _TweakCurrentDirectory();

    if (g_dwProfileCAP & 0x00000001)
        StartCAP();

#ifdef FULL_DEBUG
    // Turn off GDI batching so that paints are performed immediately
    GdiSetBatchLimit(1);
#endif

    ASSERT(g_tidParking == 0);

    g_tidParking = GetCurrentThreadId();

    ISplashScreen *pSplash = NULL;
        
#ifndef UNIX
    // Show splash screen, be simple for beta 1...
    if (!_IsDebuggerPresent())
    {
        if (SUCCEEDED(SHCreateSplashScreen(&pSplash)))
        {
            HWND hSplash;
            pSplash->Show( HINST_THISDLL, -1, -1, &hSplash );
        }
    }            
#endif

    if (SUCCEEDED(OleInitialize(NULL)))
    {
        BOOL fWeOwnWinList = WinList_Init();
        IETHREADPARAM* piei = SHCreateIETHREADPARAM(NULL, nCmdShow, NULL, NULL);
        if (piei) 
        {
            //
            // Create favorites dir (by hand if necessary).
            //
            InitFavoritesDir();

            //
            //  If we are opening IE with no parameter, check if this is
            // the very first open.
            //
            piei->pSplash = pSplash;
            if (pszCmdLine && pszCmdLine[0])
            {
                USES_CONVERSION;
                //  we are passed the an ANSI cmd line from IExplore.exe
                //  this is lame on NT when we can have UNICODE file names.
                //  on win9x, of course, GetCommandLineW() will return NULL
                //  so we use the one passed to use from IExplore.exe
                LPCWSTR pwszCmdLine;
                if (IsOS(OS_NT))
                    pwszCmdLine = EatIExploreArgs(GetCommandLineW());
                else
                    pwszCmdLine = A2W(pszCmdLine);
                    
                SHParseIECommandLine(&pwszCmdLine, piei);
                // If the "-channelband" option is selected, turn it ON by default
#ifdef ENABLE_CHANNELS
                if (piei->fDesktopChannel)
                    ChanBarSetAutoLaunchRegValue(TRUE);
#endif  // ENABLE_CHANNELS
                piei->pszCmdLine = StrDupW(pwszCmdLine);
            }
            else
            {
                piei->fCheckFirstOpen = TRUE;
            }

#ifdef UNIX
            if ( piei->fShouldStart && CheckAndDisplayEULA() )
            {
#ifndef NO_SPLASHSCREEN
                // IEUNIX - should show after parsing the commandline options.
                if (!_IsDebuggerPresent())
                {
                    if (SUCCEEDED(SHCreateSplashScreen(&pSplash)))
                    {
                        HWND hSplash;
                        pSplash->Show( HINST_THISDLL, -1, -1, &hSplash );
                    }
                }            
#endif
#endif
                DoTimebomb(NULL);
                piei->uFlags |= (COF_CREATENEWWINDOW | COF_NOFINDWINDOW | COF_INPROC | COF_IEXPLORE);
                
                SHOpenFolderWindow(piei);
#ifdef UNIX
            } // Start browser only if user accepts the license.
#endif
        }

        IERevokeClassFactoryObject();
        CUrlHistory_CleanUp();

        if (fWeOwnWinList)
            WinList_Terminate();

        CWinInetNotify::GlobalDisable();
        InternetSetOption(NULL, INTERNET_OPTION_DIGEST_AUTH_UNLOAD, NULL, 0);

        OleUninitialize();
    }

    ATOMICRELEASE(g_psfInternet);

#ifdef DEBUG
    CoFreeUnusedLibraries();
#endif

    TraceMsg(TF_SHDTHREAD, "IEWinMain about to call ExitProcess");

    if (g_dwProfileCAP & 0x00020000)
        StopCAP();

#ifdef UNIX
    MwExecuteAtExit();
    if (g_dwStopWatchMode)
        StopWatchFlush();
#endif

    ExitProcess(0);
    return TRUE;
}


