/**
 @file Any.h
  
 @author Morgan McGuire, Shawn Yarbrough, and Corey Taylor
 @maintainer Morgan McGuire
  
 @created 2006-06-11
 @edited  2010-03-16

 Copyright 2000-2010, Morgan McGuire.
 All rights reserved.
 */

#ifndef G3D_Any_h
#define G3D_Any_h

#include "G3D/platform.h"
#include "G3D/Table.h"
#include "G3D/Array.h"
#include "G3D/Set.h"
#include "G3D/AtomicInt32.h"
#include "G3D/stringutils.h"
#include <string>

// needed for Token
#include "G3D/TextInput.h"

#ifdef verify
#undef verify
#endif

namespace G3D {

class TextOutput;

/** 
\brief Easy loading and saving of human-readable configuration files.

Any encodes typed, structured data and can serialize it to a human
readable format that is very similar to the Python language's data
syntax.  It is well-suited for quickly creating human-readable file
formats, especially since deserialization and serialization preserve
comments and an Any can tell you what file and line it came from.  The
syntax allows most C++ editors to properly highlight Any files, and
makes it easy to design little ad-hoc C-like languages in
configuration files.

The class is designed so that copying Anys generally is fast, even if
it is a large array or table.  This is because data is shared between
copies until it is mutated, at which point an actual copy occurs.

\section Example
Sample File:
<pre>
{
   shape = "round",

   // in meters
   radius = 3.7,

   position = Vector3(1.0, -1.0, 0.0),
   video = { format = "RGB8", size = (320, 200)},

   material = #include("rocks.mat")
}
</pre>

Sample code using Any:
<pre>
Any x;
x.load("ball.txt");
if (x["shape"].string() == "round") {
    x["density"] = 3;
}
x.save("ball.txt");
</pre>

The custom serialization format was chosen to be terse, easy for
humans to read, and easy for machines to parse. It was specifically
chosen over formats like XML, YAML, JSON, S-expressions, and Protocol
Buffers, although there is no reason you could not write readers and
writers for G3D::Any that support those.

G3D::Any assumes that structures do not contain cycles; it is an 
error to create a structure like:

<pre>
Any x(Any::ARRAY);
x.array().append(x);    // don't do this!
</pre>

although no exception will be thrown at runtime during that append.


\section Parsing

The primary use of Any is to create your own text file formats.
The Vector3 constructor is a good example of how to use the Any::verify 
methods to provide good error checking while parsing such formats:

<pre>
Vector3::Vector3(const Any& any) {
    any.verifyName("Vector3");
    any.verifyType(Any::TABLE, Any::ARRAY);
    any.verifySize(3);

    if (any.type() == Any::ARRAY) {
        x = any[0];
        y = any[1];
        z = any[2];
    } else {
        // Table
        x = any["x"];
        y = any["y"];
        z = any["z"];
    }
}
</pre>

It is often convenient to iterate through the table portion:

<pre>
    for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
        const std::string& k = toLower(it->key);
        if (key == "hello") {
           ...
        } else if (key == "goodbye") {
           ...
        } else {
           any.verify(false, "Unsupported key: " + it->key);
        }
    }
</pre>

\section BNF
Serialized format BNF:

<pre>
identifier  ::= (letter | "_") (letter | digit | "_")*
identifier-op ::= "::" | "->" | "."

identifier-exp ::= [identifier-op] identifier (identifier-op identifier)*

comment     ::= C++ single or multi-line comments
separator   ::= "," | ";"

number      ::= <legal C printf number format>
string      ::= <legal C double-quoted string; backslashes must be escaped>
boolean     ::= "True" | "False"
none        ::= "None"
array       ::= ("(" | "[") [ value (separator value)* [separator] ] (")" | "]")
pair        ::= (identifier | string) "=" value
table       ::= "{" [ pair (separator pair)* [separator] ] "}"
named-array ::= identifier-exp array
named-table ::= identifier-exp table
include     ::= "#" "include" "(" string ")"

value       ::= [comment] (none | number | boolean | string | array | table | named-array | named-table | include)
</pre>

Except for single-line comments, whitespace is not significant.  
All parsing is case-insensitive.

The include expression pastes the contents of the named file in as if
they appeared in the original source.  Note that an include expression
can only appear in the locations where a value is expected.  This means
that it cannot yield more than one element of an array and cannot serve
as the pair in a table.

The deserializer allows the substitution of [] for () when writing
tuples and ";" for ",".  These are convenient when mimicing a
programming language, e.g., <code>"[ printf("hello world."); clearScreen();]"</code> 
parses as an array containing two named arrays within it. The 
deserializer also allows a trailing comma inside any array or table,
which also convenient when commenting out the last element.

The serializer indents four spaces for each level of nesting. 
Tables are written with the keys in alphabetic order.
*/
class Any {
public:

    enum Type {NONE, BOOLEAN, NUMBER, STRING, ARRAY, TABLE};

    static std::string toString(Type t);

    /** Where an Any came from in a file.  Useful for throwing parsing errors */
    class Source {
    public:
        std::string                 filename;
        int                         line;
        int                         character;

        Source() : line(0), character(0) {}

        void set(const TextInput& ti, const Token& t) {
            filename  = ti.filename();
            line      = t.line();
            character = t.character();
        }
    };

    typedef Array<Any> AnyArray;
    typedef Table<std::string, Any> AnyTable;

private:

    /** Called from deserialize() */
    static void deserializeComment(TextInput& ti, Token& token, std::string& comment);

    /** NONE, BOOLEAN, and NUMBER are stored directly in the Any */
    union SimpleValue {
        bool                        b;
        double                      n;

        inline SimpleValue() : n(0.0) {}
        inline SimpleValue(bool x) : b(x) {}
        inline SimpleValue(double x) : n(x) {}
    };
 
    class Data {
    public:
        /** ARRAY, TABLE, or STRING value only.  NULL otherwise. */
        union Value {
            std::string*             s;
            Array<Any>*              a;
            AnyTable*                t;
            inline Value() : s(NULL) {}
        };

        // Needed so that the destructor knows what is in Value
        // and can call its destructor. 
        Type                         type;
        
        /** Always points to memory that is allocated with the Data, so
           the destructor does not delete this. */
        Value                        value;
        
        std::string                  comment;

        std::string                  name;

        /** For STRING, ARRAY and TABLE types, m_value is shared between
            multiple instances.  Mutation is allowed only if the reference
            count is exactly 1, otherwise the mutating instance must copy
            the value.  This is not used for other types.
        */
        AtomicInt32                  referenceCount;

        Source                       source;

    private:

        /** Called by create() */
        inline Data(Type t) : type(t), referenceCount(1) {}

        /** Called by destroy */
        ~Data();

    public:

        /** Clones the argument */
        static Data* create(const Data* d);
        static Data* create(Type t);

        /** Free d, invoking its destructor and freeing the memory for
            the value. */
        static void destroy(Data* d);

    };

    /** If not empty, this Any was created from operator[] on a table
        and perhaps was not intended to exist.  The name is needed to
        format the error message if it is read from before it is
        written to.

        The source of a placeholder object is that of the parent
        object until it is written.
    */
    std::string         m_placeholderName;

    Type                m_type;
    SimpleValue         m_simpleValue;
    mutable Data*       m_data;

    /** Called before every read operation to ensure that this object
        is not a placeholder.  */
    void beforeRead() const;

    /** Called before every write operation to wipe the placeholder
        status. */
    void beforeWrite();

    /** Decrements the reference count (if there is one).  If the
    reference count is zero after decrement, calls delete on @a m_data
    and sets it to NULL.
    */
    void dropReference();

    /** Allocate the Data object if it does not exist */
    void ensureData();

    /** If m_data is not NULL, ensure that it has a unique reference
        and contains a valid m_data.  This has a race condition if two
        threads are both trying to modify the same Any
        simultaneously.*/    
    void ensureMutable();

    /** Read an unnamed a TABLE or ARRAY.  Token should be the open
        paren token; it is the next token after the close on
        return. Called from deserialize().*/
    void deserializeBody(TextInput& ti, Token& token);

    void deserialize(TextInput& ti, Token& token);

    /** Read the name of a named Array or Table. */
    static void deserializeName(TextInput& ti, Token& token, std::string& name);
    
    /** Read until a comma is consumed or a close paren is hit, and
     return that token.  Considers the passed in token to be the first
     value read. */
    static void readUntilCommaOrClose(TextInput& ti, Token& token);

    /** Construct an Any that is a proxy for a table fetch from \a data.
     This proxy can be copied exactly once on return from operator[].*/
    Any(const std::string& key, Data* data);

    inline bool isPlaceholder() const {
        return ! m_placeholderName.empty();
    }

public:

    /** Base class for all Any exceptions.*/
    class Exception {
    public:
        virtual ~Exception() {}
    };

    /** Thrown by operator[] when a key is not present in a const table. */
    class KeyNotFound : public ParseError {
    public:
        std::string key;
    };

    /** Thrown by operator[] when an array index is not present. */
    class IndexOutOfBounds : public Exception {
    public:
        int     index;
        int     size;
        inline IndexOutOfBounds() : index(0), size(0) {}
        inline IndexOutOfBounds(int i, int s) : index(i), size(s) {}
    };

    /** NONE constructor */
    Any();

    /** Deserialize */
    explicit Any(TextInput& t);

    Any(const Any& x);

    /** NUMBER constructor */
    Any(double x);

#ifdef G3D_32BIT
    /** NUMBER constructor */
    Any(int64 x);
#endif    // G3D_32BIT

#if 0
    /** NUMBER constructor */
    Any(int32 x);
#endif    // 0

    /** NUMBER constructor */
    Any(long x);

    /** NUMBER constructor */
    Any(int x);

    /** NUMBER constructor */
    Any(short x);

    /** BOOLEAN constructor */
    Any(bool x);

    /** STRING constructor */
    Any(const std::string& x);

    /** STRING constructor */
    Any(const char* x);

    /** \a t must be ARRAY or TABLE */
    Any(Type t, const std::string& name = "");
    
    ~Any();

    /** Removes the comment and name */
    Any& operator=(const Any& x);

    /** Removes the comment and name */
    Any& operator=(double x);

    /** Removes the comment and name */
    Any& operator=(int x);

    /** Removes the comment and name */
    Any& operator=(bool x);

    /** Removes the comment and name */
    Any& operator=(const std::string& x);

    /** Removes the comment and name */
    Any& operator=(const char* x);

    /** \a t must be ARRAY, TABLE, or NONE. Removes the comment and name */
    Any& operator=(Type t);

    Type type() const;

    /** Same as deserialize or load, but operates on a string instead
        of a stream or file.

      \sa deserialize, load
      */
    void parse(const std::string& src);

    std::string unparse() const;
    
    /** Comments appear before values when they are in serialized form.*/
    const std::string& comment() const;
    void setComment(const std::string& c);

    /** True if this is the NONE value */
    bool isNone() const;

    /** Throws a ParseError exception if this is not a number */
    double number() const;
    const std::string& string() const;
    bool boolean() const;

    /** If a valid string, takes the string value and creates a fully qualified filename.
        If not found, the returned string is empty.

        The file is searched for the following ways:
        
        - In the directory from which the Any was loaded.
        - By calling System::findDataFile as you would with other data files.
     */
    std::string resolveStringAsFilename() const;

    /** If this is named ARRAY or TABLE, returns the name. */
    const std::string& name() const;

    /** If this is named ARRAY or TABLE, returns true if the name begins with \a s.  The comparision is case insensitive. */
    bool nameBeginsWith(const std::string& s) const;

    /** If this is named ARRAY or TABLE, returns true if the name begins with \a s.  The comparision is case insensitive. */
    bool nameBeginsWith(const char* s) const;

    /** If this is named ARRAY or TABLE, returns true if the name is \a s.  The comparision is case insensitive. */
    bool nameEquals(const std::string& s) const;

    /** If this is named ARRAY or TABLE, returns true if the name is\a s.  The comparision is case insensitive. */
    bool nameEquals(const char* s) const;

    /** \brief Set the name used when serializing an ARRAY or TABLE.
    
       Only legal for ARRAY or TABLE.  The \a name must begin with a letter
       and contain only letters, numbers, underscores and scope operators.

        <pre>
        a2z
        hello
        Foo::bar
        color.red
        this->that
        __x
        </pre>
        
                
        The scope operators "::", "->", and
        ".", may have spaces around them.  The name may not
        contain parentheses.
    */
    void setName(const std::string& name);

    /** Number of elements if this is an ARRAY or TABLE */
    int size() const;
    int length() const;

    /** For an array, returns the ith element */
    const Any& operator[](int i) const;
    Any& operator[](int i);

    const Any& last() const {
        return (*this)[size() - 1];
    }

    Any& last() {
        return (*this)[size() - 1];
    }

    /** Directly exposes the underlying data structure for an ARRAY. */
    const Array<Any>& array() const;
    void append(const Any& v0);
    void append(const Any& v0, const Any& v1);
    void append(const Any& v0, const Any& v1, const Any& v2);
    void append(const Any& v0, const Any& v1, const Any& v2, const Any& v3);

    /** Directly exposes the underlying data structure for table.*/
    const Table<std::string, Any>& table() const;

    /** For a table, returns the element for \a key. Throws KeyNotFound
        exception if the element does not exist.
       */ 
    const Any& operator[](const std::string& key) const;

    // Needed to prevent the operator[](int) overload from catching
    // string literals
    const Any& operator[](const char* key) const {
        return operator[](std::string(key));
    }

    /** 
        Fetch an element from a table.  This can be used as:

        <pre>
        a["key"] = value;  (create the key if it did not exist)
        </pre>
        
        or

        <pre>
        value = a["key"];  (throw an error if the key did not exist)
        </pre>

        <b>Note:</b>
        In order to cause elements to be correctly created in the
        first case while still providing "key not found" errors in the
        second case, the Any returned is a special object that delays
        the actual fetch until the following assignment or method
        call.  This means that in the event of an error, the exception
        may be thrown from a line other than the actual fetch.  Use
        the Any::get() or the const Any::operator[]() methods to avoid
        this behavior and ensure error-checking at fetch time.
     */
    Any& operator[](const std::string& key);

    /** \copydoc Any::operator[](const std::string&) */
    Any& operator[](const char* key) {
        return operator[](std::string(key));
    }
    
    /** For a table, returns the element for key \a x and \a
        defaultVal if it does not exist. */
    const Any& get(const std::string& key, const Any& defaultVal) const;

    /** Returns true if this key is in the TABLE.  Illegal to call on an object that is not a TABLE. */
    bool containsKey(const std::string& key) const;
    
    /** For a table, assigns the element for key k. */
    void set(const std::string& key, const Any& val);

    /** for an ARRAY, resizes and returns the last element */
    Any& next();

    /** The parent directory of the location from which this Any was loaded.  This is useful for 
       interpreting filenames relative to the Any's source location,
       which may not match the current directory if the Any was from an included file. */
    std::string sourceDirectory() const;

    /** True if the Anys are exactly equal, ignoring comments.  Applies deeply on arrays and tables. */
    bool operator==(const Any& x) const;
    bool operator!=(const Any& x) const;

    operator int() const;
    operator float() const;
    operator double() const;
    operator bool() const;
    operator std::string() const;

    /** Resize to \a n elements, where new elements are NIL 
       It is an error to call this method if this is not an Any::ARRAY */
    void resize(int n);

    /** 
       Clears all entries.
       This must be a TABLE or ARRAY */
    void clear();

    /** Parse from a file.
     \sa deserialize, parse */
    void load(const std::string& filename);

    /** Uses the serialize method. */
    void save(const std::string& filename) const;

    void serialize(TextOutput& to) const;
    /** Parse from a stream.
     \sa load, parse */
    void deserialize(TextInput& ti);

    const Source& source() const;

    /** Throws a ParseError if \a value is false.  Useful for quickly
        creating parse rules in classes that deserialize from Any.
    */
    void verify(bool value, const std::string& message = "") const;


    /** Verifies that the name <i>begins with</i> identifier \a n (case insensitive). 
        It may contain identifier operators after this */
    void verifyName(const std::string& n) const;

    /** Verifies that the name <i>begins with</i> identifier \a n or \a m (case insensitive). 
        It may contain identifier operators after this */
    void verifyName(const std::string& n, const std::string& m) const;

    /** Verifies that the type is \a t. */
    void verifyType(Type t) const;

    /** Throws an exception if the type is not \a t0 or \a t1. */
    void verifyType(Type t0, Type t1) const;

    /** Verifies that the size is between \a low and \a high, inclusive */
    void verifySize(int low, int high) const;

    /** Verifies that the size is exactly \a s */
    void verifySize(int s) const;

private:

    void deserializeTable(TextInput& ti);
    void deserializeArray(TextInput& ti,const std::string& term);

};    // class Any


/**
   Convenient iteration over the keys of a Any::TABLE, usually
   for implementing construction of an object from an Any.

   Getting an element using either iteration or explicit requests
   consumes that element from the iterator (but not from the Any!)
   It is an error to consume the same element more than once from
   the same iterator.

   <pre>
    AnyKeyIterator r(a);
    r.getIfPresent("enabled",            enabled);
    r.getIfPresent("showSamples",        showSamples);
    r.getIfPresent("showTiles",          showTiles);

    r.verifyDone();
    </pre>

    \beta
*/
class AnyTableReader {
private:
   Any              m_any;
   Set<std::string> m_alreadyRead;
public:
    
    /** Verifies that \a is a TABLE with the given \a name. */
    AnyTableReader(const std::string& name, const Any& a) : m_any(a) {
        try {
            m_any.verifyType(Any::TABLE);
            m_any.verifyName(name);
        } catch (const ParseError& e) {
            // If an exception is thrown, the destructors will not be 
            // invoked automatically.
            m_any.~Any();
            m_alreadyRead.~Set();
            throw e;
        }
    }

    /** Verifies that \a is a TABLE. */
    AnyTableReader(const Any& a) : m_any(a) {
        try {
            m_any.verifyType(Any::TABLE);
        } catch (const ParseError& e) {
            // If an exception is thrown, the destructors will not be 
            // invoked automatically.
            m_any.~Any();
            m_alreadyRead.~Set();
            throw e;
        }
    }

    bool hasMore() const {
        return m_any.size() > m_alreadyRead.size();
    }

    /** Verifies that all keys have been read. */
    void verifyDone() const {
        if (hasMore()) {
            // Generate all keys
            // Remove the ones we've read
            // Assert the rest
          //  any.verify("");
        }
    }

#if 0
    /** Returns the current key */
    const std::string& key() const;

    /** Returns the current value */
    const Any& value() const;

    AnyKeyIterator& operator++();
#endif   
    
    /** If key \s appears in the any, reads its value into \a v and 
        removes that key from the ones available to iterate over.

        If key \s does not appear in the any, throws a G3D::ParseError.

        Assumes that if key \s appears in the any it has not already been extracted
        by this iterator.  If it has been read before, an assertion will fail in debug mode.

      */
    template<class ValueType>
    void get(const std::string& s, ValueType& v) {
        v = m_any[s];
        m_alreadyRead.insert(toLower(s));
    }

    /** Get the value associated with a key only if the key is actually present.
    
        If key \s appears in the any, reads its value into \a v and 
        removes that key from the ones available to iterate over.

        If key \s does not appear in the any, does nothing.

        Assumes that if key \s appears in the any it has not already been extracted
        by this iterator.  If it has been read before, an assertion will fail in debug mode.

        \return True if the value was read.
      */
    template<class ValueType>
    bool getIfPresent(const std::string& s, ValueType& v) {
        if (m_any.containsKey(s)) {
            debugAssertM(! m_alreadyRead.contains(toLower(s)), "read twice");

            get(s, v);
            return true;
        } else {
            return false;
        }
    }
};

}    // namespace G3D

#endif
