/*
 * Parser
 */

#ifndef DUI_PARSER_PARSEROBJ_H_INCLUDED
#define DUI_PARSER_PARSEROBJ_H_INCLUDED

#pragma once

namespace DirectUI
{

#define MAXIDENT            31

////////////////////////////////////////////////////////
// Parser table definitions

struct EnumTable
{
    LPWSTR pszEnum;
    int nEnum;
};

typedef HRESULT (*PLAYTCREATE)(int, int*, Value**);
struct LayoutTable
{
    LPWSTR pszLaytType;
    PLAYTCREATE pfnLaytCreate;
};

typedef struct
{
    LPWSTR pszElType;
    IClassInfo* pci;
} ElementTable;

struct SysColorTable
{
    LPWSTR pszSysColor;
    int nSysColor;
};

////////////////////////////////////////////////////////
// Parser tree data structures
// Parse tree nodes are any data structure dynamically allocated to store tree information

// Parse tree node types
#define NT_ValueNode            0
#define NT_PropValPairNode      1
#define NT_ElementNode          2
#define NT_AttribNode           3
#define NT_RuleNode             4
#define NT_SheetNode            5
                            
// Tree node base class
struct Node
{
    BYTE nType;
};

// Value node
#define VNT_Normal              0
#define VNT_LayoutCreate        1
#define VNT_SheetRef            2
#define VNT_EnumFixup           3  // Map name to int Value once PropertyInfo is known

struct LayoutCreate
{
    union
    {
        PLAYTCREATE pfnLaytCreate;
        LPWSTR pszLayout;  // Fixup happens immediately during Value creation
    };
    int dNumParams;
    int* pParams;
};

struct EnumsList
{
    int dNumParams;
    LPWSTR* pEnums;  // Enums to be OR'd
};

struct ValueNode : Node
{
    BYTE nValueType;
    union
    {
        Value* pv;        // VNT_Normal
        LayoutCreate lc;  // VNT_LayoutCreate, created during Element creates
        LPWSTR psres;     // VNT_SheetRef
        EnumsList el;     // VNT_EnumFixup
    };
};

// Property/Value Pair
#define PVPNT_Normal            0
#define PVPNT_Fixup             1  // Map name to ppi once Element type is known

struct PropValPairNode : Node
{
    BYTE nPropValPairType;
    union
    {
        PropertyInfo* ppi;  // PVPNT_Normal
        LPWSTR pszProperty; // PVPNT_Fixup
    };
    ValueNode* pvn;

    PropValPairNode* pNext;
};

// Element node
struct ElementNode : Node
{
    IClassInfo* pci;
    PropValPairNode* pPVNodes;
    LPWSTR pszResID;
    Value* pvContent;

    ElementNode* pChild;
    ElementNode* pNext;
};

// Sheet attribute node
#define PALOGOP_Equal           0
#define PALOGOP_NotEqual        1

struct AttribNode : PropValPairNode
{
    UINT nLogOp;
};

// Sheet rule node
struct RuleNode : Node
{
    IClassInfo* pci;
    AttribNode* pCondNodes;
    PropValPairNode* pDeclNodes;

    RuleNode* pNext;
};

// Sheet node
struct SheetNode : Node
{
    Value* pvSheet;  // Create once all Rules are known
    RuleNode* pRules;
    LPWSTR pszResID;
};

// Intermediate parser data structures
struct ParamsList
{
    int dNumParams;
    int* pParams;
};

struct StartTag
{
    WCHAR szTag[MAXIDENT];
    WCHAR szResID[MAXIDENT];
    PropValPairNode* pPVNodes;
};

// Parser

typedef void (CALLBACK *PPARSEERRORCB)(LPCWSTR pszError, LPCWSTR pszToken, int dLine);

class Parser
{
public:
    static HRESULT Create(const CHAR* pBuffer, int cCharCount, HANDLE* pHList, PPARSEERRORCB pfnErrorCB, OUT Parser** ppParser);
    static HRESULT Create(UINT uRCID, HANDLE* pHList, PPARSEERRORCB pfnErrorCB, OUT Parser** ppParser);
    static HRESULT Create(LPCWSTR pFile, HANDLE* pHList, PPARSEERRORCB pfnErrorCB, OUT Parser** ppParser);

    static HRESULT Create(const CHAR* pBuffer, int cCharCount, HINSTANCE hInst, PPARSEERRORCB pfnErrorCB, OUT Parser** ppParser);
    static HRESULT Create(UINT uRCID, HINSTANCE hInst, PPARSEERRORCB pfnErrorCB, OUT Parser** ppParser);
    static HRESULT Create(LPCWSTR pFile, HINSTANCE hInst, PPARSEERRORCB pfnErrorCB, OUT Parser** ppParser);

    void Destroy() { HDelete<Parser>(this); }

    HRESULT CreateElement(LPCWSTR pszResID, Element* peSubstitute, OUT Element** ppElement);
    virtual Value* GetSheet(LPCWSTR pszResID);
    LPCWSTR ResIDFromSheet(Value* pvSheet);

    void GetPath(LPCWSTR pIn, LPWSTR pOut, size_t cbOut);

    // Parser/scanner only use
    int _Input(CHAR* pBuffer, int cMaxChars);
    void* _TrackNodeAlloc(SIZE_T s);                // Parse-tree node memory
    void _UnTrackNodeAlloc(Node* pn);               // Parse-tree node memory
    void* _TrackAlloc(SIZE_T s);                    // Node-specific state
    void* _TrackTempAlloc(SIZE_T s);                // Parse-time temporary memory
    void _TrackTempAlloc(void* pm);                 // Parse-time temporary memory
    void* _TrackTempReAlloc(void* pm, SIZE_T s);    // Parse-time temporary memory
    void _UnTrackTempAlloc(void* pm);               // Parse-time temporary memory
    void _ParseError(LPCWSTR pszError, LPCWSTR pszToken, int dLine);

    ValueNode* _CreateValueNode(BYTE nValueType, void* pData);
    PropValPairNode* _CreatePropValPairNode(LPCWSTR pszProperty, ValueNode* pvn, UINT* pnLogOp = NULL);
    RuleNode* _CreateRuleNode(LPCWSTR pszClass, AttribNode* pCondNodes, PropValPairNode* pDeclNodes);
    ElementNode* _CreateElementNode(StartTag* pst, Value* pvContent);
    SheetNode* _CreateSheetNode(LPCWSTR pszResID, RuleNode* pRuleNodes);
    static int _QuerySysMetric(int idx);
    static LPCWSTR _QuerySysMetricStr(int idx, LPWSTR psz, UINT c);

    bool WasParseError() { return _fParseError; }
    HANDLE GetHandle(int iHandle) { return _pHList[iHandle]; }
    HINSTANCE GetHInstance() { return static_cast<HINSTANCE>(GetHandle(0)); }  // Always assume 0th item is the default HINSTANCE used

    static HRESULT ReplaceSheets(Element* pe, Parser* pFrom, Parser* pTo);

    DynamicArray<ElementNode*>* _pdaElementList;     // Root Element list
    DynamicArray<SheetNode*>* _pdaSheetList;         // Sheet list

    // Global parser context
    static Parser* g_pParserCtx;
    static bool g_fParseAbort;
    static HDC g_hDC;
    static int g_nDPI;
    static HRESULT g_hrParse;                        // Abnormal errors during parse

    Parser() { }
    HRESULT Initialize(const CHAR* pBuffer, int cCharCount, HANDLE* pHList, PPARSEERRORCB pfnErrorCB);
    HRESULT Initialize(UINT uRCID, HANDLE* pHList, PPARSEERRORCB pfnErrorCB);
    HRESULT Initialize(LPCWSTR pFile, HANDLE* pHList, PPARSEERRORCB pfnErrorCB);

    // Single HINSTANCE, setup internal default list
    // 0th entry is always default HINSTANCE when no handle is specified
    HRESULT Initialize(const CHAR* pBuffer, int cCharCount, HINSTANCE hInst, PPARSEERRORCB pfnErrorCB) { _hDefault = hInst; return Initialize(pBuffer, cCharCount, &_hDefault, pfnErrorCB); }
    HRESULT Initialize(UINT uRCID, HINSTANCE hInst, PPARSEERRORCB pfnErrorCB) { _hDefault = hInst; return Initialize(uRCID, &_hDefault, pfnErrorCB); }
    HRESULT Initialize(LPCWSTR pFile, HINSTANCE hInst, PPARSEERRORCB pfnErrorCB) { _hDefault = hInst; return Initialize(pFile, &_hDefault, pfnErrorCB); }

    virtual ~Parser();

    virtual bool ConvertEnum(LPCWSTR pszEnum, int* pEnum, PropertyInfo* ppi);
    virtual PLAYTCREATE ConvertLayout(LPCWSTR pszLayout);
    virtual IClassInfo* ConvertElement(LPCWSTR pszElement);

private:
    HRESULT _ParseBuffer(const CHAR* pBuffer, int cCharCount);
    void _DestroyTables();

    // Error handling
    bool _fParseError;
    PPARSEERRORCB _pfnErrorCB;

    // Input callback buffer and tracking
    const CHAR* _pInputBuf;
    int _dInputChars;
    int _dInputPtr;

    // Parse tree allocations and temporary parse-time only allocations
    DynamicArray<Node*>* _pdaNodeMemTrack;  // Parser nodes
    DynamicArray<void*>* _pdaMemTrack;      // Parser node extra memory
    DynamicArray<void*>* _pdaTempMemTrack;  // Temp parse-time only memory

    bool _FixupPropValPairNode(PropValPairNode* ppvpn, IClassInfo* pci, bool bRestrictVal);

    WCHAR _szDrive[MAX_PATH];   // Drive letter to the file being parsed
    WCHAR _szPath[MAX_PATH];    // Path to the file being parsed

    HANDLE* _pHList;            // Pointer to handle list used for exposing runtime handles during parse
    HANDLE _hDefault;           // Default handle list (1 item - default HINSTANCE) if no list is provided

    HRESULT _InstantiateElementNode(ElementNode* pn, Element* peSubstitute, Element* peParent, OUT Element** ppElement);
};

} // namespace DirectUI

#endif // DUI_PARSER_PARSEROBJ_H_INCLUDED
