//  Created by Sergey on 12.09.14.
//  Copyright (c) 2014 Sergey. All rights reserved.
//

#include <iostream>
#include <chrono>
#include <map>
#include <string>

template <class T, class ValueType>
class StrongType
{
public:
    inline explicit operator ValueType() const { return _value;}
    inline bool operator == (const StrongType &other) const
    {
        return _value == other._value;
    }
    inline bool operator != (const StrongType &other) const
    {
        return _value != other._value;
    }
    inline bool operator < (const StrongType &other) const
    {
        return _value < other._value;;
    }
    inline bool operator > (const StrongType &other) const
    {
        return _value > other._value;
    }
    inline bool operator <= (const StrongType &other) const
    {
        return _value <= other._value;
    }
    inline bool operator >= (const StrongType &other) const
    {
        return _value >= other._value;
    }
    
protected:
    explicit StrongType(ValueType value):_value(value) {}
    
private:
    ValueType _value;
};

template <class T, class ValueType = int>
class StringCache
{
public:
    static_assert(std::is_integral<ValueType>::value, "not integral type");
    
    class Type: public StrongType<T,ValueType>
    {
        friend class StringCache;
    private:
        explicit Type(ValueType value):StrongType<T,ValueType>(value){}
    };
    
    static Type get(const std::string &value)
    {
        return Type(_values.insert(std::make_pair(value, _values.size() + 1)).first->second);
    }
    
    static Type find(const std::string &value)
    {
        std::map<std::string,int>::const_iterator it =_values.find(value);
        if(it == _values.end())
            return Type(0);
        else
            return Type(it->second);
    }
    
    static const std::string& to_string(const Type &type)
    {
        static const std::string empty;
        if(static_cast<ValueType>(type)>=_values.size())
            return empty;
        for(const auto &it:_values)
            if(it.second == static_cast<ValueType>(type))
                return it.first;
        return empty;
    }
    
private:
    static std::map<std::string,int> _values;
};

template <class T, class ValueType>
std::map<std::string,int> StringCache<T,ValueType>::_values;

class ArmorType:public StringCache<ArmorType>{};
class WeaponType:public StringCache<WeaponType>{};

typedef int ArmorDescription;

std::string armorId0("armor0");
std::string armorId1("armor1");
std::string armorId2("armor2");
std::string armorId3("armor3");
std::string armorId4("armor4");
std::string armorId5("armor5");
std::string armorId6("armor6");
std::string armorId7("armor7");
std::string armorId8("armor8");
std::string armorId9("armor9");

int test1(const std::map<ArmorType::Type,ArmorDescription> &armor)
{
    static const ArmorType::Type value(ArmorType::get(armorId1));
    return armor.find(value)->second;
}

int test2(const std::map<std::string,ArmorDescription> &armor)
{
    static const std::string value(armorId1);
    return armor.find(value)->second;
}

int main(int argc, const char * argv[])
{
    std::map<ArmorType::Type,ArmorDescription> armorHash;
    std::map<std::string,ArmorDescription> armorString;
    
    armorHash.insert(std::make_pair(ArmorType::get(armorId0), ArmorDescription(0)));
    armorHash.insert(std::make_pair(ArmorType::get(armorId1), ArmorDescription(1)));
    armorHash.insert(std::make_pair(ArmorType::get(armorId2), ArmorDescription(2)));
    armorHash.insert(std::make_pair(ArmorType::get(armorId3), ArmorDescription(3)));
    armorHash.insert(std::make_pair(ArmorType::get(armorId4), ArmorDescription(4)));
    armorHash.insert(std::make_pair(ArmorType::get(armorId5), ArmorDescription(5)));
    armorHash.insert(std::make_pair(ArmorType::get(armorId6), ArmorDescription(6)));
    armorHash.insert(std::make_pair(ArmorType::get(armorId7), ArmorDescription(7)));
    armorHash.insert(std::make_pair(ArmorType::get(armorId8), ArmorDescription(8)));
    armorHash.insert(std::make_pair(ArmorType::get(armorId9), ArmorDescription(9)));
    
    armorString.insert(std::make_pair(armorId0, ArmorDescription(0)));
    armorString.insert(std::make_pair(armorId1, ArmorDescription(1)));
    armorString.insert(std::make_pair(armorId2, ArmorDescription(2)));
    armorString.insert(std::make_pair(armorId3, ArmorDescription(3)));
    armorString.insert(std::make_pair(armorId4, ArmorDescription(4)));
    armorString.insert(std::make_pair(armorId5, ArmorDescription(5)));
    armorString.insert(std::make_pair(armorId6, ArmorDescription(6)));
    armorString.insert(std::make_pair(armorId7, ArmorDescription(7)));
    armorString.insert(std::make_pair(armorId8, ArmorDescription(8)));
    armorString.insert(std::make_pair(armorId9, ArmorDescription(9)));
    
    auto armor1It = armorHash.find(ArmorType::find(armorId1));
    auto armor2It = armorHash.find(ArmorType::find(armorId2));
    auto armor10It = armorHash.find(ArmorType::find("armor10"));
    
    std::cout << ArmorType::to_string(armor1It->first) << ":" << static_cast<int>(armor1It->first) << std::endl;
    std::cout << ArmorType::to_string(armor2It->first) << ":" << static_cast<int>(armor2It->first) << std::endl;
    std::cout << ArmorType::to_string(armor10It->first) << ":" << static_cast<int>(armor10It->first) << std::endl;
    
    int ret1 = 0;
    int ret2 = 0;
    
    std::chrono::high_resolution_clock::time_point t = std::chrono::high_resolution_clock::now();
    for(int i=0;i<1000000;++i)
        ret1 += test1(armorHash);
    auto duration = std::chrono::high_resolution_clock::now() - t;
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() << std::endl;
    
    t = std::chrono::high_resolution_clock::now();
    for(int i=0;i<1000000;++i)
        ret2 += test2(armorString);
    duration = std::chrono::high_resolution_clock::now() - t;
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() << std::endl;
    
    return 0;
} 