#ifndef AntTweakBar_hpp__
#define AntTweakBar_hpp__

#include <string>
#include <functional>
#include <vector>

#include <AntTweakBar.h>

namespace atwb
{

    class Bar;

    template<typename T>
    void TW_CALL setVariableTwCallback(const void * data, void * userData);

    template<typename T>
    void TW_CALL getVariableTwCallback(void * data, void * userData);

    void TW_CALL buttonTwCallback(void * userData);

    template<typename ET>
    class Enumeration
    {
    public:
        static const TwEnumVal s_table[];
        static const char * s_name;
        static const int s_count;
    };

    class BarItem
    {
    public:
        BarItem(const char * name, Bar * owner);
        virtual ~BarItem() {};

        void define(const char * definition);

        const char * getName() const { return m_name.c_str(); }

    protected:
        std::string m_name;
        std::string m_fullName;
        Bar * m_owner;
    };

    template<typename T>
    class ClientType
    {
    public:
        typedef T Type;

        static TwType getTwType();
        static void fromTwData(const void * data, T & val);
        static void toTwData(void * data, const T & val);
    };

    template<typename ET>
    class ClientType<Enumeration<ET>>
    {
    public:
        typedef ET Type;

        static TwType getTwType();
        static void fromTwData(const void * data, ET & val);
        static void toTwData(void * data, const ET & val);
    };

    template<typename T>
    class Variable : public BarItem
    {
    public:
        typedef std::function<void(const typename ClientType<T>::Type &)> Setter;
        typedef std::function<const typename ClientType<T>::Type (void)> Getter;

        Variable(const char * name, Bar * owner, Setter setter, Getter getter);
        ~Variable();

        template<typename NT>
        Variable<T> * min(const NT & min);

        template<typename NT>
        Variable<T> * max(const NT & max);

        template<typename NT>
        Variable<T> * step(const NT & step);

        Variable<T> * readonly(bool readonly);
        Variable<T> * group(const char * groupName);
        Variable<T> * label(const char * label);

    private:
        void set(const void * data);
        void get(void * data);

        void fromTwData(const void * data, T & val);
        void toTwData(void * data, const T & val);

        Setter m_setter;
        Getter m_getter;

        friend void TW_CALL setVariableTwCallback<T>(const void * data, void * userData);
        friend void TW_CALL getVariableTwCallback<T>(void * data, void * userData);
    };

    class Button : public BarItem
    {
    public:
        typedef std::function<void(void)> Callback;

        Button(const char * name, Bar * owner, Callback callback);
        
        Button * group(const char * groupName);

    private:
        void pressed();

        Callback m_callback;

        friend void TW_CALL buttonTwCallback(void * userData);
    };

    class Separator : public BarItem
    {
    public:
        Separator(const char * name, Bar * owner);
    };

    class Bar
    {
    public:
        Bar(const char * name);
        ~Bar();

        const std::string & getName() const;

        void define(const char * definition);

        template<typename T>
        Variable<T> * addVariable(
            const char * name, 
            typename Variable<T>::Setter setter, 
            typename Variable<T>::Getter getter,
            const char * definition = "",
            int index = 0);

        Button * addButton(
            const char * name,
            Button::Callback callback,
            const char * definition);

        Separator * addSeparator(
            const char * name,
            const char * definition = "");

        void removeItem(const char * name, int index = 0);
        void removeAllItems();

        Bar * visible(bool visible);
        Bar * iconified(bool inconified);
        Bar * size(int sx, int sy);
        Bar * position(int x, int y);
        Bar * color(unsigned char r, unsigned char g, unsigned char b);

    private:
        typedef std::vector<BarItem*> BarItemVec;

        TwBar * m_twBar;
        std::string m_name;
        BarItemVec m_items;
    };

}

#include "AntTweakBar.inl"

#endif // AntTweakBar_hpp__
