/**
 * @file
 *
 * @copyright
 * @verbatim
   Copyright @ 2017 Audi Electronics Venture GmbH. All rights reserved.

       This Source Code Form is subject to the terms of the Mozilla
       Public License, v. 2.0. If a copy of the MPL was not distributed
       with this file, You can obtain one at https://mozilla.org/MPL/2.0/.

   If it is not possible or desirable to put the notice in a particular file, then
   You may include the notice in a location (such as a LICENSE file in a
   relevant directory) where a recipient would be likely to look for such a notice.

   You may add additional accurate notices of copyright ownership.
   @endverbatim
 */

#include <ddl.h>
#include "header_importer.h"
#include "header_basic_type.h"
#include "header_typedef.h"
#include "header_header.h"
#include "parserhelper.h"

namespace ddl_generator
{
    namespace oo
    {
        //define all needed error types and values locally
        _MAKE_RESULT(0, ERR_NOERROR)
        _MAKE_RESULT(-4, ERR_POINTER)
        _MAKE_RESULT(-5, ERR_INVALID_ARG)
        _MAKE_RESULT(-11, ERR_INVALID_FILE)
        _MAKE_RESULT(-20, ERR_NOT_FOUND)
        _MAKE_RESULT(-24, ERR_PATH_NOT_FOUND)
        _MAKE_RESULT(-36, ERR_UNKNOWN_FORMAT)
        _MAKE_RESULT(-37, ERR_NOT_INITIALISED)
        _MAKE_RESULT(-38, ERR_FAILED)
    }
}

using namespace ddl_generator::oo;

using namespace ddl;


namespace ddl
{
#define AMOUNT_OF_TOKENS 3
    //#define AMOUNT_OF_SINGLE_SEPARATORS 27
    const std::string g_tokens[AMOUNT_OF_TOKENS] = { "struct", "enum", "union" };
    const std::string g_single_separators = "{}[]#()<>%:;.?*+-/�&|�!=,\\\"�;";
    const std::string g_name_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";


    class HeaderParserHelper
    {
    public:
        /**
         * CTOR
         */
        HeaderParserHelper() {};

        /**
         * DTOR
         */
        virtual ~HeaderParserHelper() {};

        /**
         * The method skipToNextContent skips comments and whitespace
         * until the next code fragment or the end of the file is reached.
         *
         * @param [in] src  Pass a pointer to a string,
         *                   returns a pointer to the next character in the string,
         *                   that is part of the code.
         */
        static void skipToNextContent(const char* &src)
        {
            const char* current = NULL;
            const char* forward_search = NULL;
            while (current != src)
            {
                current = src;
                forward_search = src;
                skipComment(src, getNextElement(forward_search));
                ddl_generator::skipWhitespace(src);
            }
        }

        /**
         * The method isValidName checks whether a string is a valid name for either a variable or a type.
         * Used definition:
         * Must start with a letter oder an underscore.
         * Second to last character can be a number, letter or underscore.
         *
         * @param [in] name  The name to be analyzed.
         * @returns  True if the name is valid, false otherwise.
         */
        static bool isValidName(const std::string &name)
        {
            a_util::regex::RegularExpression reg_ex("^[_a-zA-Z][_a-zA-Z0-9]*$");
            int consumed;
            return reg_ex.match(name, a_util::regex::RegularExpression::AT_Both, consumed);
        }

        /**
         * The method skipBlock skips the next block.
         * Example:
         * bla = 5;
         * typedef struct {
         *   int a;
         * };
         * The function skips to the semicolon after the last parenthesis.
         *
         * @param [in, out] src  The source to be skipped, points to the next character after the parenthesis.
         */
        static void skipBlock(const char* &src)
        {
            uint64_t depth = 0;
            while (*src != '\0' && *src != '{')
            {
                src++;
            }
            depth++;
            while (*src != '\0')
            {
                if (*src == '{')
                {
                    depth++;
                }
                else if (*src == '}')
                {
                    depth--;
                    if (depth == 0)
                    {
                        src++;
                        break;
                    }
                }
                src++;
            }
        }

        /**
         * The method exitBlock exists from the current block;
         * Example
         * blubb = 5;
         * bla blubber;
         * }
         * The method jumps to the character after the parenthesis.
         *
         * @param [in] src  The string to be analyzed, returns a pointer to the character after the parenthesis
         * of the current block or to the end of the string.
         */
        static void exitBlock(const char* &src)
        {
            while (*src != '\0')
            {
                if (*src == '}')
                {
                    src++;
                    break;
                }
                if (*src == '{')
                {
                    skipBlock(src);
                }
                else
                {
                    src++;
                }
            }
        }

        /**
         * The function getNextElement extracts the next c++ element.
         * This can be a whole word like a type name or a single character like
         * a plus sign or the beginning/end of a comment line //, / * or * /
         * (cannot write the "comment close" without space or it would break the code of this file)
         * This function will not check any validity. So Names like "123onetwothree" are allowed.
         *
         * @param [in, out] src  The string to be analyzed, returns a pointer to the character after found element.
         * @returns  A string containing the element.
         */
        static std::string getNextElement(const char* &src)
        {
            std::string result = "";
            while (*src != '\0')
            {
                //Check for a number or a name
                if (g_name_chars.find(*src) != std::string::npos)
                {
                    result.push_back(*src);
                    src++;
                    while (g_name_chars.find(*src) != std::string::npos)
                    {
                        result.push_back(*src);
                        src++;
                    }
                    break;
                }
                // Check for a single character like +, - and so on
                else if (g_single_separators.find(*src) != std::string::npos)
                {
                    result = *src;
                    src++;
                    // Check for comment
                    if (result == "/")
                    {
                        if (*src == '/' || *src == '*')
                        {
                            result.push_back(*src);
                            src++;
                        }
                    }
                    else if (result == "*")
                    {
                        if (*src == '/')
                        {
                            result.push_back(*src);
                            src++;
                        }
                    }
                    //Check for escape character
                    else if (result == "\\")
                    {
                        result.push_back(*src);
                        src++;
                    }
                    break;
                }
                else
                {
                    // Probably just space or newlines. If anything else has not been captured by this algorithm,
                    // please add it here or where it fits.
                    src++;
                }
            }
            return result;
        }

        /**
         * The function getNextCodeElement extracts the next c++ element outside a comment.
         * This can be a whole word like a type name or a single character like
         * a plus sign.
         * This function will not check any validity. So Names like "123onetwothree" are allowed.
         * This function assumes that it is outside a comment.
         *
         * @param [in, out] src  The string to be analyzed, returns a pointer to the character after found element.
         * @returns  A string containing the element.
         */
        static std::string getNextCodeElement(const char* &src)
        {
            std::string result;
            while (*src != '\0')
            {
                result = getNextElement(src);
                if (result == "/*" || result == "//")
                {
                    skipComment(src, result);
                }
                else
                {
                    break;
                }
            }
            return result;
        }

        /**
         * The method skipComment skips a comment. Use in conjunction with getNextElement().
         *
         * @param [in, out] p  A pointer to the string containing the comment.
                               The pointer will point to the first character after the comment.
         * @param [in] comment_begin  The string containing the begin of the comment.
         *             if the string does not contain a comment beginning (line // or / *)
         *             nothing will be skipped.
         */
        static void skipComment(const char* &src, const std::string &comment_begin)
        {
            if (comment_begin == "//")
            {
                skipEOL(src);
            }
            else if (comment_begin == "/*")
            {
                while ((getNextElement(src) != "*/") && (*src != '\0'))
                {
                }
            }
        }

        /**
         * The method SkipToEOL [...]
         *
         * @param [in, out] src  A pointer to the string containing the comment.
                                  The pointer will point to the first character after the EOL character (0x0A).
         */
        static void skipEOL(const char* &src)
        {
            while (*src != '\n' && *src != '\0')
            {
                src++;
            }
            src++;
        }
    };


    HeaderImporter::HeaderImporter()
    {
        _types = NULL;
        _header = NULL;
        _default_type = NULL;
    }

    HeaderImporter::~HeaderImporter()
    {
        _types = NULL;
        _header = NULL;
        _default_type = NULL;
    }

    a_util::result::Result HeaderImporter::setHeaderString(const std::string &source)
    {
        _input_file = a_util::filesystem::Path();
        _header_source = source;
        return ERR_NOERROR;
    }

    a_util::result::Result HeaderImporter::setFileName(const a_util::filesystem::Path &filename)
    {
        _header_source = "";
        _input_file = filename;
        return ERR_NOERROR;
    }

    a_util::result::Result HeaderImporter::setKnownTypes(const HeaderTypesVec* types)
    {
        _types = types;
        return ERR_NOERROR;
    }

    a_util::result::Result HeaderImporter::setDefaultIntegerType(const HeaderType* type)
    {
        _default_type = type;
        return ERR_NOERROR;
    }

    HeaderTypesVec* HeaderImporter::getDefaultTypes()
    {
        HeaderBasicType* basic_type = NULL;
        HeaderTypesVec* types = new HeaderTypesVec();
        
        // Remark: t-Types(for example tBool, tUInt32) are keeped for compliance with old versions

        basic_type = new HeaderBasicType("bool", 1, 8);
        types->push_back(basic_type);
        types->push_back(new HeaderTypedef("tBool", basic_type));
        
        basic_type = new HeaderBasicType("char", 1, 8);
        types->push_back(basic_type);
        types->push_back(new HeaderTypedef("tChar", basic_type));

        basic_type = new HeaderBasicType("uint8_t", 1, 8); 
        types->push_back(basic_type);
        types->push_back(new HeaderTypedef("unsigned char", basic_type));
        types->push_back(new HeaderTypedef("tUInt8", basic_type));
        types->push_back(new HeaderTypedef("uint_least8_t", basic_type));
        types->push_back(new HeaderTypedef("uint_least8_t", basic_type));
        types->push_back(new HeaderTypedef("uint_fast8_t", basic_type));

        basic_type = new HeaderBasicType("int8_t", 1, 8);
        types->push_back(basic_type);
        types->push_back(new HeaderTypedef("signed char", basic_type));
        types->push_back(new HeaderTypedef("char", basic_type)); 
        types->push_back(new HeaderTypedef("tInt8", basic_type));
        types->push_back(new HeaderTypedef("int_least8_t", basic_type));
        types->push_back(new HeaderTypedef("int_least8_t", basic_type));
        types->push_back(new HeaderTypedef("int_fast8_t", basic_type));

        basic_type = new HeaderBasicType("uint16_t", 2, 16);
        types->push_back(basic_type);
        types->push_back(new HeaderTypedef("unsigned short", basic_type));
        types->push_back(new HeaderTypedef("tUInt16", basic_type)); 
        types->push_back(new HeaderTypedef("uint_least16_t", basic_type));

        basic_type = new HeaderBasicType("int16_t", 2, 16);
        types->push_back(basic_type);
        types->push_back(new HeaderTypedef("signed short", basic_type));
        types->push_back(new HeaderTypedef("short", basic_type));
        types->push_back(new HeaderTypedef("tInt16", basic_type)); 
        types->push_back(new HeaderTypedef("int_least16_t", basic_type));
        
        basic_type = new HeaderBasicType("uint32_t", 4, 32);
        types->push_back(basic_type);
#ifdef __ADTF32
        types->push_back(new HeaderTypedef("unsigned long", basic_type));
#endif
        types->push_back(new HeaderTypedef("unsigned int", basic_type));
        types->push_back(new HeaderTypedef("tUInt32", basic_type)); 
        types->push_back(new HeaderTypedef("uint_least32_t", basic_type));
        types->push_back(new HeaderTypedef("uint_fast16_t", basic_type));
        types->push_back(new HeaderTypedef("uint_fast32_t", basic_type));

        basic_type = new HeaderBasicType("int32_t", 4, 32);
        types->push_back(basic_type);
#ifdef __ADTF32
        types->push_back(new HeaderTypedef("signed long", basic_type));
        types->push_back(new HeaderTypedef("long", basic_type));
#endif
        types->push_back(new HeaderTypedef("signed int", basic_type));
        types->push_back(new HeaderTypedef("int", basic_type)); 
        types->push_back(new HeaderTypedef("tInt32", basic_type));
        types->push_back(new HeaderTypedef("int_least32_t", basic_type));
        types->push_back(new HeaderTypedef("int_fast16_t", basic_type));
        types->push_back(new HeaderTypedef("int_fast32_t", basic_type));

        basic_type = new HeaderBasicType("uint64_t", 8, 64);
        types->push_back(basic_type);
        types->push_back(new HeaderTypedef("unsigned long", basic_type));
        types->push_back(new HeaderTypedef("unsigned long long", basic_type));
        types->push_back(new HeaderTypedef("unsigned __int64", basic_type));
        types->push_back(new HeaderTypedef("tUInt64", basic_type)); 
        types->push_back(new HeaderTypedef("uint_least64_t", basic_type));
        types->push_back(new HeaderTypedef("uint_least64_t", basic_type));

        basic_type = new HeaderBasicType("int64_t", 8, 64);
        types->push_back(basic_type);
        types->push_back(new HeaderTypedef("signed long", basic_type));
        types->push_back(new HeaderTypedef("long", basic_type));
        types->push_back(new HeaderTypedef("signed long long", basic_type));
        types->push_back(new HeaderTypedef("long long", basic_type));
        types->push_back(new HeaderTypedef("signed __int64", basic_type));
        types->push_back(new HeaderTypedef("__int64", basic_type));
        types->push_back(new HeaderTypedef("tInt64", basic_type)); 
        types->push_back(new HeaderTypedef("int_least64_t", basic_type));
        types->push_back(new HeaderTypedef("int_least64_t", basic_type));

        basic_type = new HeaderBasicType("float", 4, 32); 
        types->push_back(basic_type);
        types->push_back(new HeaderTypedef("tFloat32", basic_type));

        basic_type = new HeaderBasicType("double", 8, 64);

        types->push_back(basic_type);
        types->push_back(new HeaderTypedef("tFloat64", basic_type)); 

        // tFloat128 is not supported, because type is deprecated
        /*basic_type = new HeaderBasicType("tFloat128", 4, 128);
        types->push_back(basic_type);
        types->push_back(new HeaderTypedef("long double", basic_type));*/

        return types;
    }

    Header * HeaderImporter::getHeader()
    {
        return _header;
    }

    a_util::result::Result HeaderImporter::createNew()
    {
        if (_header_source == "" && _input_file.getLastElement().isEmpty())
        {
            return ERR_NOT_INITIALISED;
        }
        if (!_input_file.getLastElement().isEmpty())
        {
            if (a_util::filesystem::readTextFile(_input_file, _header_source) != a_util::filesystem::OK)
            {
                return (ERR_FAILED);
            }
        }

        _last_error = "";
        // Old header will not be destroyed since caller is responsible for the pointer.
        _header = new Header();
        _header->setName(_input_file.getLastElement());
        if (isFailed((BuildTypedefs())))
        {
            return ERR_FAILED;
        }
        if (isFailed((BuildConstants())))
        {
            return ERR_FAILED;
        }
        if (isFailed(buildEnums()))
        {
            return ERR_FAILED;
        }
        if (isFailed((buildStructs())))
        {
            return ERR_FAILED;
        }
        if (_last_error.length() > 0)
        {
            return ERR_FAILED;
        }
        return ERR_NOERROR;
    }

    a_util::result::Result HeaderImporter::DestroyHeader()
    {
        delete _header;
        return ERR_NOERROR;
    }

    a_util::result::Result HeaderImporter::BuildTypedefs()
    {
        if (_header == NULL)
        {
            return ERR_POINTER;
        }
        const char* pos = _header_source.c_str();
        while (*pos != '\0')
        {
            std::string next_word = HeaderParserHelper::getNextCodeElement(pos);
            if (next_word == "typedef")
            {
                std::string original_name;
                std::string new_name;
                std::string token;
                if (a_util::result::isOk(parseTypedef(pos, token, original_name, new_name)))
                {
                    const HeaderType* original_type = findKnownType(original_name);
                    if (original_type != NULL)
                    {
                        HeaderTypedef* new_type = new HeaderTypedef(new_name, original_type);
                        _header->addTypedef(new_type);
                    }
                    else
                    {
                        addErrorDescription(std::string("Unknown type name: ").append(original_name), pos);
                    }
                }
            }
        }
        return ERR_NOERROR;
    }

    a_util::result::Result HeaderImporter::BuildConstants()
    {
        if (_header == NULL)
        {
            return ERR_POINTER;
        }

        // Only constants with integers as values will be accepted, since they are needed for the size of arrays.
        // Buildup of a constant:
        // const Type Name = number;
        // #define NAME number
        const char* pos = _header_source.c_str();
        while (*pos != '\0')
        {
            std::string next_word = HeaderParserHelper::getNextCodeElement(pos);
            if (next_word == "const")
            {
                next_word = HeaderParserHelper::getNextCodeElement(pos);
                if (HeaderParserHelper::isValidName(next_word))
                {
                    std::string type_name = next_word;
                    if (type_name == "signed" || type_name == "unsigned")
                    {
                        next_word = HeaderParserHelper::getNextCodeElement(pos);
                        type_name.append(" ");
                        type_name.append(next_word);
                    }
                    // Look for the type in the list of known types.
                    const HeaderType* type = findKnownType(next_word);
                    next_word = HeaderParserHelper::getNextCodeElement(pos);
                    // Pointers will be ignored, so next element has to be the variable name
                    if (HeaderParserHelper::isValidName(next_word))
                    {
                        std::string name = next_word;
                        next_word = HeaderParserHelper::getNextCodeElement(pos);
                        if (next_word == "=")
                        {
                            next_word = HeaderParserHelper::getNextCodeElement(pos);
                            // Anything but integers will be ignored since constants are only only useful for the parser,
                            // if an array size refers to them.
                            if (a_util::strings::isInt64(next_word))
                            {
                                int64_t value = a_util::strings::toInt64(next_word);
                                next_word = HeaderParserHelper::getNextCodeElement(pos);
                                // No fancy values like 34+32 or 866+other_const will be accepted at the moment, so we expect a semicolon now.
                                if (next_word == ";")
                                {
                                    //Yeah! We found a constant with an int.
                                    HeaderConstant* constant = new HeaderConstant();
                                    constant->setName(name);
                                    constant->setType(type);
                                    constant->reset(value);
                                    _header->addConstant(constant);
                                }
                            }
                        }
                    }
                }
            }
            else if (next_word == "#")
            {
                next_word = HeaderParserHelper::getNextCodeElement(pos);
                if (next_word == "define")
                {
                    // Look for end of line (ignoring multiline defines)
                    const char* eol_pos = pos;
                    HeaderParserHelper::skipEOL(eol_pos);
                    next_word = HeaderParserHelper::getNextCodeElement(pos);
                    std::string const_name = next_word;
                    // Still same line?
                    if (pos <= eol_pos)
                    {
                        const char* tmp_pos = pos;

                        // a #define doesn't necessarily have a value. so maybe
                        // we're already in the next line and mistakenly analyze
                        // the next statement already. this most likely breaks the
                        // parsing process, because this "next_word" would be lost.
                        // so don't consume the next word until we're sure to have a 
                        // value
                        next_word = HeaderParserHelper::getNextCodeElement(tmp_pos);
                        if (tmp_pos <= eol_pos)
                        {
                            // everything ok, go on exploring this header
                            next_word = HeaderParserHelper::getNextCodeElement(pos);
                        }

                        // Still same line? Did we find an int?
                        if (pos <= eol_pos && a_util::strings::isInt64(next_word))
                        {
                            int64_t value = a_util::strings::toInt64(next_word);
                            // Check if the next word is not in the same line anymore or eof is reached.
                            // Allowed is only /*...*/ comments.
                            // We must not increase pos, since otherwise a "#define" or "const"
                            // from the next line could be skipped.
                            next_word = HeaderParserHelper::getNextElement(tmp_pos);
                            while (tmp_pos < eol_pos && next_word == "/*")
                            {
                                HeaderParserHelper::skipComment(tmp_pos, next_word);
                                next_word = HeaderParserHelper::getNextElement(tmp_pos);
                            }
                            if (tmp_pos > eol_pos || *tmp_pos == '\0')
                            {
                                const HeaderType* type = findKnownType("tUInt64");
                                if (_default_type != NULL)
                                {
                                    type = _default_type;
                                }
                                if (type != NULL)
                                {
                                    HeaderConstant* constant = new HeaderConstant();
                                    constant->setName(const_name);
                                    constant->setType(type);
                                    constant->reset(value);
                                    _header->addConstant(constant);
                                }
                            }
                        }
                    }
                }
            }
        }
        return ERR_NOERROR;
    }

    a_util::result::Result HeaderImporter::buildStructs()
    {
        // The following types of definitions will raise our parsing attention:
        // 1. struct original_name {..};
        // 2. typedef struct {..} typedef_name;
        // 3. typedef struct original_name {..} typedef_name;
        // #3 will result in a type entry and a typedef entry in the header, since both names refer to the same type
        // Not supported are unnamed structs, like the following:
        // 4. struct {..} name;
        // 5. struct {..};
        // 6. typedef struct {..};
        const char* pos = _header_source.c_str();
        // This pack information could be extracted as a configurable setting later
        std::vector<size_t> pack_stack;
        // Set default packing depending n compiler used
#if defined(_MSC_VER) || (defined(__GNUC__) && defined(__ADTF64))
        pack_stack.push_back(8);
#else
#if defined(__GNUC__) & defined(__ADTF32)
        pack_stack.push_back(4);
#else
    //unknown compiler / platform
        pack_stack.push_back(4);
#endif
#endif
        while (*pos != '\0')
        {
            char tmp = *pos;
            bool is_typedef = false;
            std::string next_word = HeaderParserHelper::getNextCodeElement(pos);
            // Packing identification
            if (next_word == "#")
            {
                next_word = HeaderParserHelper::getNextCodeElement(pos);
                if (next_word == "pragma")
                {
                    next_word = HeaderParserHelper::getNextCodeElement(pos);
                    if (next_word == "pack")
                    {
                        next_word = HeaderParserHelper::getNextCodeElement(pos);
                        if (next_word == "(")
                        {
                            std::string command;
                            next_word = HeaderParserHelper::getNextCodeElement(pos);
                            if (!a_util::strings::isInt64(next_word))
                            {
                                if (next_word == "push" || next_word == "pop")
                                {
                                    command = next_word;
                                    next_word = HeaderParserHelper::getNextCodeElement(pos);
                                    if (next_word == ",")
                                    {
                                        next_word = HeaderParserHelper::getNextCodeElement(pos);
                                    }
                                }
                                else
                                {
                                    addErrorDescription("Cannot parse pragma, only push and pop are supported.", pos);
                                }
                            }
                            if (command == "pop")
                            {
                                if (next_word == ")")
                                {
                                    if (pack_stack.size() > 1)
                                    {
                                        pack_stack.pop_back();
                                    }
                                    else
                                    {
                                        addErrorDescription("Too many pragma pop.", pos);
                                    }
                                }
                                else
                                {
                                    addErrorDescription("Pragma pop is missing a closing parenthesis.", pos);
                                }
                            }
                            else
                            {
                                if (a_util::strings::isInt64(next_word))
                                {
                                    if (command == "")
                                    {
                                        pack_stack[pack_stack.size() - 1] = a_util::strings::toInt64(next_word);
                                    }
                                    else if (command == "push")
                                    {
                                        pack_stack.push_back(a_util::strings::toInt64(next_word));
                                    }
                                    else {
                                        addErrorDescription(a_util::strings::format("Unknown pragma command %s.", command.c_str()), pos);
                                    }
                                }
                                else
                                {
                                    addErrorDescription("Unknown value found within pragma, expected a number.", pos);
                                }
                            }
                        }
                    }
                }
            }
            // Check for typedef of #2 or #3
            if (next_word == "typedef")
            {
                is_typedef = true;
                next_word = HeaderParserHelper::getNextCodeElement(pos);
            }
            // Check for #1 - #3
            if (next_word == "struct")
            {
                next_word = HeaderParserHelper::getNextCodeElement(pos);
                // Check for #1 or #3
                std::string original_name;
                std::string typedef_name;
                if (next_word != "{")
                {
                    original_name = next_word;
                    next_word = HeaderParserHelper::getNextCodeElement(pos);
                }
                if (next_word == "{")
                {
                    HeaderStructElementVec elements = extractStructElements(pos);
                    next_word = HeaderParserHelper::getNextCodeElement(pos);
                    if (next_word != ";")
                    {
                        if (!is_typedef)
                        {
                            addErrorDescription("Cannot parse struct.", pos);
                        }
                        else
                        {
                            //check for #2 or #3
                            if (HeaderParserHelper::isValidName(next_word))
                            {
                                typedef_name = next_word;
                                next_word = HeaderParserHelper::getNextCodeElement(pos);
                            }
                            else
                            {
                                addErrorDescription("The typedef struct is unreadable.", pos);
                            }
                        }
                    }
                    HeaderStruct* header_struct = new HeaderStruct();
                    if (original_name.size() != 0)
                    {
                        header_struct->setName(original_name);
                        if (is_typedef && typedef_name.size() != 0)
                        {
                            _header->addTypedef(new HeaderTypedef(typedef_name, header_struct));
                        }
                    }
                    else
                    {
                        header_struct->setName(typedef_name);
                    }
                    // Check for not #4 - #6
                    if (header_struct->getName().size() != 0 && !(is_typedef && typedef_name.size() == 0))
                    {
                        for (HeaderStructElementVec::const_iterator iter = elements.begin(); iter != elements.end(); iter++)
                        {
                            header_struct->addElement(*iter);
                        }
                        // Set current packing within struct if current packing is smaller than the native packing of the struct
                        if (header_struct->getPacking() > pack_stack.back())
                        {
                            header_struct->setPacking(pack_stack.back());
                        }
                        _header->addStruct(header_struct);
                    }
                    else
                    {
                        addErrorDescription("Cannot interpret this struct, no name given.", pos);
                        // Clean up allocated memory
                        delete header_struct;
                        for (HeaderStructElementVec::const_iterator iter = elements.begin(); iter != elements.end(); iter++)
                        {
                            delete *iter;
                        }
                    }
                }
                else
                {
                    addErrorDescription("Cannot interpret this struct.", pos);
                    return ERR_UNKNOWN_FORMAT;
                }
            }
            else
            {
                // No typedef for a struct it seems.
            }
        }
        return ERR_NOERROR;
    }

    a_util::result::Result HeaderImporter::buildEnums()
    {
        // The following types of definitions will raise our parsing attention:
        // 1. enum original_name {..};
        // 2. typedef enum {..} typedef_name;
        // 3. typedef enum original_name {..} typedef_name;
        // #3 will result in a type entry and a typedef entry in the header, since both names refer to the same type
        // Not supported are unnamed enums, like the following:
        // 4. enum {..} name;
        // 5. enum {..};
        // 6. typedef enum {..};
        const char* pos = _header_source.c_str();

        while (*pos != '\0')
        {
            char tmp = *pos;
            bool is_typedef = false;
            std::string next_word = HeaderParserHelper::getNextCodeElement(pos);

            // Check for typedef of #2 or #3
            if (next_word == "typedef")
            {
                is_typedef = true;
                next_word = HeaderParserHelper::getNextCodeElement(pos);
            }
            // Check for #1 - #3
            if (next_word == "enum")
            {
                next_word = HeaderParserHelper::getNextCodeElement(pos);
                // Check for #1 or #3
                std::string original_name;
                std::string typedef_name;
                if (next_word != "{")
                {
                    original_name = next_word;
                    next_word = HeaderParserHelper::getNextCodeElement(pos);
                }
                if (next_word == "{")
                {
                    HeaderEnumValueMap enum_values = extractEnumValues(pos);
                    next_word = HeaderParserHelper::getNextCodeElement(pos);
                    if (next_word != ";")
                    {
                        if (!is_typedef)
                        {
                            addErrorDescription("Cannot parse enum.", pos);
                        }
                        else
                        {
                            //check for #2 or #3
                            if (HeaderParserHelper::isValidName(next_word))
                            {
                                typedef_name = next_word;
                                next_word = HeaderParserHelper::getNextCodeElement(pos);
                            }
                            else
                            {
                                addErrorDescription("The typedef enum is unreadable.", pos);
                            }
                        }
                    }
                    HeaderEnum* header_enum = new HeaderEnum();
                    if (original_name.size() != 0)
                    {
                        header_enum->setName(original_name);
                        if (is_typedef && typedef_name.size() != 0)
                        {
                            _header->addTypedef(new HeaderTypedef(typedef_name, header_enum));
                        }
                    }
                    else
                    {
                        header_enum->setName(typedef_name);
                    }
                    // Check for not #4 - #6
                    if (header_enum->getName().size() != 0)
                    {
                        for (auto iter = enum_values.begin(); iter != enum_values.end(); iter++)
                        {
                            header_enum->addValue(iter->first, iter->second);
                        }
                        _header->addEnum(header_enum);
                    }
                    else
                    {
                        addErrorDescription("Cannot interpret this enum, no name given.", pos);
                        // Clean up allocated memory
                        delete header_enum;
                    }
                }
                else
                {
                    addErrorDescription("Cannot interpret this enum.", pos);
                    return ERR_UNKNOWN_FORMAT;
                }
            }
            else
            {
                // No typedef for a enum it seems.
            }
        }
        return ERR_NOERROR;
    }


    const std::string& HeaderImporter::getLastError() const
    {
        return _last_error;
    }

    const HeaderType* HeaderImporter::findKnownType(const std::string &name) const
    {
        const HeaderType* type = NULL;
        // Search for name in know types
        for (HeaderTypesVec::const_iterator iter = _types->begin(); iter != _types->end(); iter++)
        {
            if ((*iter)->getName() == name)
            {
                type = *iter;
                break;
            }
        }
        if (type == NULL)
        {
            // If not found in the known types, look in the already parsed typedefs for the type.
            const HeaderTypedefs typedefs = _header->getTypedefs();
            for (HeaderTypedefs::const_iterator iter = typedefs.begin(); iter != typedefs.end(); iter++)
            {
                if ((*iter)->getName() == name)
                {
                    type = *iter;
                    break;
                }
            }
        }
        if (type == NULL)
        {
            // If not found in the already parsed typedefs, look in the already parsed structs for the type.
            const HeaderStructs structs = _header->getStructs();
            for (HeaderStructs::const_iterator iter = structs.begin(); iter != structs.end(); iter++)
            {
                if ((*iter)->getName() == name)
                {
                    type = *iter;
                    break;
                }
            }
        }
        if (type == NULL)
        {
            // If not found in the already parsed typedefs or structs look in the already parsed enums for the type.
            const HeaderEnums enums = _header->getEnums();
            for (auto iter = enums.begin(); iter != enums.end(); iter++)
            {
                if ((*iter)->getName() == name)
                {
                    type = *iter;
                    break;
                }
            }
        }
        return type;
    }

    HeaderStructElementVec HeaderImporter::extractStructElements(const char* &pos)
    {
        // Copied over from the adtfcodec/cc_header.cpp implementation
        // modified to fit current requirements.
        std::string next_element;
        std::set<std::string> structs;
        HeaderStructElementVec elements;

        while (*pos != '\0')
        {
            next_element = HeaderParserHelper::getNextCodeElement(pos);
            if (next_element.compare("}") == 0)
            {
                break;
            }
            // Extract type
            std::string type_name = next_element;
            if (!HeaderParserHelper::isValidName(type_name))
            {
                addErrorDescription(std::string("Unexpected string ").append(type_name), pos);
            }
            else
            {
                if (type_name == "signed" || type_name == "unsigned")
                {
                    next_element = HeaderParserHelper::getNextCodeElement(pos);
                    if (HeaderParserHelper::isValidName(next_element))
                    {
                        type_name.append(" ");
                        type_name.append(next_element);
                    }
                    else
                    {
                        addErrorDescription(std::string("Unexpected string ").append(type_name), pos);
                        HeaderParserHelper::exitBlock(pos);
                        break;
                    }
                }
                const HeaderType* type = findKnownType(type_name);
                if (type != NULL)
                {
                    // Type successfully identified, try to extract member name
                    next_element = HeaderParserHelper::getNextCodeElement(pos);
                    // No pointer support yet
                    if (next_element == "*")
                    {
                        addErrorDescription("No pointers supported yet", pos);
                        HeaderParserHelper::exitBlock(pos);
                        break;
                    }
                    if (!HeaderParserHelper::isValidName(next_element))
                    {
                        addErrorDescription(std::string("Invalid member name ").append(next_element), pos);
                        HeaderParserHelper::exitBlock(pos);
                        break;
                    }
                    std::string element_name = next_element;
                    // try to extract array info
                    size_t len = 1;
                    next_element = HeaderParserHelper::getNextCodeElement(pos);
                    if (next_element == "[")
                    {
                        next_element = HeaderParserHelper::getNextCodeElement(pos);
                        if (!a_util::strings::isInt64(next_element))
                        {
                            // Is it a constant`?
                            if (HeaderParserHelper::isValidName(next_element))
                            {
                                bool found = false;
                                HeaderConstants constants = _header->getConstants();
                                for (HeaderConstants::const_iterator iter = constants.begin(); iter != constants.end(); iter++)
                                {
                                    if ((*iter)->getName() == next_element)
                                    {
                                        found = true;
                                        if ((*iter)->asInt64() > 0)
                                        {
                                            len = (*iter)->asInt64();
                                        }
                                        else
                                        {
                                            addErrorDescription("Array size type is not int", pos);
                                            HeaderParserHelper::exitBlock(pos);
                                            break;
                                        }
                                        break;
                                    }
                                }
                                if (!found)
                                {
                                    addErrorDescription("Array size value could not be found", pos);
                                    HeaderParserHelper::exitBlock(pos);
                                    break;
                                }
                                next_element = HeaderParserHelper::getNextCodeElement(pos);
                                if (next_element != "]")
                                {
                                    addErrorDescription(std::string("Unexpected string ").append(next_element), pos);
                                }
                                next_element = HeaderParserHelper::getNextCodeElement(pos);
                                if (next_element != ";")
                                {
                                    addErrorDescription(std::string("Unexpected string ").append(next_element), pos);
                                }
                            }
                            else
                            {
                                addErrorDescription(std::string("Invalid array size ").append(next_element), pos);
                                HeaderParserHelper::exitBlock(pos);
                                break;
                            }
                        }
                        else
                        {
                            len = a_util::strings::toInt32(next_element);
                            next_element = HeaderParserHelper::getNextCodeElement(pos);
                            if (next_element != "]")
                            {
                                addErrorDescription(std::string("Unexpected string ").append(next_element), pos);
                            }
                            next_element = HeaderParserHelper::getNextCodeElement(pos);
                            if (next_element != ";")
                            {
                                addErrorDescription(std::string("Unexpected string ").append(next_element), pos);
                            }
                        }
                    }
                    else if (next_element != ";")
                    {
                        addErrorDescription(std::string("Missing ; at end of struct member"), pos);
                        HeaderParserHelper::exitBlock(pos);
                        break;
                    }
                    HeaderStructElement* element = new HeaderStructElement();
                    element->setType(type);
                    element->setName(element_name);
                    element->setArraySize(len);
                    // No pointer element recognition implemented yet
                    element->setIsPointer(false);
                    // No static element recognition implemented yet
                    element->setIsStatic(false);
                    // No const element recognition implemented yet
                    element->setIsConst(false);
                    elements.push_back(element);
                }
                else
                {
                    addErrorDescription(std::string("Unknown type name: ").append(type_name), pos);
                    do 
                    {
                        next_element = HeaderParserHelper::getNextCodeElement(pos);
                    } while (next_element != ";" && next_element != "}");
                }
            }
        }
        return elements;
    }

    a_util::result::Result HeaderImporter::addErrorDescription(const std::string &description, const char* pos)
    {
        const char* search_pos = _header_source.c_str();
        uint64_t line_number = 1;
        while (*search_pos != '\0')
        {
            if (search_pos >= pos)
            {
                break;
            }
            else if (*search_pos == '\n')
            {
                line_number++;
            }
            search_pos++;
        }
        if (_last_error.size() != 0)
        {
            _last_error.append("\n");
        }
        _last_error.append(a_util::strings::format("Error: \"%s\" at Line %d", description.c_str(), line_number));
        return ERR_NOERROR;
    }


    a_util::result::Result HeaderImporter::parseTypedef(const char* &pos, std::string &token, std::string &original_type_name, std::string& new_type_name)
    {
        // We found a possible candidate for a new typedef
        // We can have the following cases:
        // 1 typedef token{...
        // 2 typedef token {...
        // 3 typedef token Bla{...
        // 4 typedef token Bla {...
        // 5 typedef token Bla Blubb;
        // 6 typedef Bla Blubb;
        // We are looking only for the last two.
        const char* working_pos = pos;
        std::string local_token;
        // Check if first word if its a token
        std::string next_word = HeaderParserHelper::getNextCodeElement(working_pos);
        for (uint64_t idx = 0; idx < AMOUNT_OF_TOKENS; idx++)
        {
            if (g_tokens[idx] == next_word)
            {
                local_token = next_word;
                next_word = HeaderParserHelper::getNextCodeElement(working_pos);
                break;
            }
        }
        // Now there have to be two valid names and one semicolon. Lets check it out!
        if (HeaderParserHelper::isValidName(next_word))
        {
            std::string local_original_type_name = next_word;
            next_word = HeaderParserHelper::getNextCodeElement(working_pos);
            if (HeaderParserHelper::isValidName(next_word))
            {
                std::string local_new_type_name = next_word;
                next_word = HeaderParserHelper::getNextCodeElement(working_pos);
                if (next_word.compare(";") == 0)
                {
                    // Yes!!! we found a valid typedef, case #5 or #6
                    new_type_name = local_new_type_name;
                    original_type_name = local_original_type_name;
                    token = local_token;
                    pos = working_pos;
                    return ERR_NOERROR;
                }
            }
        }
        return ERR_UNKNOWN_FORMAT;
    }

    HeaderEnumValueMap HeaderImporter::extractEnumValues(const char* &pos)
    {
        std::string next_element;
        HeaderEnumValueMap values;
        int32_t next_idx = 0;

        while (*pos != '\0')
        {
            next_element = HeaderParserHelper::getNextCodeElement(pos);
            if (next_element.compare("}") == 0)
            {
                break;
            }
            // Extract type
            std::string value_name = next_element;
            if (!HeaderParserHelper::isValidName(value_name))
            {
                addErrorDescription(std::string("Unexpected string ").append(value_name), pos);
            }
            else
            {
                next_element = HeaderParserHelper::getNextCodeElement(pos);
                if (next_element == "," || next_element == "}")
                {
                    values[next_idx++] = value_name;
                }
                else if (next_element == "=")
                {
                    std::string index = HeaderParserHelper::getNextCodeElement(pos);
                    if (index == "-")
                    {
                        index += HeaderParserHelper::getNextCodeElement(pos);
                    }
                    int32_t idx = a_util::strings::toInt32(index);
                    if (values.count(idx) > 0)
                    {
                        addErrorDescription(std::string("Duplicate value in enum ").append(index), pos);
                    }
                    else
                    {
                        values[idx] = value_name;
                        next_idx = (std::max)(++next_idx, ++idx); //extra parantheses since msvc defines a max macro somewhere...
                    }

                    next_element = HeaderParserHelper::getNextCodeElement(pos);
                    if (next_element != "," && next_element != "}")
                    {
                        addErrorDescription(std::string("Unexpected token ").append(next_element), pos);
                    }
                }
                else
                {
                    addErrorDescription(std::string("Unexpected token ").append(next_element), pos);
                }

                if (next_element == "}")
                {
                    break;
                }
            }
        }
        return values;
    }

} //namespace ddl