//==========================================================================================
// HISTDATA helper (should be a static member)
//==========================================================================================


#define CUrlHistoryProp IntsiteProp

//==========================================================================================
// IntsiteProp class implementation
//==========================================================================================

#ifdef DEBUG

/*----------------------------------------------------------
Purpose: Dump the properties in this object

*/
STDMETHODIMP_(void)
    CUrlHistoryProp::Dump(void)
{
    if (IsFlagSet(g_dwDumpFlags, DF_URLPROP))
    {
        TraceMsg(TF_ALWAYS, "  Intsite Property obj: %s", m_szURL);
        URLProp::Dump();
    }
}

#endif


/*----------------------------------------------------------
Purpose: Constructor for URLProp 

*/
CUrlHistoryProp::CUrlHistoryProp(void)
{
    // Don't validate this until after construction.
    
    // This object should only be allocated, not used on the stack,
    // because we don't zero-initialize the member variables.
    // Here's a sanity assertion.
    
    ASSERT(NULL == m_pintshcut);
    
    ASSERT(IS_VALID_STRUCT_PTR(this, CIntsiteProp));
    
    return;
}


/*----------------------------------------------------------
Purpose: Destructor for CUrlHistoryProp

*/
CUrlHistoryProp::~CUrlHistoryProp(void)
{
    if (m_pintshcut)
    {
        if (!m_fPrivate)
            m_pintshcut->Release();
        m_pintshcut = NULL;
    }
    
    return;
}

STDAPI CIntsiteProp_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvOut)
{
    HRESULT hres;
    
    *ppvOut = NULL;
    
    ASSERT(punkOuter==NULL)

    IUnknown * piunk = (IUnknown *)new IntsiteProp;
    if ( !piunk ) 
    {
        hres = E_OUTOFMEMORY;
    }
    else
    {
        hres = piunk->QueryInterface(riid, ppvOut);
        piunk->Release();
    }
    
    return hres;        // S_OK or E_NOINTERFACE
}

HRESULT CUrlHistoryProp::Init(void)
{
    return URLProp::Init();
}

STDMETHODIMP CUrlHistoryProp::InitFromDB(LPCTSTR pszURL, Intshcut *pintshcut, BOOL fPrivObj)
{
    // TraceMsg(DM_HISTPROP, "CUHP::InitFromDB called for %s", pszURL);

    // Initialize the in-memory property storage from the file
    // and database
    
    HRESULT hres = Init();
    if (SUCCEEDED(hres))
    {
        if (NULL == m_pintshcut)
        {
            m_fPrivate = fPrivObj;
            if (!m_fPrivate)
                pintshcut->AddRef();
            m_pintshcut = pintshcut;
        }
        else
        {
            // We can't switch from Private->Public or visaversa.
            ASSERT(fPrivObj == m_fPrivate);
        }
        
        if (pszURL)
        {
            // Is this really a URL?? 
            PARSEDURL pu;
            
            pu.cbSize = SIZEOF(pu);
            hres = ParseURL(pszURL, &pu);
            
            if (S_OK == hres)
            {
                // Yes; go ahead and initialize
                StrCpyN(m_szURL, pszURL, SIZECHARS(m_szURL));

                hres = LoadFromDB(pszURL);
            }
        }
    }
    
    return hres;
}

HRESULT CUrlHistoryProp::LoadFromDB(
    IN LPCTSTR pszURL)
{
    TraceMsg(DM_HISTPROP, "CUHP::LoadFromDB called for %s", pszURL);

    CEI_PREALLOC buf;
    CUrlHistory::s_ConvertToPrefixedUrlW(pszURL, buf.szPrefixedUrl, ARRAYSIZE(buf.szPrefixedUrl), &buf.pszFragment);
    CUrlHistory::s_RetrievePrefixedUrlInfo(buf.szPrefixedUrl, &buf);

    //
    //	if there is already an entry for this Url, then we will reuse some of the
    //	settings.  retrieve the relevant info if possible.
    //
    if (buf.pcei) {
        CHistoryData* phdPrev =  CHistoryData::s_GetHistoryData(buf.pcei);
        if (phdPrev) {
            //
            // Initialize non-string properties first
            //
            const static PROPSPEC c_aprspec[] = {
                { PRSPEC_PROPID, PID_INTSITE_FLAGS },
                { PRSPEC_PROPID, PID_INTSITE_LASTVISIT },
                { PRSPEC_PROPID, PID_INTSITE_LASTMOD },
                { PRSPEC_PROPID, PID_INTSITE_WATCH },
            };
            PROPVARIANT apropvar[ARRAYSIZE(c_aprspec)] = { 0 };

            apropvar[0].vt = VT_UI4;
            apropvar[0].lVal = phdPrev->dwFlags;
            apropvar[1].vt = VT_FILETIME;
            apropvar[1].filetime = buf.pcei->LastAccessTime;
            apropvar[2].vt = VT_FILETIME;
            apropvar[2].filetime = buf.pcei->LastModifiedTime;
            apropvar[3].vt = VT_UI4;
            apropvar[3].lVal = phdPrev->dwWatch;

            TraceMsg(DM_HISTPROP, "CUHP::InitFromDB calling WriteMultiple (wFlags=%x)", phdPrev->dwFlags);
      	    WriteMultiple(ARRAYSIZE(c_aprspec), c_aprspec, apropvar, 0);
            PropStg_DirtyMultiple(m_hstg, ARRAYSIZE(c_aprspec), c_aprspec, FALSE);

            //
            // Then, initialize others
            //
            PROPSPEC prspec;
            prspec.ulKind = PRSPEC_PROPID;

            for (const HISTEXTRA* phextPrev = phdPrev->_GetExtra();
                 phextPrev && !phextPrev->IsTerminator();
                 phextPrev = phextPrev->GetNextFast())
            {
                TraceMsg(DM_HISTPROP, "CUHP::InitFromDB found HISTEXTRA (id=%d, vt=%d)",
                         phextPrev->idExtra, phextPrev->cbExtra);

                WCHAR wszBuf[MAX_URL_STRING]; // NOTES: scope must be right
                apropvar[0].vt = phextPrev->vtExtra;

                switch(phextPrev->vtExtra) {
                case VT_LPWSTR:
                    apropvar[0].pwszVal = (LPWSTR)phextPrev->abExtra;
                    break;

                case VT_LPSTR:
                    //
                    // Notice that we always convert it to LPWSTR
                    //
                    {
                        apropvar[0].pwszVal = wszBuf;
                        LPCSTR pszExtra = (LPCSTR)phextPrev->abExtra;
                        AnsiToUnicode(pszExtra, wszBuf, ARRAYSIZE(wszBuf));
                        apropvar[0].vt = VT_LPWSTR;
                    }
                    break;

                case VT_UI4:
                case VT_I4:
                    apropvar[0].lVal = *(DWORD*)phextPrev->abExtra;
                    break;

                case VT_NULL:
                    ASSERT(phextPrev->idExtra == PID_INTSITE_FRAGMENT);
                    continue;

                default:
                    ASSERT(0);
                    continue;
                }

                prspec.propid = phextPrev->idExtra;
                WriteMultiple(1, &prspec, apropvar, 0);
                PropStg_DirtyMultiple(m_hstg, 1, &prspec, FALSE);
            }
        } else {
            TraceMsg(DM_HISTPROP, "CUHP::LoadFromDB can't get phdPrev");
        }
    } else {
        TraceMsg(DM_HISTPROP, "CUHP::LoadFromDB can't get pcei");
    }

    return S_OK;
}


struct URLHIST_ENUMPARAM {
    CUrlHistoryProp* that;
    INTERNET_CACHE_ENTRY_INFO cei;
    LPINTERNET_CACHE_ENTRY_INFO pceiPrev;
    CHistoryData* phdPrev;
    LPHISTEXTRA phextCur;
    UINT cbHistExtra;
    BOOL fDirty;
};

STDAPI s_CommitHistItem(
    IN PROPID        propid,
    IN PROPVARIANT * ppropvar,
    IN LPARAM        lParam)
{
    URLHIST_ENUMPARAM* peparam = (URLHIST_ENUMPARAM*)lParam;
    CHistoryData* phdNew = (CHistoryData*)peparam->cei.lpHeaderInfo;

    TraceMsg(DM_HISTEXTRA, "CUHP::s_CommitHistItem called for id=%d vt=%d (phextCur=%x)",
                propid, ppropvar->vt, peparam->phextCur);

    UINT cbExtra = 0;
    UINT cbRequired;

    switch(propid) {
    case PID_INTSITE_FLAGS:
        if (ppropvar->vt == VT_UI4 && phdNew) {
            TraceMsg(DM_HISTPROP, "CUHP::s_CommitHistItem updating PID_INSITE_FLAGS (%x to %x)",
                     phdNew->dwFlags, ppropvar->lVal);
            phdNew->dwFlags = ppropvar->lVal;
            peparam->fDirty = TRUE;
        }
        break;

    case PID_INTSITE_WATCH:
        if (ppropvar->vt == VT_UI4 && phdNew) {
            TraceMsg(DM_HISTPROP, "CUHP::s_CommitHistItem updating PID_INSITE_WATCH (%x to %x)",
                     phdNew->dwFlags, ppropvar->lVal);
            phdNew->dwWatch = ppropvar->lVal;
            peparam->fDirty = TRUE;
        }
        break;

    case PID_INTSITE_LASTVISIT:
    case PID_INTSITE_LASTMOD:
        // They are read-only. We can change it if we want.
        ASSERT(0);
        break;

    default:
        switch(ppropvar->vt) {
        case VT_UI4:
        case VT_I4:
            cbExtra = DW_ALIGNED(SIZEOF(HISTEXTRA)-SIZEOF(peparam->phextCur->abExtra)+SIZEOF(UINT));
            if (peparam->phextCur) {
                peparam->phextCur->cbExtra = cbExtra;
                peparam->phextCur->idExtra = propid;
                peparam->phextCur->vtExtra = ppropvar->vt;
                *(DWORD*)peparam->phextCur->abExtra = ppropvar->lVal;
                peparam->fDirty = TRUE;
            }
            break;

        case VT_LPWSTR:
            cbRequired = WideCharToMultiByte(CP_ACP, 0, ppropvar->pwszVal, -1,
                                NULL, 0, NULL, NULL);
            cbExtra = DW_ALIGNED(SIZEOF(HISTEXTRA) + cbRequired);
            if (peparam->phextCur)
            {
                peparam->phextCur->cbExtra = cbExtra;
                peparam->phextCur->idExtra = propid;
                peparam->phextCur->vtExtra = VT_LPSTR;
                WideCharToMultiByte(CP_ACP, 0, ppropvar->pwszVal, -1, 
                    (LPSTR)peparam->phextCur->abExtra, cbRequired, NULL, NULL);
                peparam->fDirty = TRUE;
            }
            break;

        case VT_EMPTY:
            if (peparam->phextCur) {
                peparam->fDirty = TRUE;
            }
            break;

        default:
            ASSERT(0);
            break;
        }

        if (peparam->phextCur) {
            // We are saving the data, move the write pointer.
            TraceMsg(DM_HISTEXTRA, "s_CommitHistItem moving phextCur forward %d bytes",
                     peparam->phextCur->cbExtra);
            peparam->phextCur = peparam->phextCur->GetNextFastForSave();
            ASSERT(peparam->phextCur->cbExtra == 0);
        } else {
            // We are calcurating the required size, just add the size.
            TraceMsg(DM_HISTEXTRA, "s_CommitHistItem adding %d", cbExtra);
            peparam->cbHistExtra += cbExtra;

            // Remove existing one. 
            if (peparam->phdPrev) {
                // FEATURE: Bad const to non-const cast
                HISTEXTRA* phextPrev =
                    (HISTEXTRA*)peparam->phdPrev->_FindExtra(propid);
    
                if (phextPrev) {
                    TraceMsg(DM_HISTEXTRA, "s_CommitHistItem invalidate an old one id=%d %d bytes",
                              phextPrev->idExtra, phextPrev->cbExtra);
                    phextPrev->vtExtra = VT_EMPTY;
                }
            }
	}
    }
    return S_OK;
};

HRESULT CUrlHistoryProp::Commit(IN DWORD dwFlags)
{
    TraceMsg(DM_HISTPROP, "CUHP::Commit called for %s", m_szURL);

    CEI_PREALLOC buf;
    CUrlHistory::s_ConvertToPrefixedUrlW(m_szURL, buf.szPrefixedUrl, ARRAYSIZE(buf.szPrefixedUrl), &buf.pszFragment);
    CUrlHistory::s_RetrievePrefixedUrlInfo(buf.szPrefixedUrl, &buf);

    HRESULT hres;
    URLHIST_ENUMPARAM eparam = { this };
    ASSERT(eparam.fDirty == FALSE);

    eparam.pceiPrev = buf.pcei;
    if (eparam.pceiPrev) {
        eparam.cei = *eparam.pceiPrev;
        eparam.phdPrev =  CHistoryData::s_GetHistoryData(eparam.pceiPrev);
    }

    // First, enemerate once to get the size for extra. 
    eparam.cei.lpHeaderInfo = NULL;
    eparam.cbHistExtra = 0;
    hres = PropStg_Enum(m_hstg, PSTGEF_DIRTY, s_CommitHistItem, (LPARAM)&eparam);

    DWORD dwFlagsPrev = 0;
    if (eparam.phdPrev) {
        eparam.cbHistExtra += eparam.phdPrev->GetTotalExtraSize();
        dwFlagsPrev = eparam.phdPrev->dwFlags;
    }

    TraceMsg(DM_HISTEXTRA, "CUHP::Commit total size is %d", eparam.cbHistExtra);

    CHistoryData* phdNew = CHistoryData::s_AllocateHeaderInfo(
                                eparam.cbHistExtra, eparam.phdPrev,
                                &eparam.cei.dwHeaderInfoSize);

    if (phdNew) {
        eparam.cei.lpHeaderInfo = (LPTSTR)phdNew;
        eparam.phextCur = phdNew->_GetExtra();

        // Enumerate again to fill the extra data. 
        hres = PropStg_Enum(m_hstg, PSTGEF_DIRTY, s_CommitHistItem, (LPARAM)&eparam);

        if (eparam.fDirty)
        {
            if (eparam.phdPrev) {
                eparam.phdPrev->CopyExtra(eparam.phextCur);
            }

            TraceMsg(DM_HISTPROP, "CUHP::Commit It's dirty. save it (header = %d bytes)",
                     eparam.cei.dwHeaderInfoSize);

            ASSERT(eparam.cbHistExtra == phdNew->GetTotalExtraSize());

            CUrlHistory::s_CommitUrlCacheEntry(buf.szPrefixedUrl, &eparam.cei);

            if ((dwFlagsPrev & PIDISF_RECENTLYCHANGED)
                    != (phdNew->dwFlags & PIDISF_RECENTLYCHANGED))
            {
                // Yes; update the images
                CUrlHistory::s_UpdateIcon(m_pintshcut, dwFlagsPrev);
            }
        }

        LocalFree(phdNew);
        phdNew = NULL;
    }

    return hres;
}


