/*
Customizeable Text Parser

--Overview-- 

CParserValue        Data! (an int, real, string etc), basically an argument
CParserTaskType     Syntax description for a line (ex. "variable=value")
CParserLine         Parse _one_ line
CParser             Include all syntax (CParserTaskTypes)

The whole CParser* class group is used to read in config files and
give instruction on how that should be made. The CParserTaskType
declares what in a line is arguments, of course different CParserTaskTypes
will exist, and it's up to the system to figure out which one acquired.


--More Info--

    http://forums.wildfiregames.com/0ad/index.php?showtopic=134

*/

#ifndef INCLUDED_PARSER
#define INCLUDED_PARSER

#include "Pyrogenesis.h"

#if MSC_VERSION
#pragma warning(disable:4786)
#endif

//--------------------------------------------------------
//  Includes / Compiler directives
//--------------------------------------------------------

#include <vector>
#include <string>
#include <map>
#include <deque>
#include <cmath>

#include "CStr.h"

//-------------------------------------------------
// Types
//-------------------------------------------------

enum _ParserValueType
{
    typeIdent,
    typeValue,
    typeRest,
    typeAddArg,
    typeNull
};

//-------------------------------------------------
// Declarations
//-------------------------------------------------

class CParserValue;
class CParserTaskType;
class CParserLine;
class CParser;


// CParserValue
// ---------------------------------------------------------------------
// A parser value represents an argument
// color=r, g, b
// r, g and b will be CParserValues, or the arguments as they are called.
// This class can store only a string, but can try parsing it to different
// types
class CParserValue
{
public:
    CParserValue();
    ~CParserValue();

    // return is error status
    bool GetString(std::string &ret);
    bool GetString( CStr& ret );
    bool GetBool(bool &ret);
    bool GetChar(char &ret);    // As number! otherwise use GetString make sure size=1
    bool GetShort(short &ret);
    bool GetInt(int &ret);
    bool GetLong(long &ret);
    bool GetUnsignedShort(unsigned short &ret);
    bool GetUnsignedInt(unsigned int &ret);
    bool GetUnsignedLong(unsigned long &ret);
    bool GetFloat(float &ret);
    bool GetDouble(double &ret);

    // Memory regardless if it's an int, real, string or whatever
    std::string m_String;
};

// CParserTaskTypeNode
// ---------------------------------------------------------------------| Class
// A task type is basically a tree, this is because dynamic arguments
//  requires alternative routes, so basically it's a binary tree with an
//  obligatory next node (if it's not the end of the tree) and an alternative
//  dynamic arguments node
//
// If we are at the beginning of this string, this will be the layout of the node
// "<$value_>:_"
//
// m_Element    ":"
// m_AltNode    => "$value"
// m_NextNode   => "_"
//
class CParserTaskTypeNode
{
public:
    CParserTaskTypeNode();
    ~CParserTaskTypeNode();

    // Free node pointers that are below this
    void DeleteChildren();

    // Either the node is a letter or a type, if m_Letter is '\0'
    //  then check m_Type what it is
    char                            m_Letter;
    _ParserValueType                m_Type;
    std::string                     m_String;   // Used for diverse storage
                                                //  mainly for the typeAddArg
    
    // Parent node
    CParserTaskTypeNode             *m_ParentNode;

    // Next node 
    CParserTaskTypeNode             *m_NextNode;

    // true means AltNode can be looped <...>
    // false means AltNode is just an optional part [...]
    bool                            m_AltNodeRepeatable;

    // Whenever a dynamic argument is used, its first node is stored in this
    //  as an alternative node. The parser first checks if there is an
    //  alternative route, and if it applies to the next node. If not, proceed
    //  as usual with m_String and the next node
    CParserTaskTypeNode             *m_AltNode;

    // There are different kinds of alternative routes
    //int                               m_AltRouteType;
};

// CParserTaskType
// ---------------------------------------------------------------------| Class
// A task type is basically different kinds of lines for the parser
// variable=value is one type...
class CParserTaskType
{
public:
    CParserTaskType();
    ~CParserTaskType();

    // Delete the whole tree
    void DeleteTree();

    CParserTaskTypeNode             *m_BaseNode;

    // Something to identify it with
    std::string                     m_Name;
};

// CParserLine
// ---------------------------------------------------------------------| Class
// Representing one line, i.e. one task, in a config file
class CParserLine
{
public:
    CParserLine();
    ~CParserLine();

    std::deque<CParserValue>            m_Arguments;
    bool                            m_ParseOK;      // same as ParseString will return
    std::string                     m_TaskTypeName; // Name of the task type found

protected:
    bool ClearArguments();

public:
    // Interface
    bool ParseString(const CParser& parser, const std::string &line);

    // Methods for getting arguments
    //  it returns success
    bool GetArgString           (size_t arg, std::string &ret);
    bool GetArgBool             (size_t arg, bool &ret);
    bool GetArgChar             (size_t arg, char &ret);
    bool GetArgShort            (size_t arg, short &ret);
    bool GetArgInt              (size_t arg, int &ret);
    bool GetArgLong             (size_t arg, long &ret);
    bool GetArgUnsignedShort    (size_t arg, unsigned short &ret);
    bool GetArgUnsignedInt      (size_t arg, unsigned int &ret);
    bool GetArgUnsignedLong     (size_t arg, unsigned long &ret);
    bool GetArgFloat            (size_t arg, float &ret);
    bool GetArgDouble           (size_t arg, double &ret);

    // Get Argument count
    size_t GetArgCount() const { return m_Arguments.size(); }
};

// CParser
// ---------------------------------------------------------------------| Class
// Includes parsing instruction, i.e. task-types
class CParser
{
public:
    CParser();
    ~CParser();

    std::vector<CParserTaskType>    m_TaskTypes;

    // Interface
    bool InputTaskType(const std::string& strName, const std::string& strSyntax);
};



// CParserCache
// ---------------------------------------------------------------------| Class
// Provides access to CParser objects, caching them to avoid
// reconstructing the object every time a string needs to be parsed.
class CParserCache
{
public:
    // Returns a simple parser based on a single nameless task-type
    static CParser& Get(const char* str);

private:
    // Self-destructing std::map
    template <typename T, typename P> class SDMap : public std::map<T,P>
    {
    public:
        typedef typename std::map<T,P>::iterator iterator;
        ~SDMap()
        {
            for (iterator it = this->begin(); it != this->end(); ++it) delete it->second;
        }
    };

    typedef SDMap<std::string, CParser*> CacheType;

    static CacheType m_Cached;
};

#endif