#pragma once

#include <string>
#include <list>
#include <vector>
#include <map>
#include <climits>
#include "util/Cloneable.h"
#include "util/Pointers.h"
#include "util/AgObject.h"
#include "util/simpleobject/AgString.h"
#include <boost/enable_shared_from_this.hpp>

class TableFormat : public Cloneable, public AgObject, public boost::enable_shared_from_this<TableFormat>
{ 

public:
    static const int DEFAULT_MIN_RECORDS = 0;
    static const int DEFAULT_MAX_RECORDS = INT_MAX;

    static const AgChar RECORD_VALIDATOR_KEY_FIELDS = 'K';
    static const AgChar TABLE_VALIDATOR_EXPRESSION = 'E';
    static const AgChar TABLE_VALIDATOR_KEY_FIELDS = 'K';

private:
    static AgString ELEMENT_FLAGS() { return "F"; }
    static AgString ELEMENT_TABLE_VALIDATORS() { return "V"; }
    static AgString ELEMENT_RECORD_VALIDATORS() { return "R"; }
    static AgString ELEMENT_BINDINGS() { return "B"; }
    static AgString ELEMENT_MIN_RECORDS() { return "M"; }
    static AgString ELEMENT_MAX_RECORDS() { return "X"; }
    static AgString ELEMENT_NAMING() { return "N"; }

    static const AgChar REORDERABLE_FLAG = 'R';
    static const AgChar UNRESIZEBLE_FLAG = 'U';
    static const AgChar BINDINGS_EDITABLE_FLAG = 'B';


    std::vector<FieldFormat*> fields;
    std::map<AgString, int> fieldLookup;

    int minRecords;
    int maxRecords;

    bool reorderable;
    bool unresizable;
    bool bindingsEditable;

    std::list<RecordValidatorPtr> recordValidators;
    std::list<TableValidatorPtr> tableValidators;
    std::list<BindingPtr> bindings;
    ExpressionPtr namingExpression;

    bool immutable;
    DataTable *immutabilizer;

    static void encAppend(AgString &buffer, const AgString &name, const AgString &value, ClassicEncodingSettingsPtr settings);
    void createTableValidators(const AgString& source, ClassicEncodingSettingsPtr settings);
    void createRecordValidators(const AgString& source, ClassicEncodingSettingsPtr settings);
    void createBindings(const AgString& source, ClassicEncodingSettingsPtr settings);
    void createNaming(const AgString& source, ClassicEncodingSettingsPtr settings);
    AgString getEncodedFlags();
    AgString getEncodedTableValidators(ClassicEncodingSettingsPtr settings);
    AgString getEncodedRecordValidators(ClassicEncodingSettingsPtr settings);
    AgString getEncodedBindings(ClassicEncodingSettingsPtr settings);

    int getFieldsHashCode();
    static TableFormatPtr getEMPTY_FORMAT();
    void init();
    void init(int minRecords, int maxRecords);

public:


    TableFormat& addFields(std::list<FieldFormat*> &fieldFormats);
    TableFormat& addField(FieldFormat* ff);
    TableFormat& addField(const AgString& encodedFormat);
    void addField(char type, const AgString& name);
    TableFormat& addField(char type, const AgString& name, const AgString& description);
    TableFormat& addField(char type, const AgString& name, const AgString& description, AgObjectPtr defaultValue);
    TableFormat& addField(char type, const AgString& name, const AgString& description, AgObjectPtr defaultValue, const AgString &group);
    TableFormat& addField(char type, const AgString& name, const AgString& description, AgObjectPtr defaultValue, bool nullable);
    TableFormat& addField(char type, const AgString& name, const AgString& description, AgObjectPtr defaultValue, bool nullable, const AgString &group);

    TableFormat& addField(FieldFormat* ff, int index);
    TableFormat& addField(char type, const AgString& name, int index);

    TableFormat& removeField(const AgString& name);
    TableFormat& renameField(const AgString& oldName, const AgString& newName);
    char getFieldType(int index);
    AgString getFieldName(int index);
    int getFieldIndex(const AgString& name);
    int getFieldCount();
    std::vector<FieldFormat*> &getFields();
    std::list<RecordValidatorPtr> getRecordValidators();
    std::list<TableValidatorPtr> getTableValidators();
    int getMaxRecords();
    int getMinRecords();
    bool isReorderable();
    bool isUnresizable();
    void setUnresizable(bool unresizable);
    bool isBindingsEditable();
    void setBindingsEditable(bool bindingsEditable);
    std::list<BindingPtr> &getBindings();
    void addBinding(BindingPtr binding);
    void addBinding(ReferencePtr target, ExpressionPtr expression);
    void addBinding(const AgString& target, const AgString& expression);
    void removeBinding(BindingPtr binding);
    void setBindings(std::list<BindingPtr> in_bindings);
    ExpressionPtr getNamingExpression();
    AgString encode(bool useVisibleSeparators);
    AgString encode(ClassicEncodingSettingsPtr settings);

    std::map<AgString, int> &getFieldLookup();

    AgString toString();

    FieldFormat* getField(int index);
    FieldFormat* getField(const AgString& fieldName);
    bool hasField(const AgString& name);
    bool hasFields(char type);
    bool hasReadOnlyFields();

    std::list<AgString> getKeyFields();
    bool extend(TableFormatPtr other);
    AgString extendMessage(TableFormatPtr other);
    void addTableValidator(TableValidatorPtr tv);
    void addRecordValidator(RecordValidatorPtr rv);

    bool isReplicated();
    bool isReadonly();
    bool isGrouped();
    bool isAdvanced();
    bool isSingleRecord();
    bool isSingleField();
    bool isSingleCell();
    bool isEmpty();
    bool isImmutable();

    TableFormat& resetAllowedRecords();
    TableFormat& setMaxRecords(int maxRecords);
    TableFormat& setMinRecords(int minRecords);
    TableFormat& setReorderable(bool reorderable);
    TableFormat& setNamingExpression(ExpressionPtr namingExpression);
    TableFormat& setNamingExpression(const AgString &namingExpression);

    virtual int hashCode();
    virtual bool equals(AgObject* obj);
    void makeImmutable(DataTable *immutabilizer);
    void fixRecords(DataTable *table);

    virtual TableFormat *clone();

    virtual int compareTo(Comparable *obj);
    virtual std::string getClass();

public:
    TableFormat();
    virtual ~TableFormat();
    TableFormat(bool reorderable);
    TableFormat(int minRecords, int maxRecords);
    TableFormat(FieldFormat* ff);
    TableFormat(const AgString &format, ClassicEncodingSettingsPtr settings, bool validate = true);
    TableFormat(int minRecords, int maxRecords, const AgString& fieldFormat);
    TableFormat(int minRecords, int maxRecords, FieldFormat* fieldFormat);


};
