#include "enum.h"

BOOL GetVersion(LPCONTROLPIDL pcpidl, LPTSTR lpszBuf);
BOOL GetTimeInfo(
               BOOL bCreation, LPCONTROLPIDL pcpidl, 
               FILETIME* lpTime, LPTSTR lpszBuf, BOOL bShowTime);

// Also defined in nt\private\inet\urlmon\isctrl.cxx
const TCHAR *g_pszLastCheckDateKey = "LastCheckDate";
const TCHAR *g_pszUpdateInfo = "UpdateInfo";

#if defined(ALIGNMENT_MACHINE)
BOOL
__inline
static
uaGetControlInfo(
    IN     HANDLE hControlHandle,
    IN     UINT nFlag,
    IN OUT DWORD UNALIGNED *lpdwuData,
    IN OUT LPTSTR lpszBuf,
    IN     int nBufLen
    )
{
    DWORD dwData;
    LPDWORD lpdwData;
    BOOL result;

    if (lpdwuData != NULL) {
        dwData = *lpdwuData;
	lpdwData = &dwData;
    } else {
        lpdwData = NULL;
    }

    result = GetControlInfo( hControlHandle,
                             nFlag,
			     lpdwData,
			     lpszBuf,
			     nBufLen );

    if (lpdwuData != NULL) {
        *lpdwuData = dwData;
    }

    return result;
}

BOOL
__inline
static
uaGetTimeInfo(
    IN     BOOL bCreation,
    IN OUT LPCONTROLPIDL pcpidl,
    IN OUT FILETIME UNALIGNED *lpuTime,
    IN OUT LPTSTR lpszBuf,
    IN     BOOL bShowTime
    )
{
    FILETIME time;
    LPFILETIME lpTime;
    BOOL result;

    if (lpuTime != NULL) {
        time = *lpuTime;
	lpTime = &time;
    } else {
        lpTime = NULL;
    }

    result = GetTimeInfo( bCreation,
                          pcpidl,
			  lpTime,
			  lpszBuf,
			  bShowTime );

    if (lpuTime != NULL) {
        *lpuTime = time;
    }

    return result;
}

#else
#define uaGetControlInfo GetControlInfo
#define uaGetTimeInfo    GetTimeInfo
#endif

    
    

///////////////////////////////////////////////////////////////////////////////
// IEnumIDList methods

CControlFolderEnum::CControlFolderEnum(STRRET &str, LPCITEMIDLIST pidl, UINT shcontf) :
    m_shcontf(shcontf)
{
    DebugMsg(DM_TRACE, TEXT("cfe - CControlFolderEnum() called"));
    m_cRef = 1;
    DllAddRef();

    m_bEnumStarted = FALSE;
    m_hEnumControl = NULL;

    StrRetToBuf(&str, pidl, m_szCachePath, MAX_PATH);

    SHGetMalloc(&m_pMalloc);          // won't fail
}

CControlFolderEnum::~CControlFolderEnum()
{
    Assert(m_cRef == 0);         // we should always have a zero ref count here
    DebugMsg(DM_TRACE, TEXT("cfe - ~CControlFolderEnum() called."));
    DllRelease();
}

HRESULT CControlFolderEnum_CreateInstance(
                                      LPITEMIDLIST pidl, 
                                      UINT shcontf,
                                      LPENUMIDLIST *ppeidl)

{
    DebugMsg(DM_TRACE,("cfe - CreateInstance() called."));

    if (pidl == NULL)
        return HRESULT_FROM_WIN32(ERROR_BAD_ARGUMENTS);

    HRESULT hr;
    LPSHELLFOLDER pshf = NULL;
    if (FAILED(hr = SHGetDesktopFolder(&pshf)))
        return hr;

    STRRET name;
    hr = pshf->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &name);
    if (FAILED(hr))
        return hr;

    *ppeidl = NULL;                 // null the out param

    CControlFolderEnum *pCFE = new CControlFolderEnum(name, pidl, shcontf);
    if (!pCFE)
        return E_OUTOFMEMORY;
    
    *ppeidl = pCFE;

    return S_OK;
}

BOOL GetVersion(LPCONTROLPIDL pcpidl, LPTSTR lpszBuf)
{
    Assert(pcpidl != NULL);
    LPCTSTR pszLocation = GetStringInfo(pcpidl, SI_LOCATION);

    DWORD dwBufLen;
    DWORD dwHandle;
    BOOL fResult = FALSE;
    UINT uLen;
    VS_FIXEDFILEINFO *pVersionInfo = NULL;

    // Quick copy to handle failure cases
    lstrcpy(lpszBuf, g_szUnknownData);

    if ((dwBufLen = ::GetFileVersionInfoSize(
                                    (LPTSTR)pszLocation, 
                                    &dwHandle)) == 0)
    return FALSE;

    BYTE *pbData = new BYTE[dwBufLen];
    if (pbData == NULL)
        return FALSE;

    if (GetFileVersionInfo((LPTSTR)pszLocation, dwHandle, dwBufLen, pbData))
    {
        fResult = VerQueryValue(pbData, "\\", (LPVOID*)&pVersionInfo, &uLen);
        
        if (fResult)
        {
            wsprintf(lpszBuf, "%d,%d,%d,%d",
                    (pVersionInfo->dwFileVersionMS & 0xffff0000)>>16,
                    (pVersionInfo->dwFileVersionMS & 0xffff),
                    (pVersionInfo->dwFileVersionLS & 0xffff0000)>>16,
                    (pVersionInfo->dwFileVersionLS & 0xffff));
        }

    }

    delete [] pbData;

    return fResult;
}

BOOL GetTimeInfo(
             BOOL bCreation, 
             LPCONTROLPIDL pcpidl, 
             FILETIME* lpTime,
             LPTSTR lpszBuf,
             BOOL bShowTime)
{
    Assert(pcpidl != NULL);
        Assert (lpszBuf != NULL);
    if (pcpidl == NULL || lpszBuf == NULL)
        return FALSE;

    LPCTSTR pszLocation = GetStringInfo(pcpidl, SI_LOCATION);

    BOOL fResult = TRUE;
    HANDLE hFile = NULL;
        WIN32_FIND_DATA findFileData;
        TCHAR szTime[TIMESTAMP_MAXSIZE];
    TCHAR szDate[TIMESTAMP_MAXSIZE];
        SYSTEMTIME sysTime;
        FILETIME localTime;

        hFile = FindFirstFile(pszLocation, &findFileData);

    if (hFile != INVALID_HANDLE_VALUE)
    {
                // Get the creation time and date information.
        if (bCreation)
        {
            *lpTime = findFileData.ftCreationTime;
                    FileTimeToLocalFileTime(&findFileData.ftCreationTime, &localTime);
        }
        else
        {
            *lpTime = findFileData.ftLastAccessTime;
                    FileTimeToLocalFileTime(&findFileData.ftLastAccessTime, &localTime);
        }

        FileTimeToSystemTime(&localTime, &sysTime);

        GetDateFormat(
            LOCALE_SYSTEM_DEFAULT,
            DATE_SHORTDATE,
            &sysTime,
            NULL,
            szDate,
            TIMESTAMP_MAXSIZE);

        lstrcpy(lpszBuf, szDate);

        if (bShowTime)
        {
            GetTimeFormat(
                LOCALE_SYSTEM_DEFAULT,
                TIME_NOSECONDS,
                &sysTime,
                NULL,
                szTime,
                TIMESTAMP_MAXSIZE
                );

            lstrcat(lpszBuf, TEXT(" "));
            lstrcat(lpszBuf, szTime);
        }

            FindClose(hFile);
    }
    else
    {
        fResult = FALSE;
        lstrcpy(lpszBuf, g_szUnknownData);
        lpTime->dwLowDateTime = lpTime->dwHighDateTime = 0;
    }

        return fResult;
}

LPCONTROLPIDL CreateControlPidl(IMalloc *pmalloc, HANDLE hControl)
{
    Assert(pmalloc != NULL);

    DWORD dw;
    GetControlInfo(hControl, GCI_TOTALFILES, &dw, NULL, 0);
    ULONG ulSize = sizeof(CONTROLPIDL) + sizeof(USHORT);
    ulSize += (dw - 1) * sizeof(DEPENDENTFILEINFO);
    LPCONTROLPIDL pcpidl = (LPCONTROLPIDL)pmalloc->Alloc(ulSize);

    if (pcpidl)
    {
        memset(pcpidl, 0, ulSize);
        pcpidl->cb = (USHORT)(ulSize - sizeof(USHORT));

        pcpidl->ci.cTotalFiles = (UINT)dw;
        uaGetControlInfo(hControl, GCI_TOTALSIZE, &(pcpidl->ci.dwTotalFileSize), NULL, 0);
        uaGetControlInfo(hControl, GCI_SIZESAVED, &(pcpidl->ci.dwTotalSizeSaved), NULL, 0);
        GetControlInfo(hControl, GCI_NAME, NULL, pcpidl->ci.szName, CONTROLNAME_MAXSIZE);
        GetControlInfo(hControl, GCI_FILE, NULL, pcpidl->ci.szFile, MAX_PATH);
        GetControlInfo(hControl, GCI_CLSID, NULL, pcpidl->ci.szCLSID, MAX_CLSID_LEN);
        GetControlInfo(hControl, GCI_TYPELIBID, NULL, pcpidl->ci.szTypeLibID, MAX_CLSID_LEN);
        uaGetTimeInfo(TRUE, pcpidl, &(pcpidl->ci.timeCreation), pcpidl->ci.szCreation, TRUE);
        uaGetTimeInfo(FALSE, pcpidl, &(pcpidl->ci.timeLastAccessed), pcpidl->ci.szLastAccess, FALSE);

        GetControlInfo(hControl, GCI_CODEBASE, NULL, pcpidl->ci.szCodeBase, INTERNET_MAX_URL_LENGTH);
        uaGetControlInfo(hControl, GCI_ISDISTUNIT, &(pcpidl->ci.dwIsDistUnit), NULL, 0);
        uaGetControlInfo(hControl, GCI_STATUS, &(pcpidl->ci.dwStatus), NULL, 0);
        uaGetControlInfo(hControl, GCI_HAS_ACTIVEX, &(pcpidl->ci.dwHasActiveX), NULL, 0);
        uaGetControlInfo(hControl, GCI_HAS_JAVA, &(pcpidl->ci.dwHasJava), NULL, 0);
        if (pcpidl->ci.dwIsDistUnit)
        {
            GetControlInfo(hControl, GCI_DIST_UNIT_VERSION, NULL, pcpidl->ci.szVersion, VERSION_MAXSIZE);
        }
        else
        {
            GetVersion(pcpidl, pcpidl->ci.szVersion);
        }

        LONG lResult = ERROR_SUCCESS;
        DEPENDENTFILEINFO UNALIGNED *pInfo = &(pcpidl->ci.dependentFile);
        dw = GetTotalNumOfFiles(pcpidl);
        for (UINT iFile = 0; iFile < (UINT)dw; iFile++)
        {
    	    DWORD alignedSize = pInfo->dwSize;
            lResult = GetControlDependentFile(
                                       iFile,
                                       hControl,
                                       pInfo->szFile,
                                       &alignedSize,
                                       TRUE);
            pInfo->dwSize = alignedSize;
            if (lResult != ERROR_SUCCESS)
                break;
            pInfo = (LPDEPENDENTFILEINFO)(pInfo + 1);
        }
    }

    return pcpidl;
}

//////////////////////////////////
//
// IUnknown Methods...
//
HRESULT CControlFolderEnum::QueryInterface(REFIID iid,void **ppv)
{
    DebugMsg(DM_TRACE, TEXT("cfe - QueryInterface called."));
    
    if ((iid == IID_IEnumIDList) || (iid == IID_IUnknown))
    {
        *ppv = (void *)this;
        AddRef();
        return S_OK;
    }
    
    *ppv = NULL;
    return E_NOINTERFACE;
}

ULONG CControlFolderEnum::AddRef(void)
{
    return ++m_cRef;
}

ULONG CControlFolderEnum::Release(void)
{
    if (--m_cRef)
        return m_cRef;

    delete this;
    return 0;
}

HRESULT CControlFolderEnum::Next(
                             ULONG celt, 
                             LPITEMIDLIST *rgelt, 
                             ULONG *pceltFetched)
{
    DebugMsg(DM_TRACE, TEXT("cfe - Next() called."));

    // If asking for stuff we don't have, say we don't have any
    if (!(m_shcontf & SHCONTF_NONFOLDERS))
        return S_FALSE;

    LONG lres = ERROR_SUCCESS;
    HANDLE hControl = NULL;
    LPCONTROLPIDL pcpidl = NULL;

    lres = (!m_bEnumStarted ? 
                  FindFirstControl(m_hEnumControl, hControl, m_szCachePath) :
                  FindNextControl(m_hEnumControl, hControl));

    if (pceltFetched)
        *pceltFetched = (lres == ERROR_SUCCESS ? 1 : 0);

    if (lres != ERROR_SUCCESS)
        goto EXIT_NEXT;

    pcpidl = CreateControlPidl(m_pMalloc, hControl);
    if (pcpidl == NULL)
    {
        lres = ERROR_NOT_ENOUGH_MEMORY;
        goto EXIT_NEXT;
    }

    m_bEnumStarted = TRUE;
    rgelt[0] = (LPITEMIDLIST)pcpidl;

EXIT_NEXT:

    ReleaseControlHandle(hControl);

    if (lres != ERROR_SUCCESS)
    {
        if (pcpidl != NULL)
        {
            m_pMalloc->Free(pcpidl);
            pcpidl = NULL;
        }
        FindControlClose(m_hEnumControl);
        m_bEnumStarted = FALSE;
        rgelt[0] = NULL;
    }

    return HRESULT_FROM_WIN32(lres);
}

HRESULT CControlFolderEnum::Skip(ULONG celt)
{
    DebugMsg(DM_TRACE, TEXT("cfe - Skip() called."));
    return E_NOTIMPL;
}

HRESULT CControlFolderEnum::Reset()
{
    DebugMsg(DM_TRACE, TEXT("cfe - Reset() called."));
    return E_NOTIMPL;
}

HRESULT CControlFolderEnum::Clone(IEnumIDList **ppenum)
{
    DebugMsg(DM_TRACE, TEXT("cfe - Clone() called."));
    return E_NOTIMPL;
}
