// **************************************************************************
//
// rodlg.c
//
//  Microsoft Confidential
//  Copyright (c) Microsoft Corporation 1992-1993
//  All rights reserved
//
//  RunOnce wrapper. This encapsulates all applications that would like
//  to run the first time we re-boot. It lists these apps for the user
//      and allows the user to launce the apps (like apple at ease).
//
//      5 June 1994     FelixA  Started
//  8 June  Felix   Defined registry strings and functionality.
//                  Got small buttons displayed, but not working.
//  9 June  Felix   Both big and small buttons. Nice UI.
//                  Got single click app launching.
//
// 23 June  Felix   Moving it to a Chicago make thingy not Dolphin
//
// *************************************************************************/
//
#include "precomp.h"
#include <shlobj.h>
#include <stdlib.h>
#include <regstr.h>
#include <shellapi.h>
#include <shlobjp.h>
// #include <shsemip.h>

extern int g_iState;    // Command line args.

extern HINSTANCE g_hInst;          // current instance

#define WM_FINISHED (WM_USER+0x123)

#include "resource.h"

int g_fCleanBoot;
TCHAR c_szRunOnce[]=REGSTR_PATH_RUNONCE;
TCHAR c_szSetup[]=REGSTR_PATH_SETUP;
TCHAR g_szWallpaper[] = TEXT("wallpaper");
TCHAR szTileWall[] = TEXT("TileWallpaper");
TCHAR szFallback[] = TEXT("*DisplayFallback");
const TCHAR c_szTimeChangedRunOnce[] = TEXT("WarnTimeChanged"); //kernel32 and explorer use this

// Run time can be set for big or small buttons.
int g_Small=0;
HDC g_hdcMem=NULL;
DWORD g_dwThread;

//***************************************************************************
//
// <Function>()
// <Explanation>
//
// ENTRY:
//      <Params>
//
// EXIT:
//      <Params>
//
//***************************************************************************

//***************************************************************************
//
// DoAnyRandomOneTimeStuff()
//   Just a place to toss random stuff for RunOnce app to do.
//
// ENTRY:
//      void
//
// EXIT:
//      void
//
//***************************************************************************
void DoAnyRandomOneTimeStuff(void)
{
    HKEY runonce;

    // remove any time-changed warning added by kernel32 during boot
    if (RegOpenKey(HKEY_LOCAL_MACHINE, c_szRunOnce, &runonce) == ERROR_SUCCESS)
    {
        RegDeleteValue(runonce, (LPCTSTR)c_szTimeChangedRunOnce);
        RegCloseKey(runonce);
    }
}

//***************************************************************************
//
// RunOnceFill()
//   Fills the List box in the run-once dlg.
//
// ENTRY:
//      HWND of the thing to fill.
//
// EXIT:
//      <Params>
// BOOL NEAR PASCAL RunRegApps(HKEY hkeyParent, LPCSTR szSubkey, BOOL fDelete, BOOL fWait)
//
//***************************************************************************
BOOL   RunOnceFill(HWND hWnd)
{
    HKEY hkey;
    // HKEY hDescKey;
    BOOL fShellInit = FALSE;
    HKEY hkeyParent = HKEY_LOCAL_MACHINE;
    TCHAR szSubkey[MAX_PATH];
    BOOL fDelete=FALSE;
    BOOL fWait=FALSE;

    // Enumerate HKLM\Runonce\Setup - *.*
    lstrcpy(szSubkey,c_szRunOnce);
    lstrcat(szSubkey,TEXT("\\Setup"));
    if (RegOpenKey(hkeyParent, szSubkey, &hkey) == ERROR_SUCCESS)
    {
        DWORD cbData, cchValue, dwType, i;
        TCHAR szValueName[MAX_PATH], szCmdLine[MAX_PATH];
        LRESULT lRes;
        DWORD dwNumSubkeys=1, dwNumValues=5;

        for (i = 0; ; i++)
        {
            cchValue = sizeof(szValueName) / sizeof(TCHAR);
            cbData = sizeof(szCmdLine);

            if (RegEnumValue(hkey, i, szValueName, &cchValue, NULL, &dwType, (LPBYTE) szCmdLine, &cbData) != ERROR_SUCCESS)
                break;

            if (dwType == REG_SZ)
            {
                PTASK pTask;
                pTask = (PTASK)LocalAlloc( LPTR ,sizeof(TASK));
                lstrcpyn(pTask->Text,szValueName, MAX_TEXT );
                lstrcpyn( pTask->Cmd, szCmdLine, MAX_PATH );
                lRes = SendMessage( hWnd, LB_ADDSTRING,  0, (LPARAM)pTask );
                if( lRes == LB_ERR || lRes == LB_ERRSPACE )
                {
                    LocalFree(pTask);
                    pTask=NULL;
                }
            }
        }
        RegCloseKey(hkey);
    }

    return(fShellInit);
}

//***************************************************************************
//
// LaunchApp()
//  Given an index into the list box, will spawn the task, wait for it to
// finish.
//
// ENTRY:
//      Index into list.
//
// EXIT:
//      <Params>
//
//***************************************************************************
int LaunchApp(HWND hWnd, WORD wItem )
{
    LPTSTR lpszCmdLine;
    STARTUPINFO startup;
#ifndef DEBUG
    PROCESS_INFORMATION pi;
#endif
    PTASK pTask;
    RECT rWnd;

    GetWindowRect(hWnd, &rWnd);
    SendMessage(hWnd,LB_SETCURSEL,wItem,0);
    pTask = (PTASK)SendMessage( hWnd, LB_GETITEMDATA, wItem, 0L);
    if(pTask != (PTASK)LB_ERR )
    {
        lpszCmdLine = &pTask->Cmd[0];

        // Now exec it.
        startup.cb = sizeof(startup);
        startup.lpReserved = NULL;
        startup.lpDesktop = NULL;
        startup.lpTitle = NULL;
        startup.dwFlags = STARTF_USEPOSITION; // Set start position
        startup.dwX=rWnd.right+5;
        startup.dwY=rWnd.top+5;
        startup.cbReserved2 = 0;
        startup.lpReserved2 = NULL;

#ifdef DEBUG
    MessageBox(hWnd, lpszCmdLine,TEXT("DebugRun"),MB_OK);
#else
        if (CreateProcess(NULL, lpszCmdLine, NULL, NULL, FALSE, CREATE_NEW_PROCESS_GROUP,
                          NULL, NULL, &startup, &pi))
        {
            WaitForSingleObjectEx(pi.hProcess, INFINITE, TRUE);
            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);
        }
        else
        {
            MessageBeep( MB_ICONEXCLAMATION );
        }
#endif
    }
    else
    {
        MessageBeep( MB_ICONEXCLAMATION );
    }



    // Remove any selection after the app terminates.
    SendMessage( hWnd, LB_SETCURSEL, (WPARAM)-1, 0);
    return FALSE;
}

//***************************************************************************
//
// RunAppsInList()
// Enumerates all the items in the list box, spawning each in turn.
//
// ENTRY:
//      HWND of Parent.
//
// EXIT:
//      <Params>
//
//***************************************************************************
DWORD WINAPI RunAppsInList(LPVOID lp)
{
    HWND hWnd=(HWND)lp;
    WORD i,iNumItems;
    TCHAR szSubkey[MAX_PATH];
    TCHAR szWallpaper[MAX_PATH];
    DWORD cbSize;
    DWORD dwType;

    // Run all the applications in the list
    iNumItems = (WORD)SendMessage(hWnd,LB_GETCOUNT,0,0L);
    for(i=0;i<iNumItems;i++)
    {
        LaunchApp(hWnd,i);
    }

    // Delete the runonce subkey for setup.
#ifdef DEBUG
    MessageBox( hWnd, szSubkey, TEXT("Delete Key - not done"), MB_OK);
#else
    lstrcpy(szSubkey,c_szRunOnce);
    lstrcat(szSubkey,TEXT("\\Setup"));
    RegDeleteKey( HKEY_LOCAL_MACHINE, szSubkey );
#endif


    // Now see if we should reboot/restart.
    if (g_iState & (CMD_DO_REBOOT|CMD_DO_RESTART))
    {
        HKEY hkey;
        TCHAR achTitle[80];
        DWORD dwSetupFlags=0;

        //
        // because we are going to reboot, remove the VGA fallback.
        // line from OneRunce.
        //
        if (RegOpenKey(HKEY_LOCAL_MACHINE, c_szRunOnce, &hkey) == ERROR_SUCCESS)
        {
            RegDeleteValue(hkey, szFallback);
            RegCloseKey(hkey);
        }

        szWallpaper[0]=0;
        LoadString(g_hInst, IDS_PAMPER, szWallpaper, sizeof(szWallpaper) / sizeof(TCHAR));
        GetWindowText(GetParent(hWnd), achTitle, sizeof(achTitle) / sizeof(TCHAR));

        // Get the setup flags.
        if(RegOpenKey(HKEY_LOCAL_MACHINE, c_szSetup, &hkey) == ERROR_SUCCESS)
        {
            cbSize=sizeof(dwSetupFlags);
            if(RegQueryValueEx(hkey, REGSTR_VAL_SETUPFLAGS, NULL , &dwType, (LPBYTE)&dwSetupFlags, &cbSize) != ERROR_SUCCESS )
                dwSetupFlags=0;
            RegCloseKey(hkey);
        }
        //
        //  always reboot the system, dont give the user a choice.
        //
        //  alow OEMs not to have to click OK.
#ifdef DEBUG
        MessageBox(hWnd,dwSetupFlags & SUF_BATCHINF?TEXT("Batchfile used"):TEXT("No batch"),TEXT("Batch"),MB_OK);
#endif
        if( !(dwSetupFlags & SUF_BATCHINF) || !GetPrivateProfileInt(TEXT("Setup"),TEXT("NoPrompt2Boot"),0,TEXT("MSBATCH.INF")))
            MessageBox(GetParent(hWnd),szWallpaper,achTitle,MB_OK|MB_ICONEXCLAMATION);
#ifndef DEBUG
        ExitWindowsEx(EWX_REBOOT, 0);
#endif
    }

    PostMessage(GetParent(hWnd),WM_FINISHED,0,0L);
    return 0;
}


//***************************************************************************
//
// <Function>()
// <Explanation>
//
// ENTRY:
//      <Params>
//
// EXIT:
//      <Params>
//
//***************************************************************************
#define CXBORDER 3

LRESULT   _HandleLBMeasureItem(HWND hwndLB, MEASUREITEMSTRUCT  *lpmi)
{
    RECT    rWnd;
    int     wWnd;
    HDC     hDC;
    HFONT   hfontOld;
    PTASK   pTask;

    // Get the Height and Width of the child window
    GetWindowRect (hwndLB, &rWnd);
    wWnd = rWnd.right - rWnd.left;

    lpmi->itemWidth = wWnd;

    pTask = (PTASK)lpmi->itemData;

    hDC= GetDC(NULL);
    if( (hfontOld  = SelectObject(hDC,g_hBoldFont)) != 0 )
    {
        rWnd.top    = 0;
        rWnd.left   = CXBORDER*2 + g_cxSmIcon;
        rWnd.right  = lpmi->itemWidth - rWnd.left - CXBORDER*2 - g_cxSmIcon;
        rWnd.bottom = 0;
        DrawText(hDC,pTask->Text, lstrlen(pTask->Text),&rWnd, DT_CALCRECT | DT_WORDBREAK );
        SelectObject(hDC, hfontOld);
    }
    ReleaseDC(NULL,hDC);

    lpmi->itemHeight = rWnd.bottom + 2*CXBORDER;

    return TRUE;
}

//---------------------------------------------------------------------------
//***************************************************************************
//
// <Function>()
// <Explanation>
//
// ENTRY:
//      <Params>
//
// EXIT:
//      <Params>
//
//***************************************************************************
LRESULT   _HandleMeasureItem(HWND hwnd, MEASUREITEMSTRUCT  *lpmi)
{
    if (lpmi->CtlType == ODT_LISTBOX)
        return _HandleLBMeasureItem(hwnd, lpmi);
    return TRUE;
}

//---------------------------------------------------------------------------
//***************************************************************************
//
// _HandleLBDrawItem()
//  Draws the Title, Text, and icon for an entry.
//
// ENTRY:
//      HWND and the Item to draw.
//
// EXIT:
//      <Params>
//
//***************************************************************************
LRESULT   _HandleLBDrawItem(HWND hwndLB, DRAWITEMSTRUCT  *lpdi)
{
    RECT rc;
    HFONT hfontOld;
    int xArrow,y;
    PTASK pTask;
    BITMAP bm;
    HGDIOBJ hbmArrow,hbmOld;

    // Don't draw anything for an empty list.
    if ((int)lpdi->itemID < 0)
        return TRUE;

    pTask = (PTASK)lpdi->itemData;
    if(pTask == (PTASK)LB_ERR || !pTask )
        return FALSE;

    if ((lpdi->itemAction & ODA_SELECT) || (lpdi->itemAction & ODA_DRAWENTIRE))
    {
        // Put in the Title text
        hfontOld  = SelectObject(lpdi->hDC,(lpdi->itemState & ODS_SELECTED)?g_hBoldFont:g_hfont);
        ExtTextOut(lpdi->hDC,
                   lpdi->rcItem.left+ CXBORDER*2 + g_cxSmIcon,
                   lpdi->rcItem.top+CXBORDER,
                   ETO_OPAQUE,
                   &lpdi->rcItem,
                   NULL, 0,
                   NULL);
        rc.top    = lpdi->rcItem.top    + CXBORDER;
        rc.left   = lpdi->rcItem.left   + CXBORDER*2 + g_cxSmIcon;
        rc.right  = lpdi->rcItem.right;
        rc.bottom = lpdi->rcItem.bottom;
        DrawText( lpdi->hDC,
                   pTask->Text, lstrlen(pTask->Text),
                   &rc,
                   DT_WORDBREAK);
        SelectObject(lpdi->hDC, hfontOld);

    // Draw the little triangle thingies.
    if(lpdi->itemState & ODS_SELECTED)
    {
        if (!g_hdcMem)
        {
            g_hdcMem = CreateCompatibleDC(lpdi->hDC);
        }
        // selected SRCSTENCIL=0x00d8074a
        // not selected SRCAND.
        if (g_hdcMem)
        {
            hbmArrow = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_MNARROW));
            GetObject(hbmArrow, sizeof(bm), &bm);
            hbmOld = SelectObject(g_hdcMem, hbmArrow);
            xArrow = lpdi->rcItem.left + CXBORDER; // - bm.bmWidth;
            y = ((g_SizeTextExt.cy - bm.bmHeight)/2) + CXBORDER + lpdi->rcItem.top;
            BitBlt(lpdi->hDC, xArrow, y, bm.bmWidth, bm.bmHeight, g_hdcMem, 0, 0, SRCAND); // dwRop);
            SelectObject(g_hdcMem, hbmOld);
            DeleteObject(hbmArrow);
        }
    }
    }
    return TRUE;
}

//---------------------------------------------------------------------------
LRESULT   _HandleCtlColorListbox(HWND hwnd, HDC hdc)
{
    SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
    return (LRESULT) g_hbrBkGnd;
}

//---------------------------------------------------------------------------
LRESULT   _HandleDrawItem(HWND hwnd, DRAWITEMSTRUCT  *lpdi)
{
    if (lpdi->CtlType == ODT_LISTBOX)
        return _HandleLBDrawItem(hwnd, lpdi);
    return TRUE;
}

//---------------------------------------------------------------------------
LRESULT   _HandleDeleteItem(HWND hwnd, DELETEITEMSTRUCT  *lpdi)
{
    if(lpdi)
        if(lpdi->itemData)
        {
            LocalFree( (HLOCAL)lpdi->itemData );
            return TRUE;
        }
    return FALSE;
}

//***************************************************************************
//
// ShrinkToFit()
//     Makes the List box no bigger then it has to be
//     makes the parent window rsize to the LB size.
//
// ENTRY:
//     hwnd Parent
//     hwnd List box
//
// EXIT:
//
//***************************************************************************
void ShrinkToFit( HWND hWnd, HWND hLb )
{
    LONG lCount;
    LONG lNumItems;
    LONG lTotalHeight;
    LONG lHeight;
    RECT rWnd;
    LONG lChange;

    lNumItems = (LONG)SendMessage( hLb, LB_GETCOUNT, 0, 0L );
    lTotalHeight =0;
    for( lCount=0;lCount<lNumItems; lCount++ )
    {
         lHeight = (LONG)SendMessage( hLb, LB_GETITEMHEIGHT, lCount, 0L );
         lTotalHeight+=lHeight;
    }

    // Set the height of the ListBox to the number of items in it.
    GetWindowRect (hLb, &rWnd);
    SetWindowPos( hLb, hWnd, 0,0,
        rWnd.right - rWnd.left - (CXBORDER*2 + g_cxSmIcon) ,
        lTotalHeight,
        SWP_NOMOVE | SWP_SHOWWINDOW | SWP_NOZORDER );

    // Work out how much it changed in height
    lChange = lTotalHeight - (rWnd.bottom-rWnd.top);

    // Size the parent to fit around the child.
    GetWindowRect(hWnd, &rWnd);
    SetWindowPos( hWnd,0, 0,0,
        rWnd.right - rWnd.left,
        rWnd.bottom-rWnd.top + lChange,
        SWP_NOMOVE | SWP_SHOWWINDOW | SWP_NOZORDER );
}


//***************************************************************************
//
// <Function>()
// <Explanation>
//
// ENTRY:
//      <Params>
//
// EXIT:
//      <Params>
//
//***************************************************************************
LRESULT CALLBACK dlgProcRunOnce(
                HWND hWnd,         // window handle
                UINT message,      // type of message
                WPARAM uParam,     // additional information
                LPARAM lParam)     // additional information
{
    int wmId, wmEvent;
    HANDLE hThread;

    switch (message)
    {
        case WM_DELETEITEM:
            return _HandleDeleteItem( hWnd, (LPDELETEITEMSTRUCT)lParam );

        case WM_MEASUREITEM:
            return _HandleMeasureItem(hWnd, (MEASUREITEMSTRUCT  *) lParam);

        case WM_DRAWITEM:
            return _HandleDrawItem(hWnd, (DRAWITEMSTRUCT  *) lParam);

        case WM_INITDIALOG:
            CreateGlobals( hWnd );
            DoAnyRandomOneTimeStuff();
            g_fCleanBoot = GetSystemMetrics(SM_CLEANBOOT);
            TopLeftWindow( hWnd, GetParent(hWnd) );
            RunOnceFill( GetDlgItem(hWnd,IDC_LIST2) );
            // Now calculate the size needed for the LB and resize LB and parent.
            ShrinkToFit( hWnd, GetDlgItem(hWnd,IDC_LIST2));
            hThread = CreateThread(NULL, 0, RunAppsInList, (LPVOID)GetDlgItem(hWnd,IDC_LIST2),0, &g_dwThread );
            CloseHandle(hThread);
        break;

        case WM_FINISHED:
            EndDialog(hWnd,0);
            // DestroyWindow(hWnd);
        break;

        case WM_CTLCOLORLISTBOX:
            return _HandleCtlColorListbox((HWND)lParam, (HDC)uParam);

        case WM_COMMAND:  // message: command from application menu
            wmId    = LOWORD(uParam);
            wmEvent = HIWORD(uParam);
            if( wmEvent==LBN_SELCHANGE )
            {
                // LaunchApp( (HWND) lParam, LOWORD(uParam) );
                // De-select the item now.
                break;
            }
            else
            switch (wmId)
            {
                case IDOK:
                    EndDialog( hWnd, wmId);
                break;

                default:
                    // return (DefWindowProc(hWnd, message, uParam, lParam));
                break;
            }
        break;


        default:          // Passes it on if unproccessed
           // return (DefWindowProc(hWnd, message, uParam, lParam));
        return FALSE;
    }
    return TRUE;
}