#pragma once

#ifndef HTARCS_CFG_C39FB3B1_1C8D_4e4b_959A_216F1683A9A8
#define HTARCS_CFG_C39FB3B1_1C8D_4e4b_959A_216F1683A9A8

#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <unordered_map>

#include <util/generic/noncopyable.h>

namespace NWebmaster {

template <typename t_htarcs_hldr_cfg_data, typename t_htarc_cfg_data, typename t_walrus_cfg_data>
class t_htarc_cfg;

template <typename t_htarcs_hldr_cfg_data, typename t_htarc_cfg_data, typename t_walrus_cfg_data>
class t_walrus_cfg;

template <typename t_htarcs_hldr_cfg_data, typename t_htarc_cfg_data, typename t_walrus_cfg_data>
class t_htarcs_hldr_cfg;

//-----------------------------------------------------------------------------------
template <typename t_htarcs_hldr_cfg_data, typename t_htarc_cfg_data, typename t_walrus_cfg_data>
class t_htarc_cfg {
    friend class t_htarcs_hldr_cfg<t_htarcs_hldr_cfg_data, t_htarc_cfg_data, t_walrus_cfg_data>;

public:
    typedef t_walrus_cfg<t_htarcs_hldr_cfg_data, t_htarc_cfg_data, t_walrus_cfg_data> t_walrus;
    typedef std::vector<t_walrus *> t_walrus_arr;

    const std::string &name() const                             {
        return _name;
    }
    const t_walrus_arr &walruses() const                        {
        return _walruses;
    }
    operator std::string() const                                {
        return name();
    }

    const t_htarc_cfg_data &data() const {
        return d;
    }
    t_htarc_cfg_data &data()             {
        return d;
    }

private:
    t_htarc_cfg() : d(*this) {}
    std::string _name;
    t_walrus_arr _walruses;
    t_htarc_cfg_data d;
};

//-----------------------------------------------------------------------------------
template <typename t_htarcs_hldr_cfg_data, typename t_htarc_cfg_data, typename t_walrus_cfg_data>
class t_walrus_cfg {
    friend class t_htarcs_hldr_cfg<t_htarcs_hldr_cfg_data, t_htarc_cfg_data, t_walrus_cfg_data>;

public:
    typedef t_htarc_cfg<t_htarcs_hldr_cfg_data, t_htarc_cfg_data, t_walrus_cfg_data> t_htarc;

    const std::string &name() const                             {
        return _name;
    }
    const t_htarc &htarc() const                                {
        return *_htarc;
    }
    t_htarc &htarc()                                            {
        return *_htarc;
    }
    operator std::string() const                                {
        return name();
    }

    const t_walrus_cfg_data &data() const {
        return d;
    }
    t_walrus_cfg_data &data()             {
        return d;
    }

private:
    t_walrus_cfg(t_htarc *const _htarc) : _htarc(_htarc), d(*this) {}
    std::string _name;
    t_htarc *const _htarc;
    t_walrus_cfg_data d;
};

//-----------------------------------------------------------------------------------
template <typename t_htarcs_hldr_cfg_data, typename t_htarc_cfg_data, typename t_walrus_cfg_data>
class t_htarcs_hldr_cfg : public TNonCopyable {
public:
    typedef t_htarc_cfg<t_htarcs_hldr_cfg_data, t_htarc_cfg_data, t_walrus_cfg_data> t_htarc;
    typedef t_walrus_cfg<t_htarcs_hldr_cfg_data, t_htarc_cfg_data, t_walrus_cfg_data> t_walrus;
    typedef std::vector<t_htarc *> t_htarc_arr;
    typedef std::vector<t_walrus *> t_walrus_arr;

    static void from_txt_file(t_htarcs_hldr_cfg &hldr, const std::string &fname);

    t_htarcs_hldr_cfg() : d(*this) {}
    ~t_htarcs_hldr_cfg() {
        clear();
    }

    t_htarc  &htarc(const std::string &str)                {
        return *(htrc_map.at(str));
    }
    t_walrus &walrus(const std::string &str)               {
        return *(wlrs_map.at(str));
    }
    const t_htarc &htarc(const std::string &str) const     {
        return *(htrc_map.at(str));
    }
    const t_walrus &walrus(const std::string &str) const   {
        return *(wlrs_map.at(str));
    }

    t_htarc  *htarc_ptr(const std::string &str);
    t_walrus  *walrus_ptr(const std::string &str);
    const t_htarc  *htarc_ptr(const std::string &str) const;
    const t_walrus  *walrus_ptr(const std::string &str) const;

    const t_htarc_arr &htarcs() const {
        return htrc_arr;
    }
    const t_walrus_arr &walruses() const {
        return wlrs_arr;
    }
    t_htarc_arr &htarcs() {
        return htrc_arr;
    }
    t_walrus_arr &walruses() {
        return wlrs_arr;
    }

    const t_htarcs_hldr_cfg_data &data() const {
        return d;
    }
    t_htarcs_hldr_cfg_data &data()             {
        return d;
    }

private:
    typedef std::unordered_map<std::string, t_htarc *> t_htrc_map;
    typedef std::unordered_map<std::string, t_walrus *> t_wlrs_map;

    void clear();

    t_htarcs_hldr_cfg_data d;

    t_htrc_map htrc_map;
    t_wlrs_map wlrs_map;
    t_htarc_arr htrc_arr;
    t_walrus_arr wlrs_arr;
};

template <typename t_htarcs_hldr_cfg_data, typename t_htarc_cfg_data, typename t_walrus_cfg_data>
void t_htarcs_hldr_cfg<t_htarcs_hldr_cfg_data, t_htarc_cfg_data, t_walrus_cfg_data>::from_txt_file(t_htarcs_hldr_cfg &hldr, const std::string &fname) {
    using namespace std;

    ifstream fin;
    fin.open(fname.c_str(), ifstream::in);
    if (fin.fail()) {
        throw runtime_error("Cannot open input file for loading htarcs_hldr");
    }

    t_htarcs_hldr_cfg res;

    while (fin.good()) {
        string htarc_string;
        getline(fin, htarc_string);
        stringstream ss(htarc_string);
        if (ss.good()) {
            auto_ptr<t_htarc> htrc(new t_htarc());
            ss >> htrc->_name;
            if (htrc->_name.empty()) {
                continue;
            }

            while (ss.good()) {
                auto_ptr<t_walrus> wlrs(new t_walrus(htrc.get()));
                ss >> wlrs->_name;
                if (wlrs->_name.empty()) {
                    continue;
                }

                htrc->_walruses.push_back(wlrs.get());
                res.wlrs_arr.push_back(wlrs.get());
                t_walrus *p = wlrs.release();
                res.wlrs_map.insert(typename t_wlrs_map::value_type(p->name(), p));
            }

            res.htrc_arr.push_back(htrc.get());
            t_htarc *p = htrc.release();
            res.htrc_map.insert(typename t_htrc_map::value_type(p->name(), p));
        }
    }

    hldr.clear();
    swap(res.htrc_map, hldr.htrc_map);
    swap(res.wlrs_map, hldr.wlrs_map);
    swap(res.htrc_arr, hldr.htrc_arr);
    swap(res.wlrs_arr, hldr.wlrs_arr);
}

template <typename t_htarcs_hldr_cfg_data, typename t_htarc_cfg_data, typename t_walrus_cfg_data>
void t_htarcs_hldr_cfg<t_htarcs_hldr_cfg_data, t_htarc_cfg_data, t_walrus_cfg_data>::clear() {
    for (typename t_htarc_arr::value_type htrc : htrc_arr) {
        delete htrc;
    }
    htrc_arr.clear();
    htrc_map.clear();

    for (typename t_walrus_arr::value_type wlrs : wlrs_arr) {
        delete wlrs;
    }
    wlrs_arr.clear();
    wlrs_map.clear();
}

template <typename t_htarcs_hldr_cfg_data, typename t_htarc_cfg_data, typename t_walrus_cfg_data>
typename t_htarcs_hldr_cfg<t_htarcs_hldr_cfg_data, t_htarc_cfg_data, t_walrus_cfg_data>::t_htarc *
t_htarcs_hldr_cfg<t_htarcs_hldr_cfg_data, t_htarc_cfg_data, t_walrus_cfg_data>::htarc_ptr(const std::string &str) {
    typename t_htrc_map::iterator iter = htrc_map.find(str);
    if (htrc_map.end() == iter) {
        return 0;
    }
    return iter->second;
}

template <typename t_htarcs_hldr_cfg_data, typename t_htarc_cfg_data, typename t_walrus_cfg_data>
typename t_htarcs_hldr_cfg<t_htarcs_hldr_cfg_data, t_htarc_cfg_data, t_walrus_cfg_data>::t_walrus *
t_htarcs_hldr_cfg<t_htarcs_hldr_cfg_data, t_htarc_cfg_data, t_walrus_cfg_data>::walrus_ptr(const std::string &str) {
    typename t_wlrs_map::iterator iter = wlrs_map.find(str);
    if (wlrs_map.end() == iter) {
        return 0;
    }
    return iter->second;
}

template <typename t_htarcs_hldr_cfg_data, typename t_htarc_cfg_data, typename t_walrus_cfg_data>
const typename t_htarcs_hldr_cfg<t_htarcs_hldr_cfg_data, t_htarc_cfg_data, t_walrus_cfg_data>::t_htarc *
t_htarcs_hldr_cfg<t_htarcs_hldr_cfg_data, t_htarc_cfg_data, t_walrus_cfg_data>::htarc_ptr(const std::string &str) const {
    typename t_htrc_map::iterator iter = htrc_map.find(str);
    if (htrc_map.end() == iter) {
        return 0;
    }
    return iter->second;
}

template <typename t_htarcs_hldr_cfg_data, typename t_htarc_cfg_data, typename t_walrus_cfg_data>
const typename t_htarcs_hldr_cfg<t_htarcs_hldr_cfg_data, t_htarc_cfg_data, t_walrus_cfg_data>::t_walrus *
t_htarcs_hldr_cfg<t_htarcs_hldr_cfg_data, t_htarc_cfg_data, t_walrus_cfg_data>::walrus_ptr(const std::string &str) const {
    typename t_wlrs_map::iterator iter = wlrs_map.find(str);
    if (wlrs_map.end() == iter) {
        return 0;
    }
    return iter->second;
}


}

#endif //HTARCS_CFG_C39FB3B1_1C8D_4e4b_959A_216F1683A9A8
