#include <sstream>

namespace atwb
{
    
    template<typename T>
    void TW_CALL setVariableTwCallback(const void * data, void * userData)
    {
        Variable<T> * variable = static_cast<Variable<T>*>(userData);
        variable->set(data);
    }

    template<typename T>
    void TW_CALL getVariableTwCallback(void * data, void * userData)
    {
        Variable<T> * variable = static_cast<Variable<T>*>(userData);
        variable->get(data);
    }

    template<typename T>
    Variable<T>::Variable(const char * name, Bar * owner, Setter setter, Getter getter)
        :BarItem(name, owner),
         m_setter(setter),
         m_getter(getter)
    {

    }

    template<typename T>
    Variable<T>::~Variable()
    {

    }

    template<typename ET>
    void ClientType<Enumeration<ET>>::fromTwData(const void * data, ET & val)
    {
        val = *(ET*)data;
    }
    
    template<typename ET>
    void ClientType<Enumeration<ET>>::toTwData(void * data, const ET & val)
    {
        *(ET*)data = val;
    }

    template<typename ET>
    TwType ClientType<Enumeration<ET>>::getTwType()
    {
        static TwType s_type = TwType(0);
    
        if (s_type == 0)
        {
            s_type = TwDefineEnum(Enumeration<ET>::s_name, Enumeration<ET>::s_table, Enumeration<ET>::s_count);
        }
    
        return s_type;
    }

    template<typename T>
    void Variable<T>::set(const void * data)
    {
        typename ClientType<T>::Type val;
        ClientType<T>::fromTwData(data, val);
        m_setter(val);
    }

    template<typename T>
    void Variable<T>::get(void * data)
    {
        ClientType<T>::toTwData(data, m_getter());
    }

    template<typename T>
    template<typename NT>
    Variable<T> * Variable<T>::min(const NT & min)
    {
        std::stringstream ss;
        ss << "min=" << min;
        define(ss.str().c_str());

        return this;
    }

    template<typename T>
    template<typename NT>
    Variable<T> * Variable<T>::max(const NT & max)
    {
        std::stringstream ss;
        ss << "max=" << max;
        define(ss.str().c_str());

        return this;
    }

    template<typename T>
    template<typename NT>
    Variable<T> * Variable<T>::step(const NT & step)
    {
        std::stringstream ss;
        ss << "step=" << step;
        define(ss.str().c_str());

        return this;
    }

    template<typename T>
    Variable<T> * Variable<T>::readonly(bool readonly)
    {
        if (readonly)
        {
            define("readonly=true");
        }
        else
        {
            define("readonly=false");
       }

       return this;
    }

    template<typename T>
    Variable<T> * Variable<T>::group(const char * groupName)
    {
        std::stringstream ss;
        ss << "group='" << groupName << "'";
        define(ss.str().c_str());

        return this;
    }

    template<typename T>
    Variable<T> * Variable<T>::label(const char * label)
    {
        std::stringstream ss;
        ss << "label='" << label << "'";
        define(ss.str().c_str());

        return this;
    }

    template<typename T>
    Variable<T> * Bar::addVariable(
        const char * name, 
        typename Variable<T>::Setter setter, 
        typename Variable<T>::Getter getter, 
        const char * definition,
        int index)
    {
        std::stringstream indexedNameStream;
        indexedNameStream << name << "_" << index;

        std::string indexedName = indexedNameStream.str();

        Variable<T> * variable = new Variable<T>(indexedName.c_str(), this, setter, getter);
        TwAddVarCB(m_twBar, indexedName.c_str(), ClientType<T>::getTwType(), 
            setVariableTwCallback<T>, getVariableTwCallback<T>, (void *)variable, definition);
        m_items.push_back(variable);

        if (setter == nullptr)
        {
            variable->readonly(true);
        }

        variable->label(name);

        return variable;
    }

    // other


}