#include "pch.h"
#pragma hdrstop
#include "global.h"
#include "ncreg.h"
#include "ncstring.h"
#include "ncxbase.h"
#include "param.h"
#include "resource.h"
#include "util.h"

CParam::CParam ()
:   m_fInit(FALSE),
    m_eType(VALUETYPE_UNKNOWN),
    m_hkRoot(NULL),
    m_pszKeyName(NULL),
    m_pszDesc(NULL),
    m_pszHelpFile(NULL),
    m_dwHelpContext(0),
    m_uLimitText(0),
    m_hkEnum(NULL),
    m_fOptional(FALSE),
    m_fModified(FALSE),
    m_fReadOnly(FALSE),
    m_fOEMText(FALSE),
    m_fUppercase(FALSE)
{
}

BOOL CParam::FInit(HKEY hkRoot, HKEY hkNdiParam, PWSTR pszSubKey)
{
    HRESULT hr = S_OK;
    DWORD   cbBuf;
    BYTE    szBuf[VALUE_SZMAX];
    UINT    uTemp;
    DWORD   dwType;
    HKEY    hkParamInfo;

    // store hkRoot, pszSubKey for future reference
    m_hkRoot = hkRoot;
    m_pszKeyName = new WCHAR[lstrlenW (pszSubKey) + 1];

	if (m_pszKeyName == NULL)
	{
		return(FALSE);
	}

    lstrcpyW (m_pszKeyName, pszSubKey);

    hr = HrRegOpenKeyEx(hkNdiParam, pszSubKey, KEY_READ,
                        &hkParamInfo);
    if (FAILED(hr))
    {
        hkParamInfo = NULL;
        goto error;
    }

    // Get the parameter type, use EDIT if none specified
    // range values (etc.) for the type. If 'type' is empty
    // or invalid, the "int" type is returned.
    cbBuf = sizeof(szBuf);
    hr = HrRegQueryValueEx(hkParamInfo,c_szRegParamType,&dwType,szBuf,&cbBuf);
    if (SUCCEEDED(hr))
    {
        AssertSz(REG_SZ == dwType,
                 "Expecting REG_SZ type but got something else.");
    }
    else
    {
        ((PWCHAR)szBuf)[0] = L'\0';
    }

    InitParamType((PTSTR)szBuf);

    // Get the description text
    cbBuf = sizeof(szBuf);
    hr = HrRegQueryValueEx(hkParamInfo,c_szRegParamDesc,&dwType,szBuf,&cbBuf);
    if (SUCCEEDED(hr))
    {
        AssertSz(REG_SZ == dwType,
                 "Expecting REG_SZ type but got something else.");
    }
    else
    {
        // No description string
        lstrcpyW((WCHAR *)szBuf, SzLoadIds (IDS_NO_DESCRIPTION));
    }

    // allocate and store description
    m_pszDesc = new WCHAR[lstrlenW((WCHAR *)szBuf) + 1];

	if (m_pszDesc == NULL)
	{
		return(FALSE);
	}

    lstrcpyW(m_pszDesc, (WCHAR *)szBuf);

    // Optional parameter
    m_fOptional = FALSE;
    uTemp = Reg_QueryInt(hkParamInfo,c_szRegParamOptional,0);

    if (uTemp != 0)
    {
        m_fOptional = TRUE;
    }

    // Help file info
    m_pszHelpFile = NULL;
    m_dwHelpContext = 0;
    cbBuf = sizeof(szBuf);
    hr = HrRegQueryValueEx(hkParamInfo,c_szRegParamHelpFile,&dwType,
                           szBuf,&cbBuf);
    if (SUCCEEDED(hr))
    {
        AssertSz(REG_SZ == dwType,
                 "Expecting REG_SZ type but got something else.");
        m_pszHelpFile = new WCHAR[lstrlenW((WCHAR *)szBuf)+1];

		if (m_pszHelpFile == NULL)
		{
			return(FALSE);
		}

        lstrcpyW(m_pszHelpFile, (WCHAR *)szBuf);
        m_dwHelpContext = Reg_QueryInt(hkParamInfo,c_szRegParamHelpContext,0);
    }

    // Numeric Type Info
    if (m_vValue.IsNumeric())
    {
        // if no step value in registry, default to 1 (default already
        // set in FInitParamType() )
        m_vStep.FLoadFromRegistry(hkParamInfo,c_szRegParamStep);
        if (m_vStep.GetNumericValueAsDword() == 0)
        {
            m_vStep.SetNumericValue(1);
        }

        // get m_vMix and m_vMax from registry (no effect if doesn't exist,
        // defaults were set in FInitParamType() )
        (VOID) m_vMin.FLoadFromRegistry(hkParamInfo,c_szRegParamMin);
        (VOID) m_vMax.FLoadFromRegistry(hkParamInfo,c_szRegParamMax);
    }

    // Edit type info
    else if (m_eType == VALUETYPE_EDIT)
    {
        // Limit text
        m_uLimitText = VALUE_SZMAX-1;
        uTemp = Reg_QueryInt(hkParamInfo,c_szRegParamLimitText,m_uLimitText);
        if ((uTemp > 0) && (uTemp < VALUE_SZMAX))
        {
            m_uLimitText = uTemp;
        }

        // Read-only
        m_fReadOnly = FALSE;
        uTemp = Reg_QueryInt(hkParamInfo,c_szRegParamReadOnly,0);
        if (uTemp != 0)
        {
            m_fReadOnly = TRUE;
        }

        // OEMText
        m_fOEMText = FALSE;
        uTemp = Reg_QueryInt(hkParamInfo,c_szRegParamOEMText,0);
        if (uTemp != 0)
        {
            m_fOEMText = TRUE;
        }

        // Uppercase
        m_fUppercase = FALSE;
        uTemp = Reg_QueryInt(hkParamInfo,c_szRegParamUppercase,0);
        if (uTemp != 0)
        {
            m_fUppercase = TRUE;
        }
    }

    // Enum type info
    else if (m_eType == VALUETYPE_ENUM)
    {
        hr = HrRegOpenKeyEx(hkParamInfo,c_szRegParamTypeEnum,KEY_READ,
                            &m_hkEnum);
        if (FAILED(hr))
        {
            m_hkEnum = NULL;
        }
    }

    // Current Value
    m_fModified = FALSE;
    if (!m_vValue.FLoadFromRegistry(m_hkRoot,m_pszKeyName,hkParamInfo))
    {
        // Use default value (current value not in registry)
        if (!m_vValue.FLoadFromRegistry(hkParamInfo,c_szRegParamDefault))
        {
            // If no default in registry, assume a decent value
            if (m_vValue.IsNumeric())
            {
                m_vValue.Copy(&m_vMin);
            }
            else
            {
                m_vValue.FromString(L"");
            }
        }

        // Keep not-present state of optional parameters.
        // Mark required parameters modified since we read the default.
        if (m_fOptional)
        {
            m_vValue.SetPresent(FALSE);
        }
        else
        {
            m_fModified = TRUE;
        }
    }

    // Save initial value for comparison in Param_Validate
    // The initial value is always valid - in case the user hand-mucks
    // it to something outside the specified range.
    m_vInitial.Copy(&m_vValue);

    m_fInit = TRUE;
    RegSafeCloseKey(hkParamInfo);
    return TRUE;

error:
    // Cleanup done by destructor.
    return FALSE;

}


VOID CParam::InitParamType(PTSTR pszType)
{
    typedef struct tagPTABLE
    {
        const WCHAR * pszToken;
        VALUETYPE        type;
        DWORD       dwMin;
        DWORD       dwMax;
    } PTABLE;
    static PTABLE ptable[] =
    {
        // 1st entry is default if pszType is invalid or unknown
        {c_szRegParamTypeEdit,  VALUETYPE_EDIT,  NULL,           NULL},
        {c_szRegParamTypeInt,   VALUETYPE_INT,   SHRT_MIN, SHRT_MAX},
        {c_szRegParamTypeLong,  VALUETYPE_LONG,  LONG_MIN,(DWORD)LONG_MAX},
        {c_szRegParamTypeWord,  VALUETYPE_WORD,  0,              USHRT_MAX},
        {c_szRegParamTypeDword, VALUETYPE_DWORD, 0,              ULONG_MAX},
        {c_szRegParamTypeEnum,  VALUETYPE_ENUM,  NULL,           NULL},
        {c_szRegParamTypeKeyonly, VALUETYPE_KONLY, NULL,           NULL}
    };

    UINT    i;
    PTABLE* pt;

    Assert(pszType != NULL);

    // Lookup token in param table
    for (i=0; i < celems(ptable); i++)
    {
        pt = &ptable[i];
        if (lstrcmpiW(pt->pszToken,pszType) == 0)
        {
            break;
        }
    }
    if (i >= celems(ptable))
    {
        pt = &ptable[0];
    }

    // Table default values
    m_eType = pt->type;
    m_vValue.Init(pt->type,0);
    m_vInitial.Init(pt->type,0);

    if (m_vValue.IsNumeric())
    {
        m_vMin.Init(pt->type,pt->dwMin);
        m_vMax.Init(pt->type,pt->dwMax);
        m_vStep.Init(pt->type,1);
    }
    else
    {
        m_vMin.Init(pt->type,NULL);
        m_vMax.Init(pt->type,NULL);
        m_vStep.Init(pt->type,0);
    }
}

// Notes: Don't close m_hkRoot since other's may have copies of it.
//        ~CAdvanced will close it.
//
CParam::~CParam()
{
    // Close the enum subkey
    RegSafeCloseKey(m_hkEnum);

    // free strings
    delete m_pszKeyName;
    delete m_pszDesc;
    delete m_pszHelpFile;

    // free values
    m_vValue.Destroy();
    m_vInitial.Destroy();
    m_vMin.Destroy();
    m_vMax.Destroy();
    m_vStep.Destroy();
}

// Applies from In-Memory storage to registry
BOOL CParam::Apply() {
    AssertSz(m_fInit,"CParam not FInit()'ed.");
    if (!FIsModified())
    {
        return TRUE;  // not modified, don't save.
    }
    Assert(0 == m_vValue.Compare(&m_vValue));
    m_fModified = FALSE;
    m_vInitial.Copy(&m_vValue);
    return m_vValue.FSaveToRegistry(m_hkRoot,m_pszKeyName);

}


UINT CParam::Validate()
{
    AssertSz(m_fInit, "CParam not FInit()'ed.");
    // Equal to the initial value is ok
    if (m_vValue.Compare(&m_vInitial) == 0)
    {
        return VALUE_OK;
    }

    // Unpresent-optional value is ok
    if (FIsOptional() && !m_vValue.IsPresent())
    {
        return VALUE_OK;
    }

    // Invalid characters
    if (m_vValue.IsInvalidChars())
    {
        return VALUE_BAD_CHARS;
    }

    // Empty required field
    if (m_vValue.IsEmptyString() && m_vValue.IsPresent() && (m_vValue.GetType() != VALUETYPE_KONLY))
    {
        return VALUE_EMPTY;
    }

    // Numeric range
    if (m_vValue.IsNumeric())
    {
        // If value is < min, out of range
        if (m_vValue.Compare(&m_vMin) < 0)
        {
            return VALUE_OUTOFRANGE;
        }

        // If value is > max, out of range
        if (m_vValue.Compare(&m_vMax) > 0)
        {
            return VALUE_OUTOFRANGE;
        }

        // Step-range
        Assert(m_vStep.GetNumericValueAsDword() != 0);

        if (((m_vValue.GetNumericValueAsDword() -
             m_vMin.GetNumericValueAsDword())
             % m_vStep.GetNumericValueAsDword()) != 0)
        {
            return VALUE_OUTOFRANGE;
        }
    }

    return VALUE_OK;
}


VOID CParam::GetDescription(WCHAR * sz, UINT cch)
{
    AssertSz(m_fInit, "CParam not FInit()'ed.");
    lstrcpynW(sz, m_pszDesc, cch);
}

VOID CParam::GetHelpFile(WCHAR * sz, UINT cch)
{
    AssertSz(m_fInit, "CParam not FInit()'ed.");
    lstrcpynW(sz, m_pszHelpFile, cch);
}