#include "SyntaxNode.h"
#include "parser.tab.h"
#include "flex_bison_common.h"
#include "debug.h"
#include <unordered_map>
#include <cstdlib>
#include <cstring>
#include "Trinity/String.h"

Node::Node()
{
    //copies lhs_loc, which is extracted in YYLLOC_DEFAULT from the LHS structure 'yyloc'
    sourceLocation = new YYLTYPE();
    sourceLocation->filename = lhs_loc.filename;
    sourceLocation->first_column = lhs_loc.first_column;
    sourceLocation->first_line = lhs_loc.first_line;
    sourceLocation->last_column = lhs_loc.last_column;
    sourceLocation->last_line = lhs_loc.last_line;
#ifdef LOCATION_TEST
    wcout << "Node location:[" << sourceLocation.first_line << ":" << sourceLocation.first_column << "-" << sourceLocation.last_line << ":" << sourceLocation.last_column << "]" << endl;
#endif
}
Node::~Node()
{
    delete sourceLocation;
}

void Node::delete_children()
{
    for (Node* child : this->get_children())
    {
        delete child;
    }
}

void Node::error(string err, string prefix)
{
    std::string error_string = prefix;
    char err_first_char = err.empty() ? ' ' : err.front();

    if (!prefix.empty() && !isspace(prefix.back()) && (!isspace(err_first_char)))
        error_string.push_back(' ');
    error_string.append(err);
    ::error(*sourceLocation, error_string);
}

void Node::dependency_traverse(DependencyResolver &resolver)
{
    resolver.declareNode(this);
    for (Node* &node : this->get_children())
    {
        resolver.declareDependency(this, node);
        node->dependency_traverse(resolver);
    }
    tb_dependency_check(resolver);
}

void Node::semantic_check()
{
    DependencyResolver resolver;
    vector<Node*> traverse_sequence;
    vector<vector<Node*>> recursive_dependencies;

    dependency_traverse(resolver);
    //get_traverse_sequence will remove the vertices with dependency problems.
    //thus, tb_semantic_check() can assume that all the dependencies are met, safely.
    resolver.get_traverse_sequence(traverse_sequence, recursive_dependencies);

    for (auto *it : traverse_sequence)
    {
        it->tb_semantic_check();
    }

    //for now, the legacy codegen does not support recursive dependencies, so
    //we just report such situation as an error. in the future we may want to
    //add support for recursive structures.
    for (auto &rec_ring : recursive_dependencies)
    {
        error("", "Recursive dependency detected.");
        for (auto *node : rec_ring)
        {
            node->error("here:");
        }
    }

    return;
}

/**
 * Copy construct from a pointer of another type(that).
 * Field pointer will be removed, which means that the
 * copy-constructed field type cannot be optional.
 */
NFieldType::NFieldType(NFieldType* const that)
{
    this->fieldType = that->fieldType;
    this->layoutType = that->layoutType;
    this->field = nullptr;
    this->sourceLocation = that->sourceLocation;

    switch (this->fieldType)
    {
    case FT_ARRAY:
        this->arrayInfo = that->arrayInfo;
        break;
    case FT_ATOM:
        this->atom_token = that->atom_token;
        break;
    case FT_ENUM:
        this->referencedNEnum = that->referencedNEnum;
        this->referencedTypeName = that->referencedTypeName;
        break;
    case FT_LIST:
        this->listElementType = that->listElementType;
        break;
    case FT_STRUCT:
        this->referencedNStruct = that->referencedNStruct;
        this->referencedTypeName = that->referencedTypeName;
        break;
    default:
        error("NFieldType::NFieldType(NFieldType* const that)");
    }
}

bool NFieldType::is_atom()
{
    return fieldType == FT_ATOM;
}

bool NFieldType::is_long()
{
    return is_atom() && atom_token == T_LONGTYPE;
}

bool NFieldType::is_enum()
{
    return fieldType == FT_ENUM;
}

bool NFieldType::is_bool()
{
    return fieldType == FT_ATOM && atom_token == T_BOOLTYPE;
}

bool NFieldType::is_integral()
{
    if (fieldType != FT_ATOM)
        return false;
    switch (atom_token)
    {
    case T_BYTETYPE:
    case T_SBYTETYPE:
    case T_SHORTTYPE:
    case T_USHORTTYPE:
    case T_INTTYPE:
    case T_UINTTYPE:
    case T_LONGTYPE:
    case T_ULONGTYPE:
        return true;
    default:
        return false;
    }
}

bool NFieldType::is_signed()
{
    switch (atom_token)
    {
    case T_SBYTETYPE:
    case T_SHORTTYPE:
    case T_INTTYPE:
    case T_LONGTYPE:
    case T_FLOATTYPE:
    case T_DOUBLETYPE:
    case T_DECIMALTYPE:
        return true;
    default:
        /* unsigned or non-integral/non-fp: return false. */
        return false;
    }
}

bool NFieldType::is_decimal()
{
    return fieldType == FT_ATOM && atom_token == T_DECIMALTYPE;
}

bool NFieldType::is_numeric()
{
    return this->is_integral() || this->is_floating_point();
}

bool NFieldType::is_floating_point()
{
    switch (atom_token)
    {
    case T_FLOATTYPE:
    case T_DOUBLETYPE:
    case T_DECIMALTYPE:
        return true;
    default:
        return false;
    }
}

bool NFieldType::is_string()
{
    return fieldType == FT_ATOM && (atom_token == T_STRINGTYPE || atom_token == T_U8STRINGTYPE);
}

bool NFieldType::is_container_of_strings()
{
    switch (fieldType)
    {
    case FT_ARRAY:
        //XXX currently this is not happening as strings don't fit in arrays.
        return (arrayInfo.arrayElement->is_string() || arrayInfo.arrayElement->is_container_of_strings());
    case FT_LIST:
        return (listElementType->is_string() || listElementType->is_container_of_strings());
    default:
        return false;
    }
}

bool NFieldType::is_datetime()
{
    return (fieldType == FT_ATOM && atom_token == T_DATETIMETYPE);
}

bool NFieldType::is_guid()
{
    return (fieldType == FT_ATOM && atom_token == T_GUIDTYPE);
}

bool NFieldType::is_optional()
{
    if (!field)
        return false;
    return field->is_optional();
}

//  A value type is a type 'allocated on stack' instead of 
//  'allocated on heap'.
bool NFieldType::is_value_type()
{
    switch (fieldType)
    {
    case FT_ARRAY:
    case FT_LIST:
        return false;
    case FT_ATOM:
        return !is_string();
    case FT_STRUCT:
    case FT_ENUM:
    default:
        /*Cannot have FT_REFERENCE after semantic calculation*/
        return true;
    }
}

bool NFieldType::is_list()
{
    return fieldType == FT_LIST;
}

bool NFieldType::is_array()
{
    return fieldType == FT_ARRAY;
}

bool NFieldType::is_container()
{
    return is_list() || is_array();
}

bool NFieldType::is_struct()
{
    return fieldType == FT_STRUCT;
}

bool NFieldType::is_nullable()
{
    return is_optional() && is_value_type();
}

/**
 * Tell if both this and that are arrays of the same element type, and
 * of the same rank.
 */
bool NFieldType::is_array_same_rank_same_element_type(NFieldType* that)
{
    return (this != that && this->is_array() && that->is_array() &&
            !NFieldType_Compare(this->arrayInfo.arrayElement, that->arrayInfo.arrayElement)
            &&
            this->arrayInfo.array_dimension_size->size() == that->arrayInfo.array_dimension_size->size());
}

/**
 * Tell if this type is assignable from that type.
 * We say if this type is assignable from that type,
 * if the assignment thisVar = thatVar is valid.
 * By definition, the following rules apply for validity:
 *
 *  - this and that are the same.
 *  - If this is nullable and that is not, but the non-nullable version of this is assignable from that,
 *    then this is still assignable from that.
 *  - If this is non-nullable while that is, then this is not assignable from that.
 *  - this and that are both lists of a same element type.
 *  - this and that are structs with same type.
 *  - this and that are enums with same type.
 *  - this and that are atoms, and:
 *    - both are strings.
 *    - both are bools.
 *    - both are integral, and:
 *      - longer signed type is assignable from shorter signed/unsigned.
 *      - longer unsigned type is assignable from shorter unsigned.
 *    - longer floating-point is assignable from shorter fp.
 *      - does not apply to decimal <- shorter fp.
 *      - however decimal <- shorter integral is valid.
 *    - floating-point is assignable from integral.
 *
 * @param   *this   The target type to examine.
 * @param    that   The other field type to examine.
 */

bool NFieldType::is_assignable_from(NFieldType* that)
{
    if (!NFieldType_Compare(this, that))
        return true;

    if (!this->is_nullable() && that->is_nullable())
        return false;

    if (this->is_nullable() && !that->is_nullable())
    {
        // mark this temporarily as non-optional
        this->field->unset_optional();

        bool non_optional_this_assignable_from_that = this->is_assignable_from(that);

        // mark this back as optional
        this->field->set_optional();

        return non_optional_this_assignable_from_that;
    }

    /* For arrays with same rank & same element type, we say they are compatible. */
    if (this->is_array_same_rank_same_element_type(that))
        return true;

    if (this->is_list() && that->is_list())
        return (0 == NFieldType_Compare(this->listElementType, that->listElementType));

    if (this->is_enum() && that->is_enum())
        return this->referencedNEnum == that->referencedNEnum;

    if (this->is_struct() && that->is_struct())
        return this->referencedNStruct == that->referencedNStruct;

    if (!(this->is_atom() && that->is_atom()))
        return false;

    /* Here we have this->is_atom() and that->is_atom */

    if (this->is_string() && that->is_string())
        return true;

    if (this->is_integral() && that->is_integral())
    {
        return
            (this->type_size() > that->type_size() && (this->is_signed() || !that->is_signed()))
            ||
            (this->type_size() == that->type_size()
             &&
             this->is_signed() == that->is_signed());
    }

    if (this->is_bool() && that->is_bool())
        return true;

    if (this->is_floating_point() && that->is_integral())
        return true;

    if (this->is_floating_point() && that->is_floating_point())
    {
        if (this->is_decimal() /* && !that->is_decimal() */)
            return false;

        return this->type_size() > that->type_size();
    }

    /* anything else, we say this is not assignable from that. */
    return false;
}

/**
 * Determine the conversion action from that type to this type.
 * This is convertible from that if:
 *
 *  - This is assignable from that.
 *  - This is string or that is string.
 *  - This is bool and that is numeric.
 *  - This is list and:
 *    - That is list, and the element of this is convertible from the element of that.
 *    - That can be converted to an element of this.
 *    - That is array and the element types are convertible.
 *  - TODO This is array and that is list
 *  - This is convertible from the non-nullable version of that.
 */

TypeConversionAction NFieldType::get_type_conversion_action(NFieldType* that)
{
    if (this->is_assignable_from(that))
        return TypeConversionAction::TC_ASSIGN;

    if (this->is_string() && !that->is_string())
        return TypeConversionAction::TC_TOSTRING;

    if (!this->is_string() && that->is_string())
        return TypeConversionAction::TC_PARSESTRING;

    if (this->is_bool() && that->is_numeric())
        return TypeConversionAction::TC_TOBOOL;

    if (this->is_list())
    {
        if (that->is_list() && this->listElementType->is_convertible_from(that->listElementType))
            return TypeConversionAction::TC_CONVERTLIST;

        if (this->listElementType->is_convertible_from(that))
            return TypeConversionAction::TC_WRAPINLIST;

        if (that->is_array() && this->listElementType->is_convertible_from(that->arrayInfo.arrayElement))
            return TypeConversionAction::TC_ARRAYTOLIST;
    }

    if (!this->is_nullable() && that->is_nullable())
    {
        that->field->unset_optional();
        TypeConversionAction conv_action_non_nullable = this->get_type_conversion_action(that);
        that->field->set_optional();

        if (conv_action_non_nullable != TypeConversionAction::TC_NONCONVERTIBLE)
            return TypeConversionAction::TC_EXTRACTNULLABLE;
    }

    /* Otherwise, this is not convertible from that. */
    return TypeConversionAction::TC_NONCONVERTIBLE;
}

/**
 * Tell if this type is convertible from that type by the type converter generated by Traits.cs.
 */

bool NFieldType::is_convertible_from(NFieldType* that)
{
    return (TypeConversionAction::TC_NONCONVERTIBLE != this->get_type_conversion_action(that));
}

/**
 * An alias is a field type that we want to hide from being output to the code,
 * and instead be renamed to something else everytime that it appears.
 * The purpose of alias is to support different internal types that map to the
 * same runtime type recognized by the compiler.
 * For example, string and u8string are both mapped to System.String, but we
 * use the u8string as an internal representation to generate accessors.
 */
bool NFieldType::is_alias()
{
    if (this->fieldType == FT_LIST) { return this->listElementType->is_alias(); }
    else { return this->is_string() && this->atom_token == T_U8STRINGTYPE; }
}

/**
 * A FieldType has corresponding pointer type if and only if:
 * 1. its layout is LT_FIXED AND
 * 2. it is NOT an array AND
 * 3. it is NOT a struct containing a field whose type has no pointer type.
 */
bool NFieldType::has_pointer_type()
{
    if (this->layoutType != LT_FIXED || this->is_array()) return false;
    if (!this->is_struct()) return true;

    for (NField* field : *this->referencedNStruct->fieldList)
    {
        if (!field->fieldType->has_pointer_type())
            return false;
    }
    return true;
}

int _enumerate_rank(NFieldType* type)
{
    NFieldType* ft_container_element = type->get_container_element_type();
    if (ft_container_element != NULL)
        return 1 + _enumerate_rank(ft_container_element);
    else
        return 0;
}

/**
 * Tells how many orders should this go to yield that.
 * This method will greedily dig into the container elements, until the "order" of the element
 * is no more than that type.
 * For example, calling List<List<int>>->enumerate_depth(string) will not return 0, causing
 * serializer to serialize the whole object as a string. Instead it digs into the container chain
 * and decide that enumerate_depth should be 2 so that it flattens all the integers and converts
 * them to strings.
 * On the other hand, "the order of the element is no more than that type" means that when we
 * want to enumerate containers ("that" is a container), we do not dig too much into "this",
 * and wrap high-order "this" container elements into "that". For example, calling
 * List<int>->enumerate_depth(List<List<int>>) should not wrap each integer element into List<List<int>>,
 * but rather, wrap the whole List<int> into a single element in List<List<int>>. The same applies
 * to List<List<int>>->enumerate_depth(List<int>), we do not enumerate 2nd-order (integer) elements
 * and wrap them each into a list with a single element, but rather enumerate 1st-order List<int>
 * elements.
 *
 * @returns     N if that is convertible from the N-th order container element (this->element->element...),
                (Go down orders) without wrapping the N-th order container element into a container.
 * -otherwise-  0 if that is convertible from this. (Direct conversion, or go up orders)
 * -otherwise- -1 if cannot enumerate.
 */

int NFieldType::enumerate_depth(NFieldType* that)
{
    bool directly_convertible = that->is_convertible_from(this);

    if (_enumerate_rank(this) > _enumerate_rank(that))
    {
        NFieldType* ft_container_element = this->get_container_element_type();
        if (ft_container_element != NULL)
        {
            int element_enum_depth = ft_container_element->enumerate_depth(that);
            if (element_enum_depth >= 0)
                return 1 + element_enum_depth;
        }
    }

    return directly_convertible ? 0 : -1;
}

/**
 * Complementary to enumerate_depth. We say that a field type can enumerate elements of another field type, if:
 *
 *   - That is convertible from this
 *   - This type is a container and its element can enumerate that.
 */
bool NFieldType::can_enumerate(NFieldType* that)
{
    if (that->is_convertible_from(this))
        return true;

    NFieldType* ft_container_element = this->get_container_element_type();
    if (ft_container_element != NULL && ft_container_element->can_enumerate(that))
        return true;

    return false;
}

NFieldType* NFieldType::get_container_element_type()
{
    if (this->is_array())
        return this->arrayInfo.arrayElement;

    if (this->is_list())
        return this->listElementType;

    return NULL;
}

size_t NFieldType::type_size()
{
    if (layoutType != LT_FIXED) { return -1; }

    if (is_atom())
    {
        switch (atom_token)
        {
        case T_BYTETYPE:
        case T_SBYTETYPE:
        case T_BOOLTYPE:
            return 1;
        case T_SHORTTYPE:
        case T_USHORTTYPE:
		case T_CHARTYPE:
            return 2;
        case T_INTTYPE:
        case T_UINTTYPE:
        case T_FLOATTYPE:
            return 4;
        case T_LONGTYPE:
        case T_ULONGTYPE:
        case T_DOUBLETYPE:
        case T_DATETIMETYPE:
            return 8;
        case T_DECIMALTYPE:
        case T_GUIDTYPE:
            return 16;
        }
    }
    else if (is_struct())
    {
        size_t size = 0;
        for (auto &field : *referencedNStruct->fieldList)
        {
            size += field->fieldType->type_size();
        }
        return size;
    }
    else if (is_array())
    {
        size_t size = 1;
        for (auto s : *arrayInfo.array_dimension_size) { size *= s; }
        return size * arrayInfo.arrayElement->type_size();
    }
    else if (is_enum())
    {
        return 1;
    }

    error("type_size(): unknown type");
    return -1;
}

std::string NFieldType::get_atom_type()
{
    switch (atom_token)
    {
    case T_BYTETYPE:
        return "byte";
    case T_SBYTETYPE:
        return "sbyte";
    case T_BOOLTYPE:
        return "bool";
    case T_CHARTYPE:
        return "char";
    case T_SHORTTYPE:
        return "short";
    case T_USHORTTYPE:
        return "ushort";
    case T_INTTYPE:
        return  "int";
    case T_UINTTYPE:
        return "uint";
    case T_LONGTYPE:
        return "long";
    case T_ULONGTYPE:
        return "ulong";
    case T_FLOATTYPE:
        return "float";
    case T_DOUBLETYPE:
        return "double";
    case T_DECIMALTYPE:
        return "decimal";
    case T_DATETIMETYPE:
        return "DateTime";
    case T_GUIDTYPE:
        return "Guid";
    case T_STRINGTYPE:
        return "string";
    case T_U8STRINGTYPE:
        return "u8string";
    default:
        return "???";
    }
}

bool string_is_number(string* pstr)
{
    for (auto chr : *pstr)
        if (!isdigit(chr))return false;
    return true;
}

void NFieldType::parse_array_dimension_size()
{
    arrayInfo.array_dimension_size = new vector<int>();
    for (auto *it : *arrayInfo.array_dimension_list)
    {
        if (string_is_number(it))
        {
            arrayInfo.array_dimension_size->push_back(stoi(*it));//TODO exception handling
        }
        else
        {
            //TODO macro support
            error(*it + ": not a number, nor a defined value");
        }
        delete it;
    }
    delete arrayInfo.array_dimension_list;
    arrayInfo.array_dimension_list = NULL;
}

int NFieldType::compare_array_dimension_size_with(NFieldType* that)
{
    for (size_t dim = 0; dim < this->arrayInfo.array_dimension_size->size(); ++dim)
    {
        //TODO invent an iterator over two/more containers
        int ret = this->arrayInfo.array_dimension_size->at(dim) - that->arrayInfo.array_dimension_size->at(dim);
        if (ret != 0)
            return ret;
    }
    return 0;
}

int NFieldType_Compare(NFieldType* lhs, NFieldType* rhs)
{
    int ret;

    ret = lhs->is_optional() - rhs->is_optional();

    if (ret != 0)
        return ret;

    ret = lhs->fieldType - rhs->fieldType;
    if (ret != 0)
        return ret;
    switch (lhs->fieldType)
    {
    case FT_ARRAY:
        ret = NFieldType_Compare(lhs->arrayInfo.arrayElement, rhs->arrayInfo.arrayElement);
        if (ret != 0)
            return ret;
        ret =
            lhs->arrayInfo.array_dimension_size->size()
            -
            rhs->arrayInfo.array_dimension_size->size();
        if (ret != 0)
            return ret;
        for (size_t dim = 0; dim < lhs->arrayInfo.array_dimension_size->size(); ++dim)
        {
            ret = lhs->arrayInfo.array_dimension_size->at(dim) - rhs->arrayInfo.array_dimension_size->at(dim);
            if (ret != 0)
                return ret;
        }
        return 0;
    case FT_ATOM:
        return (lhs->atom_token - rhs->atom_token);
    case FT_ENUM:
    case FT_REFERENCE:
    case FT_STRUCT:
        return (lhs->referencedTypeName->compare(*rhs->referencedTypeName));
    case FT_LIST:
        return NFieldType_Compare(lhs->listElementType, rhs->listElementType);
    }
    /* Should not reach here. */
    error("Internal error T5002.");
    return -1;
}
/* Return true if lhs < rhs */
bool NFieldType_LessThan(NFieldType *lhs, NFieldType *rhs)
{
    return NFieldType_Compare(lhs, rhs) < 0;
}
bool NFieldType_LessThan(const std::unique_ptr<NFieldType> &lhs, const std::unique_ptr<NFieldType> &rhs)
{
    return NFieldType_Compare(lhs.get(), rhs.get()) < 0;
}

bool NField::is_optional()
{
    for (auto m : *modifiers)
        if (m == T_OPTIONALMODIFIER)
            return true;
    return false;
}

void NField::set_optional()
{
    modifiers->push_back(T_OPTIONALMODIFIER);
}

void NField::unset_optional()
{
    auto it = std::find(modifiers->begin(), modifiers->end(), T_OPTIONALMODIFIER);
    if (it != modifiers->end())
        modifiers->erase(it);
}

/**
 *  @return     True if a property is successfully set.
 *              False if there's an error.
 */

bool NIndex::set_property(Trinity::String& key, Trinity::String& value)
{
    key.Trim().ToLower();
    if (key == "target")
    {
        if (this->target != NULL)
        {
            error("Duplicated target designation.");
            return false;
        }
        this->target = new std::string(*field->name);
        this->target->append(".").append(value.Data());
        return true;
    }
    else if (key == "type")
    {
        value.ToLower();
        if (value == "substring")
        {
            this->type = IndexType::IT_SUBSTRING;
        }
        else
        {
            error("Unrecognized index type.");
            return false;
        }
        return true;
    }
    else
    {
        error("Unrecognized index property.");
        return false;
    }
}

std::unique_ptr<std::vector<NField*>> NIndex::resolve_target()
{
    std::unique_ptr<std::vector<NField*>>   path(new std::vector<NField*>());
    NStructBase*                            current_struct = cell;
    size_t                                  current_offset = 0;
    size_t                                  length = target->length();

    do
    {
        auto next_offset = target->find_first_of('.', current_offset);
        std::string field_name = target->substr(current_offset,
                                                next_offset == std::string::npos ?
                                                std::string::npos :
                                                next_offset - current_offset);
        NField* current_field = NULL;
        for (auto* f : *current_struct->fieldList)
        {
            if (*f->name == field_name)
            {
                current_field = f;
                break;
            }
        }
        if (current_field == NULL)
        {
            error("Unrecognized field'" + field_name + "'.");
            goto resolve_target_failed;
        }
        path->push_back(current_field);
        if (next_offset == std::string::npos)
        {
            break;
        }
        else
        {
            current_offset = next_offset + 1;
            if (current_offset >= length)
            {
                error("Syntax error. Expecting an identifier.");
                goto resolve_target_failed;
            }
            if (current_field->fieldType->is_struct())
            {
                current_struct = current_field->fieldType->referencedNStruct;
            }
            else if (current_field->fieldType->is_container())
            {
                auto *container_chain = current_field->fieldType->resolve_container_chain();
                //TODO
                error("Field '" + field_name + "' is not a struct.");
                goto resolve_target_failed;
            }
            else
            {
                error("Field '" + field_name + "' is not a struct.");
                goto resolve_target_failed;
            }
        }
    } while (true);

    return path;

resolve_target_failed:
    return std::unique_ptr<std::vector<NField*>>(nullptr);
}

NIndex::NIndex(NCell* cell, NField* field, NKVPair* indexDescriptor)
{
    this->cell           = cell;
    this->sourceLocation = indexDescriptor->sourceLocation;
    this->type           = IndexType::IT_UNDEFINED;
    this->target         = NULL;
    this->field          = field;

    Trinity::String index_str = *indexDescriptor->value;
    if (index_str.StartsWith("\"") && index_str.EndsWith("\""))
        index_str = index_str.Substring(1, index_str.Length() - 2);
    auto properties = index_str.Split(";,");
    for (auto property : properties)
    {
        auto kvp = property.Trim().Split("=");
        if (kvp.Length() != 2)
        {
            error("Syntax error. Index properties should be specified in 'property = value;' form.");
            return;
        }
        if (!set_property(kvp[0], kvp[1]))
            return;
    }

    if (this->target == NULL)
    {
        //XXX memory leak
        this->target = field->name;
    }

    if (this->type == IndexType::IT_UNDEFINED)
    {
        this->type = IndexType::IT_SUBSTRING;
    }

    auto target_field_accessing_path = resolve_target();
    if (target_field_accessing_path.get() == NULL)
    {
        error("Unrecognized target.");
        return;
    }

    NField* target_field = target_field_accessing_path->back();
    if (this->type == IndexType::IT_SUBSTRING &&
        !target_field->fieldType->is_string() &&
        !target_field->fieldType->is_container_of_strings())
    {
        error("Substring index is only valid for strings or containers of strings.");
        return;
    }
    this->target_field = target_field;
}

/**
 * Copy construct, parent pointer is dropped.
 */
NField::NField(NField* const other)
{
    this->fieldType = new NFieldType(other->fieldType);
    this->fieldType->field = this;
    this->modifiers = new vector<int>();
    this->modifiers->insert(this->modifiers->end(), other->modifiers->begin(), other->modifiers->end());
    this->attributes = new vector<NKVPair*>();
    this->parent = nullptr;
    this->name = new string(*other->name);

    for (auto a_ : *other->attributes)
    {
        NKVPair* a = new NKVPair(a_);
    }
}

void NField::aggregate_indices(NCell* cell)
{
    std::vector<NIndex*>    *indexList = cell->indexList;
    for (auto* kvpair : *attributes)
    {
        Trinity::String key = *kvpair->key;
        key.Trim().ToLower();
        if (key == "index")
        {
            auto idx = new NIndex(cell, this, kvpair);
            if (idx->target != NULL)
                indexList->push_back(idx);
            else
                delete idx;
        }
    }
}

std::string* NField::get_attribute(const std::string& key)
{
    for (auto &kvp : *attributes)
    {
        if (*kvp->key == key)
            return kvp->value;
    }

    return nullptr;
}

void NFieldType::fill_with_sub_field_types(std::vector<NFieldType*>* list)
{
    switch (fieldType)
    {
    case FT_ARRAY:
        list->push_back(arrayInfo.arrayElement);
        arrayInfo.arrayElement->fill_with_sub_field_types(list);
        break;
    case FT_LIST:
        list->push_back(listElementType);
        listElementType->fill_with_sub_field_types(list);
        break;
    case FT_STRUCT:
        referencedNStruct->fill_with_sub_field_types(list);
        break;
    case FT_ATOM:
    case FT_ENUM:
    default:
        break;
    }
}

std::vector<NFieldType*>* NFieldType::resolve_container_chain()
{
    NFieldType* current_ft        = const_cast<NFieldType*>(this);
    std::vector<NFieldType*>* ret = new std::vector<NFieldType*>();
    while (true)
    {
        ret->push_back(current_ft);
        switch (current_ft->fieldType)
        {
        case FT_ARRAY:
            current_ft = current_ft->arrayInfo.arrayElement;
            break;
        case FT_LIST:
            current_ft = current_ft->listElementType;
            break;
        default:
            return ret;
        }
    }
}

void NStructBase::fill_with_sub_field_types(std::vector<NFieldType*>* list)
{
    for (auto *field : *fieldList)
    {
        list->push_back(field->fieldType);
        field->fieldType->fill_with_sub_field_types(list);
    }
}

LayoutType NStructBase::getLayoutType()
{
    for (auto *field : *fieldList)
    {
        if (field->getLayoutType() == LT_DYNAMIC || field->is_optional())
            return LT_DYNAMIC;
    }

    return LT_FIXED;
}

/**
 * Copy construct.
 */

NStructBase::NStructBase(NStructBase* const other)
{
    fieldList = new vector<NField*>();
    attributes = new vector<NKVPair*>();
    name = new string(*other->name);

    for (auto f_ : *other->fieldList)
    {
        NField* f = new NField(f_);
        f->parent = this;
        fieldList->push_back(f);
    }

    for (auto a_ : *other->attributes)
    {
        NKVPair* a = new NKVPair(a_);
    }
}

bool NProtocolGroup::has_http_protocol()
{
    for (auto *protocol_reference : *protocolList)
    {
        if (tsl->find_protocol(protocol_reference->name)->is_http_protocol())
            return true;
    }
    return false;
}

