#pragma once

#include <cstdlib>

#include <util/stream/file.h>
#include <util/generic/vector.h>
#include <util/generic/hash.h>
#include <util/generic/set.h>
#include <util/generic/string.h>
#include <util/generic/yexception.h>
#include <util/string/cast.h>
#include <util/system/yassert.h>

#define VERIFY_AND_THROW(val, msg) {\
    if (Y_UNLIKELY( !(val) )) {\
        Cerr << msg;\
        exit(EXIT_FAILURE);\
    }\
}

namespace NRtyInfraTests {

    typedef std::pair<TString, TString> TCommand;
    typedef TString TAttrName;
    typedef TString TAttrValue;
    typedef THashMap<TAttrName, TAttrValue> TAttrs;

    class TScriptData {
    public:
        typedef TVector<TCommand> TMain;
        typedef THashMap<TString, TAttrs> TObjects;
    public:
        TScriptData();
        void ReadScript(const char* scriptPath);

        const TAttrs& GetObject(const TString& name) const;
        const TString& GetValue(const TAttrs& currentObject, const TString& attrName) const;
        const TAttrs& GetObjectByAttr(const TAttrs& currentObject, const TString& attrName) const;

        const TMain& GetMainSequence() const;
        const TObjects& GetObjects() const;
    private:
        void TokenizeLine(TString& line, TVector<TString>& tokens);
        void ProcessMain(TIFStream& fileInput, TString& currentLine, size_t& counter);
        void ProcessObject(TIFStream& fileInput, TString& currentLine, size_t& counter);
        TAttrs& ProcessParent(const TString& parentName, const TString& objectName);
    private:
        TMain MainSequence;
        TObjects Objects;
        TSet<TString> AllowedTypes;

        static const TString SPACE;
        static const TString MAIN;
        static const TString INCLUDE;
    };

    template<typename TReturnType> inline TReturnType GetValue(const TAttrs& object, const TString& attrName, const TReturnType& defaultValue) {
        TAttrs::const_iterator attrPtr = object.find(attrName);
        if (object.end() != attrPtr) {
            return FromString<TReturnType>(attrPtr->second);
        }
        return defaultValue;
    };

    template<typename TReturnType> inline TReturnType GetValue(const TAttrs& object, const TString& attrName) {
        TAttrs::const_iterator attrPtr = object.find(attrName);
        VERIFY_AND_THROW(object.end() != attrPtr, attrName << " is not set");
        return FromString<TReturnType>(attrPtr->second);
    };

    inline TString GetValue(const TAttrs& object, const TString& attrName, const TString& defaultValue) {
        return GetValue<TString>(object, attrName, defaultValue);
    };

    inline TString GetValue(const TAttrs& object, const TString& attrName) {
        return GetValue<TString>(object, attrName);
    };
}
