/****************************************************************************\
 *
 *   PICSUSER.C -- Structure for holding user information
 *
 *     Created:      02/29/96 gregj
 *                from original sources by t-jasont
 *     
\****************************************************************************/

/*Includes------------------------------------------------------------------*/
#include "msrating.h"
#include "mslubase.h"
#include "debug.h"

BOOL GetRegBool(HKEY hKey, LPCSTR pszValueName, BOOL fDefault)
{
    BOOL fRet = fDefault;
    DWORD dwSize, dwValue, dwType;
    UINT uErr;

    dwSize = sizeof(dwValue);

    uErr = RegQueryValueEx(hKey, pszValueName, NULL, &dwType, 
                            (LPBYTE)&dwValue, &dwSize);

    if (uErr == ERROR_SUCCESS)
    {
        if ((dwType == REG_DWORD) || (dwType == REG_BINARY && dwSize >= sizeof(fRet)))
            fRet = dwValue;
    }

    return fRet;
}


void SetRegBool(HKEY hkey, LPCSTR pszValueName, BOOL fValue)
{
    RegSetValueEx(hkey, pszValueName, 0, REG_DWORD, (LPBYTE)&fValue, sizeof(fValue));
}


PicsRatingSystem *FindInstalledRatingSystem(LPCSTR pszRatingService)
{
    UINT cServices = gPRSI->arrpPRS.Length();

    for (UINT i=0; i<cServices; i++) {
        PicsRatingSystem *pPRS = gPRSI->arrpPRS[i];
        if (!(pPRS->dwFlags & PRS_ISVALID) || !pPRS->etstrRatingService.fIsInit())
            continue;
        if (!::strcmpf(pPRS->etstrRatingService.Get(), pszRatingService))
            return pPRS;
    }
    return NULL;
}


PicsCategory *FindInstalledCategory(array<PicsCategory *>&arrpPC, LPCSTR pszName)
{
    UINT cCategories = arrpPC.Length();

    for (UINT i=0; i<cCategories; i++) {
        LPSTR pszThisName = arrpPC[i]->etstrTransmitAs.Get();
        if (!::strcmpf(pszThisName, pszName))
            return arrpPC[i];
        if (!::strncmpf(pszThisName, pszName, strlenf(pszThisName)) &&
            arrpPC[i]->arrpPC.Length() > 0) {
            PicsCategory *pCategory = FindInstalledCategory(arrpPC[i]->arrpPC, pszName);
            if (pCategory != NULL)
                return pCategory;
        }
    }
    return NULL;
}


UserRating::UserRating()
    : NLS_STR(NULL),
      m_nValue(0),
      m_pNext(NULL),
      m_pPC(NULL)
{
}


UserRating::UserRating(UserRating *pCopyFrom)
    : NLS_STR(*pCopyFrom),
      m_nValue(pCopyFrom->m_nValue),
      m_pNext(NULL),
      m_pPC(pCopyFrom->m_pPC)
{
}


UserRating::~UserRating()
{
    // needed to destruct name string
}


UserRating *UserRating::Duplicate(void)
{
    UserRating *pNew = new UserRating(this);
    return pNew;
}


UserRatingSystem::UserRatingSystem()
    : NLS_STR(NULL),
      m_pRatingList(NULL),
      m_pNext(NULL),
      m_pPRS(NULL)
{

}


UserRatingSystem::UserRatingSystem(UserRatingSystem *pCopyFrom)
    : NLS_STR(*pCopyFrom),
      m_pRatingList(NULL),
      m_pNext(NULL),
      m_pPRS(pCopyFrom->m_pPRS)
{

}


UserRatingSystem *UserRatingSystem::Duplicate(void)
{
    UserRatingSystem *pNew = new UserRatingSystem(this);
    if (pNew != NULL) {
        UserRating *pRating;

        for (pRating = m_pRatingList; pRating != NULL; pRating = pRating->m_pNext) {
            UserRating *pNewRating = pRating->Duplicate();
            if (pNewRating != NULL) {
                if (pNew->AddRating(pNewRating) != ERROR_SUCCESS) {
                    delete pNewRating;
                    pNewRating = NULL;
                }
            }

            if (pNewRating == NULL)
                break;
        }
    }

    return pNew;
}


UserRatingSystem *DuplicateRatingSystemList(UserRatingSystem *pOld)
{
    UserRatingSystem *pNewList = NULL;

    while (pOld != NULL) {
        UserRatingSystem *pNewEntry = pOld->Duplicate();
        if (pNewEntry == NULL)
            break;

        pNewEntry->m_pNext = pNewList;
        pNewList = pNewEntry;

        pOld = pOld->m_pNext;
    }

    return pNewList;
}


UserRatingSystem::~UserRatingSystem()
{
    UserRating *pRating, *pNext;

    for (pRating = m_pRatingList; pRating != NULL; )
    {
        pNext = pRating->m_pNext;
        delete pRating;
        pRating = pNext;
    }

#ifdef DEBUG
    m_pRatingList = NULL;
#endif
}


UserRating *UserRatingSystem::FindRating(LPCSTR pszTransmitName)
{
    UserRating *p;

    for (p = m_pRatingList; p != NULL; p = p->m_pNext)
    {
        if (!::stricmpf(p->QueryPch(), pszTransmitName))
            break;
    }

    return p;
}


UINT UserRatingSystem::AddRating(UserRating *pRating)
{
    pRating->m_pNext = m_pRatingList;
    m_pRatingList = pRating;
    return ERROR_SUCCESS;
}


UINT UserRatingSystem::ReadFromRegistry(HKEY hkeyProvider)
{
    UINT err;
    DWORD iValue = 0;
    char szValueName[MAXPATHLEN];
    DWORD cchValue;
    DWORD dwValue;
    DWORD cbData;

    do {
        cchValue = sizeof(szValueName);
        cbData = sizeof(dwValue);
        err = RegEnumValue(hkeyProvider, iValue, szValueName, &cchValue,
                           NULL, NULL, (LPBYTE)&dwValue, &cbData);
        if (err == ERROR_SUCCESS && cbData >= sizeof(dwValue)) {
            UserRating *pRating = new UserRating;
            if (pRating != NULL) {
                if (pRating->QueryError()) {
                    err = pRating->QueryError();
                }
                else {
                    pRating->SetName(szValueName);
                    pRating->m_nValue = (INT)dwValue;
                    if (m_pPRS != NULL)
                        pRating->m_pPC = FindInstalledCategory(m_pPRS->arrpPC, szValueName);
                    err = AddRating(pRating);
                }
                if (err != ERROR_SUCCESS)
                {
                    delete pRating;
                    pRating = NULL;
                }
            }
            else
                err = ERROR_NOT_ENOUGH_MEMORY;
        }
        iValue++;
    } while (err == ERROR_SUCCESS);

    if (err == ERROR_NO_MORE_ITEMS)
        err = ERROR_SUCCESS;

    return err;
}


UINT UserRatingSystem::WriteToRegistry(HKEY hkeyRatings)
{
    UserRating *pRating;
    UINT err = ERROR_SUCCESS;
    CRegKey         key;

    err = key.Create( hkeyRatings, QueryPch() );
    if (err != ERROR_SUCCESS)
    {
        TraceMsg( TF_WARNING, "UserRatingSystem::WriteToRegistry() - Failed to create Ratings Key QueryPch()='%s'!", QueryPch() );
        return err;
    }

    for (pRating = m_pRatingList; pRating != NULL; pRating = pRating->m_pNext)
    {
        err = key.SetValue( pRating->m_nValue, pRating->QueryPch() );
        if (err != ERROR_SUCCESS)
        {
            TraceMsg( TF_WARNING, "UserRatingSystem::WriteToRegistry() - Failed to set Ratings Value pRating->QueryPch()='%s'!", pRating->QueryPch() );
            break;
        }
    }

    return err;
}


PicsUser::PicsUser()
    : nlsUsername(NULL),
      fAllowUnknowns(FALSE),
      fPleaseMom(TRUE),
      fEnabled(TRUE),
      m_pRatingSystems(NULL)
{
}


PicsRatingSystemInfo::~PicsRatingSystemInfo()
{
    arrpPRS.DeleteAll();

    if ( pUserObject )
    {
        delete pUserObject;
        pUserObject = NULL;
    }
}


void DestroyRatingSystemList(UserRatingSystem *pList)
{
    UserRatingSystem *pSystem, *pNext;

    for (pSystem = pList; pSystem != NULL; )
    {
        pNext = pSystem->m_pNext;
        delete pSystem;
        pSystem = pNext;
    }
}


PicsUser::~PicsUser()
{
    DestroyRatingSystemList(m_pRatingSystems);
#ifdef DEBUG
    m_pRatingSystems = NULL;
#endif
}


UserRatingSystem *FindRatingSystem(UserRatingSystem *pList, LPCSTR pszSystemName)
{
    UserRatingSystem *p;

    for (p = pList; p != NULL; p = p->m_pNext)
    {
        if (!::strcmpf(p->QueryPch(), pszSystemName))
            break;
    }

    return p;
}


UINT PicsUser::AddRatingSystem(UserRatingSystem *pRatingSystem)
{
    pRatingSystem->m_pNext = m_pRatingSystems;
    m_pRatingSystems = pRatingSystem;
    return ERROR_SUCCESS;
}


UINT PicsUser::ReadFromRegistry(HKEY hkey, char *pszUserName)
{
    CRegKey             keyUser;

    nlsUsername = pszUserName;

    UINT err = keyUser.Open( hkey, pszUserName, KEY_READ );
    if (err != ERROR_SUCCESS)
    {
        TraceMsg( TF_WARNING, "PicsUser::ReadFromRegistry() - Failed keyUser Open to pszUserName='%s'!", pszUserName );
        return err;
    }

    fAllowUnknowns = GetRegBool( keyUser.m_hKey, VAL_UNKNOWNS, FALSE);
    fPleaseMom = GetRegBool( keyUser.m_hKey, VAL_PLEASEMOM, TRUE);
    fEnabled = GetRegBool( keyUser.m_hKey, VAL_ENABLED, TRUE);

    {
        char szKeyName[MAXPATHLEN];
        int j = 0;
        // enumerate the subkeys, which are rating systems
        while ( ( err = RegEnumKey( keyUser.m_hKey, j, szKeyName, sizeof(szKeyName) ) ) == ERROR_SUCCESS )
        {
            CRegKey             keyProvider;

            if ( ( err = keyProvider.Open( keyUser.m_hKey, szKeyName, KEY_READ ) != ERROR_SUCCESS ) )
            {
                TraceMsg( TF_WARNING, "PicsUser::ReadFromRegistry() - Failed keyProvider Open to szKeyName='%s'!", szKeyName );
                break;
            }

            UserRatingSystem *pRatingSystem = new UserRatingSystem;
            if (pRatingSystem == NULL)
            {
                err = ERROR_NOT_ENOUGH_MEMORY;
                break;
            }

            if (pRatingSystem->QueryError())
            {
                err = pRatingSystem->QueryError();
            }
            else
            {
                pRatingSystem->SetName(szKeyName);
                pRatingSystem->m_pPRS = FindInstalledRatingSystem(szKeyName);
                err = pRatingSystem->ReadFromRegistry( keyProvider.m_hKey );
                if (err == ERROR_SUCCESS)
                {
                    err = AddRatingSystem(pRatingSystem);
                }
            }

            if (err != ERROR_SUCCESS)
            {
                delete pRatingSystem;
                pRatingSystem = NULL;
            }

            j++;
        }
    }

    // end of enum will report ERROR_NO_MORE_ITEMS, don't report this as error
    if (err == ERROR_NO_MORE_ITEMS)
    {
        err = ERROR_SUCCESS;
    }

    return err;
}

BOOL PicsUser::NewInstall()
{
    nlsUsername = szDefaultUserName;
    fAllowUnknowns = FALSE;
    fPleaseMom = TRUE;
    fEnabled = TRUE;
    
    return TRUE;
}


UINT PicsUser::WriteToRegistry(HKEY hkey)
{
    UINT err;

    //Delete it to clean out registry
    MyRegDeleteKey(hkey, nlsUsername.QueryPch());

    CRegKey             keyUser;

    err = keyUser.Create( hkey, nlsUsername.QueryPch() );
    if (err != ERROR_SUCCESS)
    {
        TraceMsg( TF_WARNING, "PicsUser::WriteToRegistry() - Failed to Create User Key nlsUsername='%s'!", nlsUsername.QueryPch() );
        return err;
    }

    SetRegBool( keyUser.m_hKey, VAL_UNKNOWNS, fAllowUnknowns);
    SetRegBool( keyUser.m_hKey, VAL_PLEASEMOM, fPleaseMom);
    SetRegBool( keyUser.m_hKey, VAL_ENABLED, fEnabled);

    {
        UserRatingSystem *pSystem;

        /* Note, if any user settings correspond to invalid or unknown
         * rating systems, we still save them here.  That way, the user
         * settings don't get lost if the supervisor later fixes a problem
         * with a .RAT file.
         *
         * We clean up user settings to match the installed rating systems
         * in the add/remove rating systems dialog code.
         */
        for (pSystem = m_pRatingSystems; pSystem != NULL; pSystem = pSystem->m_pNext)
        {
            err = pSystem->WriteToRegistry( keyUser.m_hKey );
            if (err != ERROR_SUCCESS)
            {
                TraceMsg( TF_WARNING, "PicsUser::WriteToRegistry() - Failed pSystem->WriteToRegistry()!" );
                break;
            }
        }
    }

    return err;
}


PicsUser *GetUserObject(LPCSTR pszUsername /* = NULL */ )
{
    ASSERT( gPRSI );

    return gPRSI ? gPRSI->pUserObject : NULL;
}


void DeleteUserSettings(PicsRatingSystem *pPRS)
{
    if (!pPRS->etstrRatingService.fIsInit())
        return;        /* can't recognize user settings without this */

    PicsUser *pPU = GetUserObject();

    UserRatingSystem **ppLast = &pPU->m_pRatingSystems;

    while (*ppLast != NULL)
    {
        if (!stricmpf((*ppLast)->QueryPch(), pPRS->etstrRatingService.Get()))
        {
            UserRatingSystem *pCurrent = *ppLast;
            *ppLast = pCurrent->m_pNext;    /* remove from list */
            delete pCurrent;
            pCurrent = NULL;
            break;
        }
        else
        {
            ppLast = &((*ppLast)->m_pNext);
        }
    }
}


void CheckUserCategory(UserRatingSystem *pURS, PicsCategory *pPC)
{
    for (UserRating *pRating = pURS->m_pRatingList;
         pRating != NULL;
         pRating = pRating->m_pNext)
    {
        if (!::strcmpf(pRating->QueryPch(), pPC->etstrTransmitAs.Get()))
            break;
    }

    if (pRating == NULL) {
        /* User setting not found for this category.  Add one. */

        pRating = new UserRating;
        if (pRating != NULL) {
            pRating->SetName(pPC->etstrTransmitAs.Get());
            pRating->m_pPC = pPC;
            pRating->m_pNext = pURS->m_pRatingList;
            pURS->m_pRatingList = pRating;
            if ((pPC->etfLabelled.fIsInit() && pPC->etfLabelled.Get()) ||
                !pPC->etnMin.fIsInit())
                pRating->m_nValue = 0;
            else
                pRating->m_nValue = pPC->etnMin.Get();
        }
    }

    /* Check all subcategories in this category as well.
     */
    UINT cCategories = pPC->arrpPC.Length();
    for (UINT i=0; i<cCategories; i++)
        CheckUserCategory(pURS, pPC->arrpPC[i]);
}


void CheckUserSettings(PicsRatingSystem *pPRS)
{
    if (pPRS == NULL || !(pPRS->dwFlags & PRS_ISVALID) ||
        !pPRS->etstrRatingService.fIsInit())
        return;

    PicsUser *pPU = GetUserObject();

    UserRatingSystem **ppLast = &pPU->m_pRatingSystems;

    while (*ppLast != NULL) {
        if (!stricmpf((*ppLast)->QueryPch(), pPRS->etstrRatingService.Get())) {
            break;
        }
        ppLast = &((*ppLast)->m_pNext);
    }

    if (*ppLast == NULL) {
        *ppLast = new UserRatingSystem;
        if (*ppLast == NULL)
            return;
        (*ppLast)->SetName(pPRS->etstrRatingService.Get());
    }

    UserRatingSystem *pCurrent = *ppLast;

    pCurrent->m_pPRS = pPRS;

    /* First go through all the settings for the user and make sure the
     * categories are valid.  If not, delete them.
     */
    UserRating **ppRating = &pCurrent->m_pRatingList;
    while (*ppRating != NULL)
    {
        UserRating *pRating = *ppRating;
        pRating->m_pPC = FindInstalledCategory(pPRS->arrpPC, pRating->QueryPch());
        if (pRating->m_pPC == NULL)
        {
            *ppRating = pRating->m_pNext;        /* remove from list */
            delete pRating;
            pRating = NULL;
        }
        else
        {
            ppRating = &pRating->m_pNext;
        }
    }

    /* Now go through all the categories in the rating system and make
     * sure the user has settings for them.  If any are missing, add
     * settings for the default values (minimums).
     */
    UINT cCategories = pPRS->arrpPC.Length();
    for (UINT i=0; i<cCategories; i++)
        CheckUserCategory(pCurrent, pPRS->arrpPC[i]);
}