<commit_before>//
//  ofxOscPublisher.h
//
//  Created by ISHII 2bit on 2015/05/11.
//
//

#pragma once

#include "ofMain.h"
#include "ofxOsc.h"

#include "details/ofxpubsubosc_settings.h"
#include "details/ofxpubsubosc_type_utils.h"

#include <initializer_list>

namespace ofx {
    using namespace ofxpubsubosc;
    
    namespace {
        template <typename T>
        struct remove_reference {
            typedef T type;
        };
        template <typename T>
        struct remove_reference<T &> {
            typedef T type;
        };
#define remove_ref(T) typename ofx::remove_reference<T>::type
        
        template <typename T> struct is_integral { static const bool value = false; };
#define define_is_integral(T) template <> struct is_integral<T> { static const bool value = true; };
        define_is_integral(bool);
        define_is_integral(short);
        define_is_integral(unsigned char);
        define_is_integral(char);
        define_is_integral(unsigned short);
        define_is_integral(int);
        define_is_integral(unsigned int);
        define_is_integral(long);
        define_is_integral(unsigned long);
        define_is_integral(long long);
        define_is_integral(unsigned long long);
#undef define_is_integral
        
#define type_ref(T) typename add_reference_if_non_arithmetic<T>::type

        template <bool b>
        struct enable;
        
        template <>
        struct enable<true> { typedef void type; };
        
        template <typename T>
        struct is_integral_and_lt_64bit {
            static const bool value = is_integral<T>::value && (sizeof(T) < 8);
        };
        
        template <typename T>
        struct is_integral_and_geq_64bit {
            static const bool value = is_integral<T>::value && (8 <= sizeof(T));
        };
    };
    
    class OscPublisherManager {
    private:
        class OscPublisher;
        
        struct SetImplementation {
        protected:
            template <typename T>
            inline typename enable<is_integral_and_lt_64bit<T>::value>::type set(ofxOscMessage &m, T v) const { m.addIntArg(v); }
            
            template <typename T>
            inline typename enable<is_integral_and_geq_64bit<T>::value>::type set(ofxOscMessage &m, T v) const { m.addInt64Arg(v); }
            
#define define_set_float(type) inline void set(ofxOscMessage &m, type v) const { m.addFloatArg(v); }
            define_set_float(float);
            define_set_float(double);
#undef define_set_float
            inline void set(ofxOscMessage &m, const std::string &v) const { m.addStringArg(v); }
#if ENABLE_OF_BUFFER
            inline void set(ofxOscMessage &m, const ofBuffer &v) const { m.addBlobArg(v); };
#endif
            template <typename PixType>
            inline void set(ofxOscMessage &m, const ofColor_<PixType> &v) const {  setVec<4>(m, v); }
            inline void set(ofxOscMessage &m, const ofVec2f &v) const { setVec<2>(m, v); }
            inline void set(ofxOscMessage &m, const ofVec3f &v) const { setVec<3>(m, v); }
            inline void set(ofxOscMessage &m, const ofVec4f &v) const { setVec<4>(m, v); }
            inline void set(ofxOscMessage &m, const ofQuaternion &v) const { setVec<4>(m, v); }
            
            template <size_t n, typename T>
            inline void setVec(ofxOscMessage &m, const T &v) const {
                for(int i = 0; i < n; i++) { set(m, v[i]); }
            }
            
            inline void set(ofxOscMessage &m, const ofMatrix3x3 &v) const {
                set(m, v.a);
                set(m, v.b);
                set(m, v.c);
                set(m, v.d);
                set(m, v.e);
                set(m, v.f);
                set(m, v.g);
                set(m, v.h);
                set(m, v.i);
            }
            
            inline void set(ofxOscMessage &m, const ofMatrix4x4 &v) const {
                for(int j = 0; j < 4; j++) for(int i = 0; i < 4; i++) set(m, v(i, j));
            }
            
            inline void set(ofxOscMessage &m, const ofRectangle &v) const {
                set(m, v.x);
                set(m, v.y);
                set(m, v.width);
                set(m, v.height);
            }
            
            template <typename U, size_t size>
            inline void set(ofxOscMessage &m, const U (&v)[size]) const {
                for(int i = 0; i < size; i++) { set(m, v[i]); }
            }
            
            template <typename U>
            inline void set(ofxOscMessage &m, const vector<U> &v) const {
                for(int i = 0; i < v.size(); i++) { set(m, v[i]); }
            }
            
#pragma mark ofParameter<T> / ofParameterGroup
            
            template <typename U>
            inline void set(ofxOscMessage &m, const ofParameter<U> &p) const {
                set(m, p.get());
            }
        };
        
#pragma mark Condition
        
        struct BasicCondition {
            BasicCondition() : bPublishNow(true) {};
            
            inline bool getCondition() { return isPublishNow() && inner_condition(); };
            
            inline bool isPublishNow() const { return bPublishNow; };
            inline void setEnablePublish(bool bEnablePublish) { this->bPublishNow = bEnablePublish; };
            
            virtual bool inner_condition() { return true; };
            
            typedef std::shared_ptr<BasicCondition> Ref;
        private:
            bool bPublishNow;
        };

        struct ConditionRef : BasicCondition {
            ConditionRef(bool &ref) : BasicCondition(), ref(ref) {};
            virtual bool inner_condition() { return ref; };
        private:
            bool &ref;
        };

        struct ConditionFunction : BasicCondition {
            ConditionFunction(bool (*getter)()) : BasicCondition(), getter(getter) {};
            virtual bool inner_condition() { return getter(); };
        private:
            bool (*getter)();
        };

        template <typename T>
        struct ConditionMethod : BasicCondition {
            ConditionMethod(T &that, bool (T::*getter)())
            : BasicCondition()
            , that(that)
            , getter(getter) {};
            
            virtual bool inner_condition() { return (that.*getter)(); };
        private:
            T &that; // dirty!!!
            bool (T::*getter)();
        };

        template <typename T>
        struct ConstConditionMethod : BasicCondition {
            ConstConditionMethod(const T &that, bool (T::*getter)() const)
            : BasicCondition()
            , that(that)
            , getter(getter) {};
            
            virtual bool inner_condition() { return (that.*getter)(); };
        private:
            const T &that;
            bool (T::*getter)() const;
        };

        typedef BasicCondition::Ref BasicConditionRef;
        
#pragma mark Parameter
        
        struct AbstractParameter {
            AbstractParameter() : condition(new BasicCondition) {}
            virtual bool setMessage(ofxOscMessage &m, const std::string &address) = 0;
            void setCondition(BasicConditionRef ref) { condition = ref; };
            
            inline void setEnablePublish(bool bEnablePublish) { condition->setEnablePublish(bEnablePublish); };
            inline bool isPublishNow() const { return condition->isPublishNow(); };
        protected:
            bool canPublish() {
                return condition->getCondition();
            }
        private:
            BasicConditionRef condition;
        };
        
        template <typename T, bool isCheckValue>
        struct Parameter : AbstractParameter, SetImplementation {
            Parameter(T &t)
            : t(t) {}
            
            virtual bool setMessage(ofxOscMessage &m, const std::string &address) {
                if(!canPublish() || !isChanged()) return false;
                m.setAddress(address);
                set(m, get());
                return true;
            }
        protected:
            virtual bool isChanged() { return true; }
            virtual type_ref(T) get() { return t; }
            T &t;
        };

        template <typename T>
        struct Parameter<T, true> : Parameter<T, false> {
            Parameter(T &t)
            : Parameter<T, false>(t) {}
            
        protected:
            virtual bool isChanged() {
                if(old != this->get()) {
                    old = this->get();
                    return true;
                } else {
                    return false;
                }
            }
            
            T old;
        };

        template <typename Base, size_t size>
        struct Parameter<Base(&)[size], true> : AbstractParameter, SetImplementation {
            Parameter(Base (&t)[size])
            : t(t) { for(size_t i = 0; i < size; i++) old[i] = t[i]; }
            virtual ~Parameter() { };
            
            virtual bool setMessage(ofxOscMessage &m, const std::string &address) {
                if(!canPublish() || !isChanged()) return false;
                m.setAddress(address);
                set(m, get());
                return true;
            }
            
        protected:
            virtual bool isChanged() {
                bool isChange = false;
                for(int i = 0; i < size; i++) {
                    isChange = isChange || (old[i] != get()[i]);
                    if(isChange) break;
                }
                
                if(isChange) {
                    for(int i = 0; i < size; i++) {
                        old[i] = get()[i];
                    }
                    return true;
                } else {
                    return false;
                }
            }
            
            virtual Base (&get())[size] { return t; }
        protected:
            Base (&t)[size];
            Base old[size];
        };
        
        template <typename T, bool isCheckValue>
        struct GetterFunctionParameter : Parameter<T, isCheckValue> {
            typedef T (*GetterFunction)();
            GetterFunctionParameter(GetterFunction getter)
            : Parameter<T, isCheckValue>(dummy)
            , getter(getter) {}
            
        protected:
            virtual type_ref(T) get() { return dummy = getter(); }
            GetterFunction getter;
            remove_ref(T) dummy;
        };
        
        template <typename Base, size_t size, bool isCheckValue>
        struct GetterFunctionParameter<Base(&)[size], isCheckValue> : Parameter<Base(&)[size], isCheckValue>  {
            typedef Base (&T)[size];
            typedef T (*GetterFunction)();
            GetterFunctionParameter(GetterFunction getter)
            : Parameter<Base[size], isCheckValue>(dummy)
            , getter(getter) {}
            
        protected:
            virtual Base (&get())[size] {
                Base (&arr)[size] = getter();
                for(size_t i = 0; i < size; i++) dummy[i] = arr[i];
                return dummy;
            }
            GetterFunction getter;
            Base dummy[size];
        };
        
        template <typename T, typename C, bool isCheckValue>
        struct GetterParameter : Parameter<T, isCheckValue> {
            typedef T (C::*Getter)();
            
            GetterParameter(C &that, Getter getter)
            : Parameter<T, isCheckValue>(dummy)
            , getter(getter)
            , that(that) {}
            
        protected:
            virtual type_ref(T) get() { return dummy = (that.*getter)(); }
            Getter getter;
            C &that;
            remove_ref(T) dummy;
        };

        template <typename Base, size_t size, typename C, bool isCheckValue>
        struct GetterParameter<Base(&)[size], C, isCheckValue> : Parameter<Base(&)[size], isCheckValue> {
            typedef Base (&T)[size];
            typedef T (C::*Getter)();
            
            GetterParameter(C &that, Getter getter)
            : Parameter<T, isCheckValue>(dummy)
            , getter(getter)
            , that(that) {}

        protected:
            virtual T get() {
                T arr = (that.*getter)();
                for(size_t i = 0; i < size; i++) dummy[i] = arr[i];
                return dummy;
            }
            Getter getter;
            C &that;
            Base dummy[size];
        };
        
        template <typename T, typename C, bool isCheckValue>
        struct ConstGetterParameter : Parameter<T, isCheckValue> {
            typedef T (C::*Getter)() const;
            
            ConstGetterParameter(const C &that, Getter getter)
            : Parameter<T, isCheckValue>(dummy)
            , getter(getter)
            , that(that) {}
            
        protected:
            virtual type_ref(T) get() { return dummy = (that.*getter)(); }
            Getter getter;
            const C &that;
            remove_ref(T) dummy;
        };
        
        template <typename Base, size_t size, typename C, bool isCheckValue>
        struct ConstGetterParameter<Base(&)[size], C, isCheckValue> : Parameter<Base(&)[size], isCheckValue> {
            typedef Base (&T)[size];
            typedef T (C::*Getter)() const;
            
            ConstGetterParameter(const C &that, Getter getter)
            : Parameter<T, isCheckValue>(dummy)
            , getter(getter)
            , that(that) {}
            
        protected:
            virtual T get() {
                T arr = (that.*getter)();
                for(size_t i = 0; i < size; i++) dummy[i] = arr[i];
                return dummy;
            }
            Getter getter;
            const C &that;
            Base dummy[size];
        };

        typedef std::shared_ptr<AbstractParameter> ParameterRef;
        typedef std::multimap<std::string, ParameterRef> Targets;
        
        public:
        struct IP {
            IP(const IP &ip)
            : ip(ip.ip) {}
            
            IP(const std::string &ip)
            : ip(ip) {}
            
            bool operator<(const IP &rhs) const {
                return ip < rhs.ip;
            }
            
            std::string ip;
        private:
            IP();
        };
        
        struct Destination {
            Destination(const Destination &destination)
            : ip(destination.ip)
            , port(destination.port) {}
            
            Destination(const std::string &ip, int port)
            : ip(ip)
            , port(port) {}
            
            inline bool operator<(const Destination &rhs) const {
                return (ip != rhs.ip) ? (ip < rhs.ip) : (port < rhs.port);
            }
            
            inline bool operator!=(const Destination &rhs) const {
                return ip != rhs.ip || port != rhs.port;
            }
            
            std::string ip;
            int port;
        private:
            Destination();
        };
        
        struct DestinationWithAddress {
            DestinationWithAddress(const DestinationWithAddress &destination)
            : destination(destination.destination)
            , address(destination.address) {}
            
            DestinationWithAddress(const std::string &ip, int port, const std::string &address)
            : destination(ip, port)
            , address(address) {}
            
            inline bool operator<(const DestinationWithAddress &rhs) const {
                return (destination != rhs.destination) ? (destination < rhs.destination) : (address < rhs.address);
            }
            
            operator Destination() const {
                return destination;
            }

            inline operator const Destination&() const {
                return destination;
            }
            
            Destination destination;
            std::string address;
        private:
            DestinationWithAddress();
        };

        class Identifier {
            std::string address;
            ParameterRef ref;
            Destination key;
            
            void invalidation() {
                address = "";
                ref = nullptr;
                key = Destination();
            }
        public:
            Identifier() {}
            Identifier(const std::string &address, const ParameterRef &ref, const Destination &key)
            : address(address)
            , ref(ref)
            , key(key) {}
            
            const Destination &getKey() const { return key; };
            bool isValid() const { return static_cast<bool>(ref); }

            friend class OscPublisher;
        };

    public:
        class OscPublisher {
            Targets::const_iterator findFromTargets(const Identifier &identifier, const Targets &targets) const {
                Targets::const_iterator it = targets.find(identifier.address);
                if(it != targets.end()) {
                    for(std::size_t i = 0, size = targets.count(identifier.address); i < size; ++i, ++it) {
                        if(it->second == identifier.ref) {
                            return it;
                        }
                    }
                }
                return targets.end();
            }
            
            inline Targets::const_iterator findPublished(const Identifier &identifier) const {
                return findFromTargets(identifier, targets);
            }
            
            inline Targets::const_iterator findRegistered(const Identifier &identifier) const {
                return findFromTargets(identifier, registeredTargets);
            }
            

        public:
#pragma mark publish
            
            inline Identifier publish(const std::string &address, ParameterRef ref) {
                targets.insert(std::make_pair(address, ref));
                return {address, ref, destination};
            }
            
            template <typename T>
            Identifier publish(const std::string &address, T &value, bool whenValueIsChanged = true) {
                ParameterRef p;
                if(whenValueIsChanged) p = ParameterRef(new Parameter<T, true>(value));
                else                   p = ParameterRef(new Parameter<T, false>(value));
                return publish(address, p);
            }
            
            template <typename T>
            Identifier publish(const std::string &address, T (*getter)(), bool whenValueIsChanged = true) {
                ParameterRef p;
                if(whenValueIsChanged) p = ParameterRef(new GetterFunctionParameter<T, true>(getter));
                else                   p = ParameterRef(new GetterFunctionParameter<T, false>(getter));
                return publish(address, p);
            }

            template <typename T, typename C>
            Identifier publish(const std::string &address, C *that, T (C::*getter)(), bool whenValueIsChanged = true) {
                ParameterRef p;
                if(whenValueIsChanged) p = ParameterRef(new GetterParameter<T, C, true>(*that, getter));
                else                   p = ParameterRef(new GetterParameter<T, C, false>(*that, getter));
                return publish(address, p);
            }
            
            template <typename T, typename C>
            Identifier publish(const std::string &address, C &that, T (C::*getter)(), bool whenValueIsChanged = true) {
                ParameterRef p;
                if(whenValueIsChanged) p = ParameterRef(new GetterParameter<T, C, true>(that, getter));
                else                   p = ParameterRef(new GetterParameter<T, C, false>(that, getter));
                return publish(address, p);
            }
            
            template <typename T, typename C>
            Identifier publish(const std::string &address, const C * const that, T (C::*getter)() const, bool whenValueIsChanged = true) {
                ParameterRef p;
                if(whenValueIsChanged) p = ParameterRef(new ConstGetterParameter<T, C, true>(*that, getter));
                else                   p = ParameterRef(new ConstGetterParameter<T, C, false>(*that, getter));
                return publish(address, p);
            }
            
            template <typename T, typename C>
            Identifier publish(const std::string &address, const C &that, T (C::*getter)() const, bool whenValueIsChanged = true) {
                ParameterRef p;
                if(whenValueIsChanged) p = ParameterRef(new ConstGetterParameter<T, C, true>(that, getter));
                else                   p = ParameterRef(new ConstGetterParameter<T, C, false>(that, getter));
                return publish(address, p);
            }

#pragma mark publish conditional
#pragma mark condition is bool value ref
            
            template <typename T>
            Identifier publishIf(bool &condition, const std::string &address, T &value) {
                ParameterRef p = ParameterRef(new Parameter<T, false>(value));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionRef(condition)));
                return publish(address, p);
            }

            template <typename T>
            Identifier publishIf(bool &condition, const std::string &address, T (*getter)()) {
                ParameterRef p = ParameterRef(new GetterFunctionParameter<T, false>(getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionRef(condition)));
                return publish(address, p);
            }
            
            template <typename T, typename C>
            Identifier publishIf(bool &condition, const std::string &address, C *that, T (C::*getter)()) {
                ParameterRef p = ParameterRef(new GetterParameter<T, C, false>(*that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionRef(condition)));
                return publish(address, p);
            }

            template <typename T, typename C>
            Identifier publishIf(bool &condition, const std::string &address, C &that, T (C::*getter)()) {
                ParameterRef p = ParameterRef(new GetterParameter<T, C, false>(that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionRef(condition)));
                return publish(address, p);
            }
            
            template <typename T, typename C>
            Identifier publishIf(bool &condition, const std::string &address, const C * const that, T (C::*getter)() const) {
                ParameterRef p = ParameterRef(new ConstGetterParameter<T, C, false>(*that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionRef(condition)));
                return publish(address, p);
            }

            template <typename T, typename C>
            Identifier publishIf(bool &condition, const std::string &address, const C &that, T (C::*getter)() const) {
                ParameterRef p = ParameterRef(new ConstGetterParameter<T, C, false>(that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionRef(condition)));
                return publish(address, p);
            }
            
#pragma mark condition is function
            
            template <typename T>
            Identifier publishIf(bool (*condition)(), const std::string &address, T &value) {
                ParameterRef p = ParameterRef(new Parameter<T, false>(value));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionFunction(condition)));
                return publish(address, p);
            }
            
            template <typename T>
            Identifier publishIf(bool (*condition)(), const std::string &address, T (*getter)()) {
                ParameterRef p = ParameterRef(new GetterFunctionParameter<T, false>(getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionFunction(condition)));
                return publish(address, p);
            }
            
            template <typename T, typename C>
            Identifier publishIf(bool (*condition)(), const std::string &address, C *that, T (C::*getter)()) {
                ParameterRef p = ParameterRef(new GetterParameter<T, C, false>(*that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionFunction(condition)));
                return publish(address, p);
            }

            template <typename T, typename C>
            Identifier publishIf(bool (*condition)(), const std::string &address, C &that, T (C::*getter)()) {
                ParameterRef p = ParameterRef(new GetterParameter<T, C, false>(that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionFunction(condition)));
                return publish(address, p);
            }
            
            template <typename T, typename C>
            Identifier publishIf(bool (*condition)(), const std::string &address, const C * const that, T (C::*getter)() const) {
                ParameterRef p = ParameterRef(new ConstGetterParameter<T, C, false>(*that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionFunction(condition)));
                return publish(address, p);
            }

            template <typename T, typename C>
            Identifier publishIf(bool (*condition)(), const std::string &address, const C &that, T (C::*getter)() const) {
                ParameterRef p = ParameterRef(new ConstGetterParameter<T, C, false>(that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionFunction(condition)));
                return publish(address, p);
            }
            
#pragma mark condition is method
            
            template <typename T, typename Condition>
            Identifier publishIf(Condition &condition, bool (Condition::*method)(), const std::string &address, T &value) {
                ParameterRef p = ParameterRef(new Parameter<T, true>(value));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename Condition>
            Identifier publishIf(Condition &condition, bool (Condition::*method)(), const std::string &address, T (*getter)()) {
                ParameterRef p = ParameterRef(new GetterFunctionParameter<T, true>(getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename C, typename Condition>
            Identifier publishIf(Condition &condition, bool (Condition::*method)(), const std::string &address, C *that, T (C::*getter)()) {
                ParameterRef p = ParameterRef(new GetterParameter<T, C, true>(*that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename C, typename Condition>
            Identifier publishIf(Condition &condition, bool (Condition::*method)(), const std::string &address, C &that, T (C::*getter)()) {
                ParameterRef p = ParameterRef(new GetterParameter<T, C, true>(that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename C, typename Condition>
            Identifier publishIf(Condition &condition, bool (Condition::*method)(), const std::string &address, const C * const that, T (C::*getter)() const) {
                ParameterRef p = ParameterRef(new ConstGetterParameter<T, C, true>(*that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename C, typename Condition>
            Identifier publishIf(Condition &condition, bool (Condition::*method)(), const std::string &address, const C &that, T (C::*getter)() const) {
                ParameterRef p = ParameterRef(new ConstGetterParameter<T, C, true>(that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
#pragma mark condition is const method
            
            template <typename T, typename Condition>
            Identifier publishIf(const Condition &condition, bool (Condition::*method)() const, const std::string &address, T &value) {
                ParameterRef p = ParameterRef(new Parameter<T, true>(value));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConstConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename Condition>
            Identifier publishIf(const Condition &condition, bool (Condition::*method)() const, const std::string &address, T (*getter)()) {
                ParameterRef p = ParameterRef(new GetterFunctionParameter<T, true>(getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConstConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename C, typename Condition>
            Identifier publishIf(const Condition &condition, bool (Condition::*method)() const, const std::string &address, C *that, T (C::*getter)()) {
                ParameterRef p = ParameterRef(new GetterParameter<T, C, true>(*that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConstConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename C, typename Condition>
            Identifier publishIf(const Condition &condition, bool (Condition::*method)() const, const std::string &address, C &that, T (C::*getter)()) {
                ParameterRef p = ParameterRef(new GetterParameter<T, C, true>(that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConstConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename C, typename Condition>
            Identifier publishIf(const Condition &condition, bool (Condition::*method)() const, const std::string &address, const C * const that, T (C::*getter)() const) {
                ParameterRef p = ParameterRef(new ConstGetterParameter<T, C, true>(*that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConstConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename C, typename Condition>
            Identifier publishIf(const Condition &condition, bool (Condition::*method)() const, const std::string &address, const C &that, T (C::*getter)() const) {
                ParameterRef p = ParameterRef(new ConstGetterParameter<T, C, true>(that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConstConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
#pragma mark unpublish
            
            void unpublish(const std::string &address) {
                if(targets.find(address) == targets.end()) targets.erase(address);
            }
            
            void unpublish(const Identifier &identifier) {
                Targets::const_iterator it{findPublished(identifier)};
                if(it != targets.end()) {
                    targets.erase(it);
                }
            }
            
            void unpublish() {
                targets.clear();
            }

#pragma mark stop publish temporary
            
            void stopPublishTemporary(const std::string &address) {
                if(isPublished(address)) {
                    Targets::iterator it = targets.find(address);
                    for(std::size_t i = 0, size = targets.count(address); i < size; i++, ++it) {
                        it->second->setEnablePublish(false);
                    }
                }
            }
            
            void stopPublishTemporary(const Identifier &identifier) {
                Targets::const_iterator it{findPublished(identifier)};
                if(it != targets.end()) {
                    it->second->setEnablePublish(false);
                }
            }
            
            void resumePublish(const std::string &address) {
                if(isPublished(address)) {
                    Targets::iterator it = targets.find(address);
                    for(std::size_t i = 0, size = targets.count(address); i < size; i++, ++it) {
                        it->second->setEnablePublish(true);
                    }
                }
            }
            
            void resumePublishTemporary(const Identifier &identifier) {
                Targets::const_iterator it{findPublished(identifier)};
                if(it != targets.end()) {
                    it->second->setEnablePublish(true);
                }
            }
            
#pragma mark doRegister
            
            inline Identifier doRegister(const std::string &address, ParameterRef ref) {
                registeredTargets.insert(std::make_pair(address, ref));
                return {address, ref, destination};
            }
            
            template <typename T>
            Identifier doRegister(const std::string &address, T &value) {
                return doRegister(address, ParameterRef(new Parameter<T, false>(value)));
            }
            
            template <typename T>
            Identifier doRegister(const std::string &address, T (*getter)()) {
                return doRegister(address, ParameterRef(new GetterFunctionParameter<T, false>(getter)));
            }
            
            template <typename T, typename C>
            Identifier doRegister(const std::string &address, C *that, T (C::*getter)()) {
                return doRegister(address, ParameterRef(new GetterParameter<T, C, false>(*that, getter)));
            }
            
            template <typename T, typename C>
            Identifier doRegister(const std::string &address, C &that, T (C::*getter)()) {
                return doRegister(address, ParameterRef(new GetterParameter<T, C, false>(that, getter)));
            }
            
            template <typename T, typename C>
            Identifier doRegister(const std::string &address, const C * const that, T (C::*getter)() const) {
                return doRegister(address, ParameterRef(new ConstGetterParameter<T, C, false>(*that, getter)));
            }
            
            template <typename T, typename C>
            Identifier doRegister(const std::string &address, const C &that, T (C::*getter)() const) {
                return doRegister(address, ParameterRef(new ConstGetterParameter<T, C, false>(that, getter)));
            }
            
#pragma mark publishRegistered
            
            inline void publishRegistered(const std::string &address) {
                Targets::iterator it = registeredTargets.find(address);
                if(it == registeredTargets.end()) {
                    ofLogWarning("ofxPubSubOsc") << address << " is not registered.";
                }
                ofxOscMessage m;
                for(std::size_t i = 0, size = registeredTargets.count(address); i < size; i++, ++it) {
                    if(it->second->setMessage(m, it->first)) sender.sendMessage(m);
                    m.clear();
                }
            }
            
            inline void publishRegistered(const Identifier &identifier) {
                // TODO: implement
                Targets::const_iterator it{findRegistered(identifier)};
                if(it != registeredTargets.end()) {
                    ofxOscMessage m;
                    if(it->second->setMessage(m, it->first)) sender.sendMessage(m);
                    m.clear();
                }
            }
            
#pragma mark unregister
            
            inline void unregister(const std::string &address) {
                if(registeredTargets.find(address) == registeredTargets.end()) registeredTargets.erase(address);
            }
            
            inline void unregister(const Identifier &identifier) {
                // TODO: implement
                Targets::const_iterator it{findRegistered(identifier)};
                if(it != registeredTargets.end()) {
                    registeredTargets.erase(it);
                }
            }
            
            inline void unregister() {
                registeredTargets.clear();
            }
            
#pragma mark status
            
            inline bool isPublished() const {
                return !targets.empty();
            }
            
            inline bool isPublished(const Identifier &identifier) const {
                return isPublished() && (findPublished(identifier) != targets.end());
            }
            
            inline bool isPublished(const std::string &address) const {
                return isPublished() && (targets.find(address) != targets.end());
            }
            
            inline bool isEnabled(const std::string &address) const {
                // TODO: fix
                return isPublished(address) && targets.find(address)->second->isPublishNow();
            }
            
            inline bool isEnabled(const Identifier &identifier) const {
                Targets::const_iterator it{findPublished(identifier)};
                return (it != targets.end()) && it->second->isPublishNow();
            }
            
            inline bool isRegistered() const {
                return !registeredTargets.empty();
            }
            
            inline bool isRegistered(const std::string &address) const {
                return isRegistered() && (registeredTargets.find(address) != registeredTargets.end());
            }
            
            typedef std::shared_ptr<OscPublisher> Ref;
            
            static void setUseBundle(bool b) {
                bUseBundle = b;
            }
            
            static bool isUseBundle() {
                return bUseBundle;
            }
            
        private:
            OscPublisher(const Destination &destination)
            : destination(destination) {
                sender.setup(destination.ip, destination.port);
            }
            
            void update() {
                ofxOscMessage m;
                if(isUseBundle()) {
                    ofxOscBundle bundle;
                    for(Targets::iterator it = targets.begin(); it != targets.end(); ++it) {
                        if(it->second->setMessage(m, it->first)) bundle.addMessage(m);
                        m.clear();
                    }
                    if(bundle.getMessageCount()) sender.sendBundle(bundle);
                    bundle.clear();
                    return;
                }
                for(Targets::iterator it = targets.begin(); it != targets.end(); ++it) {
                    if(it->second->setMessage(m, it->first)) sender.sendMessage(m);
                    m.clear();
                }
                
            }
            
            Destination destination;
            ofxOscSender sender;
            Targets targets;
            Targets registeredTargets;
            static bool bUseBundle;
            friend class OscPublisherManager;
        };
        
        typedef std::map<Destination, OscPublisher::Ref> OscPublishers;
        
        static OscPublisherManager &getSharedInstance() {
            static OscPublisherManager *sharedInstance = new OscPublisherManager;
            return *sharedInstance;
        }
        
        static OscPublisher &getOscPublisher(const std::string &ip, int port) {
            OscPublishers &publishers = getSharedInstance().publishers;
            Destination destination(ip, port);
            if(publishers.find(destination) == publishers.end()) {
                publishers.insert(std::make_pair(destination, OscPublisher::Ref(new OscPublisher(destination))));
            }
            return *(publishers[destination].get());
        }
        
        void update(ofEventArgs &args) {
            for(OscPublishers::iterator it = publishers.begin(); it != publishers.end(); ++it) {
                it->second->update();
            }
        }
        OscPublisherManager() {
            ofAddListener(ofEvents().update, this, &OscPublisherManager::update, OF_EVENT_ORDER_AFTER_APP);
        }
        virtual ~OscPublisherManager() {
            ofRemoveListener(ofEvents().update, this, &OscPublisherManager::update, OF_EVENT_ORDER_AFTER_APP);
        }
        OscPublishers publishers;
        
#pragma mark iterator
    public:
        typedef OscPublishers::iterator iterator;
        typedef OscPublishers::const_iterator const_iterator;
        typedef OscPublishers::reverse_iterator reverse_iterator;
        typedef OscPublishers::const_reverse_iterator const_reverse_iterator;
        
        iterator begin() { return publishers.begin(); }
        iterator end() { return publishers.end(); }
        
        const_iterator begin() const { return publishers.begin(); }
        const_iterator end() const { return publishers.end(); }
        const_iterator cbegin() const { return publishers.begin(); }
        const_iterator cend() const { return publishers.end(); }
        
        reverse_iterator rbegin() { return publishers.rbegin(); }
        reverse_iterator rend() { return publishers.rend(); }
        
        const_reverse_iterator rbegin() const { return publishers.rbegin(); }
        const_reverse_iterator rend() const { return publishers.rend(); }
        const_reverse_iterator crbegin() const { return publishers.rbegin(); }
        const_reverse_iterator crend() const { return publishers.crend(); }
    };
    
    bool OscPublisherManager::OscPublisher::bUseBundle = false;
};
#undef type_ref

#pragma mark - syntax sugars

#pragma mark getter

typedef ofx::OscPublisherManager ofxOscPublisherManager;
typedef ofxOscPublisherManager::OscPublisher ofxOscPublisher;
typedef ofxOscPublisherManager::Identifier ofxOscPublisherIdentifier;

/// \brief get a OscPublisherManager.
/// \returns ofxOscPublisherManager

inline ofxOscPublisherManager &ofxGetOscPublisherManager() {
    return ofxOscPublisherManager::getSharedInstance();
}

/// \brief get a OscPublisher.
/// \param ip target ip is typed const std::string &
/// \param port target port is typed int
/// \returns ofxOscPublisher binded to ip & port

inline ofxOscPublisher &ofxGetOscPublisher(const std::string &ip, int port) {
    return ofxOscPublisherManager::getOscPublisher(ip, port);
}

#pragma mark publish

/// \name ofxPublishOsc
/// \{

/// \brief publish value as an OSC message with an address pattern address to ip:port every time the value has changed. 
/// If whenValueIsChanged is set to false, then the binded value is sent every frame after App::update.
/// template parameter T is suggested by value
/// \param ip target ip is typed const std::string &
/// \param port target port is typed int
/// \param address osc address is typed const std::string &
/// \param value reference of value is typed T &
/// \param whenValueIsChanged if this value to false, then we send value every update
/// \returns ofxOscPublisherIdentifier

template <typename T>
inline ofxOscPublisherIdentifier ofxPublishOsc(const std::string &ip, int port, const std::string &address, T &value, bool whenValueIsChanged = true) {
    return ofxGetOscPublisher(ip, port).publish(address, value, whenValueIsChanged);
}

/// \brief publish the value will be gave by function as an OSC message with an address pattern address to ip:port every time the value has changed.
///  If whenValueIsChanged is set to false, then the binded value is sent every frame after App::update.
/// template parameter T is suggested by value
/// \param ip target ip is typed const std::string &
/// \param port target port is typed int
/// \param address osc address is typed const std::string &
/// \param getter this function gives value, is typed T(*)()
/// \param whenValueIsChanged if this value to false, then we send value every update
/// \returns ofxOscPublisherIdentifier

template <typename T>
inline ofxOscPublisherIdentifier ofxPublishOsc(const std::string &ip, int port, const std::string &address, T (*getter)(), bool whenValueIsChanged = true) {
    return ofxGetOscPublisher(ip, port).publish(address, getter, whenValueIsChanged);
}

/// \brief publish the value will be gave by function as an OSC message with an address pattern address to ip:port every time the value has changed.
/// If whenValueIsChanged is set to false, then the binded value is sent every frame after App::update.
/// template parameter T is suggested by value and U is suggested by that and getter.
/// \param ip target ip is typed const std::string &
/// \param port target port is typed int
/// \param address osc address is typed const std::string &
/// \param that this object is typed U*, will bind with next parameter method. is called as (that->*getter)().
/// \param getter this method gives value, is typed T(C::*)()
/// \param whenValueIsChanged if this value to false, then we send value every update
/// \returns ofxOscPublisherIdentifier

template <typename T, typename C>
inline ofxOscPublisherIdentifier ofxPublishOsc(const std::string &ip, int port, const std::string &address, C *that, T (C::*getter)(), bool whenValueIsChanged = true) {
    return ofxGetOscPublisher(ip, port).publish(address, *that, getter, whenValueIsChanged);<commit_msg>add ConstParameter<commit_after>//
//  ofxOscPublisher.h
//
//  Created by ISHII 2bit on 2015/05/11.
//
//

#pragma once

#include "ofMain.h"
#include "ofxOsc.h"

#include "details/ofxpubsubosc_settings.h"
#include "details/ofxpubsubosc_type_utils.h"

#include <initializer_list>

namespace ofx {
    using namespace ofxpubsubosc;
    
    namespace {
        template <typename T>
        struct remove_reference {
            typedef T type;
        };
        template <typename T>
        struct remove_reference<T &> {
            typedef T type;
        };
#define remove_ref(T) typename ofx::remove_reference<T>::type
        
        template <typename T> struct is_integral { static const bool value = false; };
#define define_is_integral(T) template <> struct is_integral<T> { static const bool value = true; };
        define_is_integral(bool);
        define_is_integral(short);
        define_is_integral(unsigned char);
        define_is_integral(char);
        define_is_integral(unsigned short);
        define_is_integral(int);
        define_is_integral(unsigned int);
        define_is_integral(long);
        define_is_integral(unsigned long);
        define_is_integral(long long);
        define_is_integral(unsigned long long);
#undef define_is_integral
        
#define type_ref(T) typename add_reference_if_non_arithmetic<T>::type

        template <bool b>
        struct enable;
        
        template <>
        struct enable<true> { typedef void type; };
        
        template <typename T>
        struct is_integral_and_lt_64bit {
            static const bool value = is_integral<T>::value && (sizeof(T) < 8);
        };
        
        template <typename T>
        struct is_integral_and_geq_64bit {
            static const bool value = is_integral<T>::value && (8 <= sizeof(T));
        };
    };
    
    class OscPublisherManager {
    private:
        class OscPublisher;
        
        struct SetImplementation {
        protected:
            template <typename T>
            inline typename enable<is_integral_and_lt_64bit<T>::value>::type set(ofxOscMessage &m, T v) const { m.addIntArg(v); }
            
            template <typename T>
            inline typename enable<is_integral_and_geq_64bit<T>::value>::type set(ofxOscMessage &m, T v) const { m.addInt64Arg(v); }
            
#define define_set_float(type) inline void set(ofxOscMessage &m, type v) const { m.addFloatArg(v); }
            define_set_float(float);
            define_set_float(double);
#undef define_set_float
            inline void set(ofxOscMessage &m, const std::string &v) const { m.addStringArg(v); }
#if ENABLE_OF_BUFFER
            inline void set(ofxOscMessage &m, const ofBuffer &v) const { m.addBlobArg(v); };
#endif
            template <typename PixType>
            inline void set(ofxOscMessage &m, const ofColor_<PixType> &v) const {  setVec<4>(m, v); }
            inline void set(ofxOscMessage &m, const ofVec2f &v) const { setVec<2>(m, v); }
            inline void set(ofxOscMessage &m, const ofVec3f &v) const { setVec<3>(m, v); }
            inline void set(ofxOscMessage &m, const ofVec4f &v) const { setVec<4>(m, v); }
            inline void set(ofxOscMessage &m, const ofQuaternion &v) const { setVec<4>(m, v); }
            
            template <size_t n, typename T>
            inline void setVec(ofxOscMessage &m, const T &v) const {
                for(int i = 0; i < n; i++) { set(m, v[i]); }
            }
            
            inline void set(ofxOscMessage &m, const ofMatrix3x3 &v) const {
                set(m, v.a);
                set(m, v.b);
                set(m, v.c);
                set(m, v.d);
                set(m, v.e);
                set(m, v.f);
                set(m, v.g);
                set(m, v.h);
                set(m, v.i);
            }
            
            inline void set(ofxOscMessage &m, const ofMatrix4x4 &v) const {
                for(int j = 0; j < 4; j++) for(int i = 0; i < 4; i++) set(m, v(i, j));
            }
            
            inline void set(ofxOscMessage &m, const ofRectangle &v) const {
                set(m, v.x);
                set(m, v.y);
                set(m, v.width);
                set(m, v.height);
            }
            
            template <typename U, size_t size>
            inline void set(ofxOscMessage &m, const U (&v)[size]) const {
                for(int i = 0; i < size; i++) { set(m, v[i]); }
            }
            
            template <typename U>
            inline void set(ofxOscMessage &m, const vector<U> &v) const {
                for(int i = 0; i < v.size(); i++) { set(m, v[i]); }
            }
            
#pragma mark ofParameter<T> / ofParameterGroup
            
            template <typename U>
            inline void set(ofxOscMessage &m, const ofParameter<U> &p) const {
                set(m, p.get());
            }
        };
        
#pragma mark Condition
        
        struct BasicCondition {
            BasicCondition() : bPublishNow(true) {};
            
            inline bool getCondition() { return isPublishNow() && inner_condition(); };
            
            inline bool isPublishNow() const { return bPublishNow; };
            inline void setEnablePublish(bool bEnablePublish) { this->bPublishNow = bEnablePublish; };
            
            virtual bool inner_condition() { return true; };
            
            typedef std::shared_ptr<BasicCondition> Ref;
        private:
            bool bPublishNow;
        };

        struct ConditionRef : BasicCondition {
            ConditionRef(bool &ref) : BasicCondition(), ref(ref) {};
            virtual bool inner_condition() { return ref; };
        private:
            bool &ref;
        };

        struct ConditionFunction : BasicCondition {
            ConditionFunction(bool (*getter)()) : BasicCondition(), getter(getter) {};
            virtual bool inner_condition() { return getter(); };
        private:
            bool (*getter)();
        };

        template <typename T>
        struct ConditionMethod : BasicCondition {
            ConditionMethod(T &that, bool (T::*getter)())
            : BasicCondition()
            , that(that)
            , getter(getter) {};
            
            virtual bool inner_condition() { return (that.*getter)(); };
        private:
            T &that; // dirty!!!
            bool (T::*getter)();
        };

        template <typename T>
        struct ConstConditionMethod : BasicCondition {
            ConstConditionMethod(const T &that, bool (T::*getter)() const)
            : BasicCondition()
            , that(that)
            , getter(getter) {};
            
            virtual bool inner_condition() { return (that.*getter)(); };
        private:
            const T &that;
            bool (T::*getter)() const;
        };

        typedef BasicCondition::Ref BasicConditionRef;
        
#pragma mark Parameter
        
        struct AbstractParameter {
            AbstractParameter() : condition(new BasicCondition) {}
            virtual bool setMessage(ofxOscMessage &m, const std::string &address) = 0;
            void setCondition(BasicConditionRef ref) { condition = ref; };
            
            inline void setEnablePublish(bool bEnablePublish) { condition->setEnablePublish(bEnablePublish); };
            inline bool isPublishNow() const { return condition->isPublishNow(); };
        protected:
            bool canPublish() {
                return condition->getCondition();
            }
        private:
            BasicConditionRef condition;
        };
        
        template <typename T, bool isCheckValue>
        struct Parameter : AbstractParameter, SetImplementation {
            Parameter(T &t)
            : t(t) {}
            
            virtual bool setMessage(ofxOscMessage &m, const std::string &address) {
                if(!canPublish() || !isChanged()) return false;
                m.setAddress(address);
                set(m, get());
                return true;
            }
        protected:
            virtual bool isChanged() { return true; }
            virtual type_ref(T) get() { return t; }
            T &t;
        };

        template <typename T>
        struct Parameter<T, true> : Parameter<T, false> {
            Parameter(T &t)
            : Parameter<T, false>(t) {}
            
        protected:
            virtual bool isChanged() {
                if(old != this->get()) {
                    old = this->get();
                    return true;
                } else {
                    return false;
                }
            }
            
            T old;
        };

        template <typename Base, size_t size>
        struct Parameter<Base(&)[size], true> : AbstractParameter, SetImplementation {
            Parameter(Base (&t)[size])
            : t(t) { for(size_t i = 0; i < size; i++) old[i] = t[i]; }
            virtual ~Parameter() { };
            
            virtual bool setMessage(ofxOscMessage &m, const std::string &address) {
                if(!canPublish() || !isChanged()) return false;
                m.setAddress(address);
                set(m, get());
                return true;
            }
            
        protected:
            virtual bool isChanged() {
                bool isChange = false;
                for(int i = 0; i < size; i++) {
                    isChange = isChange || (old[i] != get()[i]);
                    if(isChange) break;
                }
                
                if(isChange) {
                    for(int i = 0; i < size; i++) {
                        old[i] = get()[i];
                    }
                    return true;
                } else {
                    return false;
                }
            }
            
            virtual Base (&get())[size] { return t; }
        protected:
            Base (&t)[size];
            Base old[size];
        };
        
        template <typename T, bool isCheckValue>
        struct ConstParameter : AbstractParameter, SetImplementation {
            ConstParameter(const T &t)
            : t(t) {}
            
            virtual bool setMessage(ofxOscMessage &m, const std::string &address) {
                if(!canPublish() || !isChanged()) return false;
                m.setAddress(address);
                set(m, get());
                return true;
            }
        protected:
            virtual bool isChanged() { return true; }
            virtual const T &get() { return t; }
            const T t;
        };
        
        template <typename T>
        struct ConstParameter<T, true> : ConstParameter<T, false> {
            ConstParameter(const T &t)
            : ConstParameter<T, false>(t) {}
            
        protected:
            virtual bool isChanged() {
                static bool initial{true};
                bool tmp = initial;
                initial = false;
                return tmp;
            }
        };
        
        template <typename Base, size_t size>
        struct ConstParameter<const Base(&)[size], false> : AbstractParameter, SetImplementation {
            ConstParameter(const Base (&t)[size])
            : t(t) {}
            
            virtual bool setMessage(ofxOscMessage &m, const std::string &address) {
                if(!canPublish() || !isChanged()) return false;
                m.setAddress(address);
                set(m, get());
                return true;
            }
            
        protected:
            virtual bool isChanged() { return true; }
            
            virtual const Base (&get())[size] { return t; }
            const Base (&t)[size];
        };
        
        template <typename Base, size_t size>
        struct ConstParameter<const Base(&)[size], true> : ConstParameter<const Base(&)[size], false> {
            ConstParameter(const Base(&t)[size])
            : ConstParameter<const Base(&)[size], false>(t) {}
            
        protected:
            virtual bool isChanged() {
                static bool initial{true};
                bool tmp = initial;
                initial = false;
                return tmp;
            }
        };

        template <typename T, bool isCheckValue>
        struct GetterFunctionParameter : Parameter<T, isCheckValue> {
            typedef T (*GetterFunction)();
            GetterFunctionParameter(GetterFunction getter)
            : Parameter<T, isCheckValue>(dummy)
            , getter(getter) {}
            
        protected:
            virtual type_ref(T) get() { return dummy = getter(); }
            GetterFunction getter;
            remove_ref(T) dummy;
        };
        
        template <typename Base, size_t size, bool isCheckValue>
        struct GetterFunctionParameter<Base(&)[size], isCheckValue> : Parameter<Base(&)[size], isCheckValue>  {
            typedef Base (&T)[size];
            typedef T (*GetterFunction)();
            GetterFunctionParameter(GetterFunction getter)
            : Parameter<Base[size], isCheckValue>(dummy)
            , getter(getter) {}
            
        protected:
            virtual Base (&get())[size] {
                Base (&arr)[size] = getter();
                for(size_t i = 0; i < size; i++) dummy[i] = arr[i];
                return dummy;
            }
            GetterFunction getter;
            Base dummy[size];
        };
        
        template <typename T, typename C, bool isCheckValue>
        struct GetterParameter : Parameter<T, isCheckValue> {
            typedef T (C::*Getter)();
            
            GetterParameter(C &that, Getter getter)
            : Parameter<T, isCheckValue>(dummy)
            , getter(getter)
            , that(that) {}
            
        protected:
            virtual type_ref(T) get() { return dummy = (that.*getter)(); }
            Getter getter;
            C &that;
            remove_ref(T) dummy;
        };

        template <typename Base, size_t size, typename C, bool isCheckValue>
        struct GetterParameter<Base(&)[size], C, isCheckValue> : Parameter<Base(&)[size], isCheckValue> {
            typedef Base (&T)[size];
            typedef T (C::*Getter)();
            
            GetterParameter(C &that, Getter getter)
            : Parameter<T, isCheckValue>(dummy)
            , getter(getter)
            , that(that) {}

        protected:
            virtual T get() {
                T arr = (that.*getter)();
                for(size_t i = 0; i < size; i++) dummy[i] = arr[i];
                return dummy;
            }
            Getter getter;
            C &that;
            Base dummy[size];
        };
        
        template <typename T, typename C, bool isCheckValue>
        struct ConstGetterParameter : Parameter<T, isCheckValue> {
            typedef T (C::*Getter)() const;
            
            ConstGetterParameter(const C &that, Getter getter)
            : Parameter<T, isCheckValue>(dummy)
            , getter(getter)
            , that(that) {}
            
        protected:
            virtual type_ref(T) get() { return dummy = (that.*getter)(); }
            Getter getter;
            const C &that;
            remove_ref(T) dummy;
        };
        
        template <typename Base, size_t size, typename C, bool isCheckValue>
        struct ConstGetterParameter<Base(&)[size], C, isCheckValue> : Parameter<Base(&)[size], isCheckValue> {
            typedef Base (&T)[size];
            typedef T (C::*Getter)() const;
            
            ConstGetterParameter(const C &that, Getter getter)
            : Parameter<T, isCheckValue>(dummy)
            , getter(getter)
            , that(that) {}
            
        protected:
            virtual T get() {
                T arr = (that.*getter)();
                for(size_t i = 0; i < size; i++) dummy[i] = arr[i];
                return dummy;
            }
            Getter getter;
            const C &that;
            Base dummy[size];
        };

        typedef std::shared_ptr<AbstractParameter> ParameterRef;
        typedef std::multimap<std::string, ParameterRef> Targets;
        
        public:
        struct IP {
            IP(const IP &ip)
            : ip(ip.ip) {}
            
            IP(const std::string &ip)
            : ip(ip) {}
            
            bool operator<(const IP &rhs) const {
                return ip < rhs.ip;
            }
            
            std::string ip;
        private:
            IP();
        };
        
        struct Destination {
            Destination(const Destination &destination)
            : ip(destination.ip)
            , port(destination.port) {}
            
            Destination(const std::string &ip, int port)
            : ip(ip)
            , port(port) {}
            
            inline bool operator<(const Destination &rhs) const {
                return (ip != rhs.ip) ? (ip < rhs.ip) : (port < rhs.port);
            }
            
            inline bool operator!=(const Destination &rhs) const {
                return ip != rhs.ip || port != rhs.port;
            }
            
            std::string ip;
            int port;
        private:
            Destination();
        };
        
        struct DestinationWithAddress {
            DestinationWithAddress(const DestinationWithAddress &destination)
            : destination(destination.destination)
            , address(destination.address) {}
            
            DestinationWithAddress(const std::string &ip, int port, const std::string &address)
            : destination(ip, port)
            , address(address) {}
            
            inline bool operator<(const DestinationWithAddress &rhs) const {
                return (destination != rhs.destination) ? (destination < rhs.destination) : (address < rhs.address);
            }
            
            operator Destination() const {
                return destination;
            }

            inline operator const Destination&() const {
                return destination;
            }
            
            Destination destination;
            std::string address;
        private:
            DestinationWithAddress();
        };

        class Identifier {
            std::string address;
            ParameterRef ref;
            Destination key;
            
            void invalidation() {
                address = "";
                ref = nullptr;
                key = Destination();
            }
        public:
            Identifier() {}
            Identifier(const std::string &address, const ParameterRef &ref, const Destination &key)
            : address(address)
            , ref(ref)
            , key(key) {}
            
            const Destination &getKey() const { return key; };
            bool isValid() const { return static_cast<bool>(ref); }

            friend class OscPublisher;
        };

    public:
        class OscPublisher {
            Targets::const_iterator findFromTargets(const Identifier &identifier, const Targets &targets) const {
                Targets::const_iterator it = targets.find(identifier.address);
                if(it != targets.end()) {
                    for(std::size_t i = 0, size = targets.count(identifier.address); i < size; ++i, ++it) {
                        if(it->second == identifier.ref) {
                            return it;
                        }
                    }
                }
                return targets.end();
            }
            
            inline Targets::const_iterator findPublished(const Identifier &identifier) const {
                return findFromTargets(identifier, targets);
            }
            
            inline Targets::const_iterator findRegistered(const Identifier &identifier) const {
                return findFromTargets(identifier, registeredTargets);
            }
            

        public:
#pragma mark publish
            
            inline Identifier publish(const std::string &address, ParameterRef ref) {
                targets.insert(std::make_pair(address, ref));
                return {address, ref, destination};
            }
            
            template <typename T>
            Identifier publish(const std::string &address, T &value, bool whenValueIsChanged = true) {
                ParameterRef p;
                if(whenValueIsChanged) p = ParameterRef(new Parameter<T, true>(value));
                else                   p = ParameterRef(new Parameter<T, false>(value));
                return publish(address, p);
            }
            
            template <typename T>
            Identifier publish(const std::string &address, const T &value, bool whenValueIsChanged = true) {
                ParameterRef p;
                if(whenValueIsChanged) p = ParameterRef(new ConstParameter<T, true>(value));
                else                   p = ParameterRef(new ConstParameter<T, false>(value));
                return publish(address, p);
            }
            
            template <typename T>
            Identifier publish(const std::string &address, T (*getter)(), bool whenValueIsChanged = true) {
                ParameterRef p;
                if(whenValueIsChanged) p = ParameterRef(new GetterFunctionParameter<T, true>(getter));
                else                   p = ParameterRef(new GetterFunctionParameter<T, false>(getter));
                return publish(address, p);
            }

            template <typename T, typename C>
            Identifier publish(const std::string &address, C *that, T (C::*getter)(), bool whenValueIsChanged = true) {
                ParameterRef p;
                if(whenValueIsChanged) p = ParameterRef(new GetterParameter<T, C, true>(*that, getter));
                else                   p = ParameterRef(new GetterParameter<T, C, false>(*that, getter));
                return publish(address, p);
            }
            
            template <typename T, typename C>
            Identifier publish(const std::string &address, C &that, T (C::*getter)(), bool whenValueIsChanged = true) {
                ParameterRef p;
                if(whenValueIsChanged) p = ParameterRef(new GetterParameter<T, C, true>(that, getter));
                else                   p = ParameterRef(new GetterParameter<T, C, false>(that, getter));
                return publish(address, p);
            }
            
            template <typename T, typename C>
            Identifier publish(const std::string &address, const C * const that, T (C::*getter)() const, bool whenValueIsChanged = true) {
                ParameterRef p;
                if(whenValueIsChanged) p = ParameterRef(new ConstGetterParameter<T, C, true>(*that, getter));
                else                   p = ParameterRef(new ConstGetterParameter<T, C, false>(*that, getter));
                return publish(address, p);
            }
            
            template <typename T, typename C>
            Identifier publish(const std::string &address, const C &that, T (C::*getter)() const, bool whenValueIsChanged = true) {
                ParameterRef p;
                if(whenValueIsChanged) p = ParameterRef(new ConstGetterParameter<T, C, true>(that, getter));
                else                   p = ParameterRef(new ConstGetterParameter<T, C, false>(that, getter));
                return publish(address, p);
            }

#pragma mark publish conditional
#pragma mark condition is bool value ref
            
            template <typename T>
            Identifier publishIf(bool &condition, const std::string &address, T &value) {
                ParameterRef p = ParameterRef(new Parameter<T, false>(value));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionRef(condition)));
                return publish(address, p);
            }

            template <typename T>
            Identifier publishIf(bool &condition, const std::string &address, const T &value) {
                ParameterRef p = ParameterRef(new ConstParameter<T, false>(value));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionRef(condition)));
                return publish(address, p);
            }
            
            template <typename T>
            Identifier publishIf(bool &condition, const std::string &address, T (*getter)()) {
                ParameterRef p = ParameterRef(new GetterFunctionParameter<T, false>(getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionRef(condition)));
                return publish(address, p);
            }
            
            template <typename T, typename C>
            Identifier publishIf(bool &condition, const std::string &address, C *that, T (C::*getter)()) {
                ParameterRef p = ParameterRef(new GetterParameter<T, C, false>(*that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionRef(condition)));
                return publish(address, p);
            }

            template <typename T, typename C>
            Identifier publishIf(bool &condition, const std::string &address, C &that, T (C::*getter)()) {
                ParameterRef p = ParameterRef(new GetterParameter<T, C, false>(that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionRef(condition)));
                return publish(address, p);
            }
            
            template <typename T, typename C>
            Identifier publishIf(bool &condition, const std::string &address, const C * const that, T (C::*getter)() const) {
                ParameterRef p = ParameterRef(new ConstGetterParameter<T, C, false>(*that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionRef(condition)));
                return publish(address, p);
            }

            template <typename T, typename C>
            Identifier publishIf(bool &condition, const std::string &address, const C &that, T (C::*getter)() const) {
                ParameterRef p = ParameterRef(new ConstGetterParameter<T, C, false>(that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionRef(condition)));
                return publish(address, p);
            }
            
#pragma mark condition is function
            
            template <typename T>
            Identifier publishIf(bool (*condition)(), const std::string &address, T &value) {
                ParameterRef p = ParameterRef(new Parameter<T, false>(value));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionFunction(condition)));
                return publish(address, p);
            }
            
            template <typename T>
            Identifier publishIf(bool (*condition)(), const std::string &address, T (*getter)()) {
                ParameterRef p = ParameterRef(new GetterFunctionParameter<T, false>(getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionFunction(condition)));
                return publish(address, p);
            }
            
            template <typename T, typename C>
            Identifier publishIf(bool (*condition)(), const std::string &address, C *that, T (C::*getter)()) {
                ParameterRef p = ParameterRef(new GetterParameter<T, C, false>(*that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionFunction(condition)));
                return publish(address, p);
            }

            template <typename T, typename C>
            Identifier publishIf(bool (*condition)(), const std::string &address, C &that, T (C::*getter)()) {
                ParameterRef p = ParameterRef(new GetterParameter<T, C, false>(that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionFunction(condition)));
                return publish(address, p);
            }
            
            template <typename T, typename C>
            Identifier publishIf(bool (*condition)(), const std::string &address, const C * const that, T (C::*getter)() const) {
                ParameterRef p = ParameterRef(new ConstGetterParameter<T, C, false>(*that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionFunction(condition)));
                return publish(address, p);
            }

            template <typename T, typename C>
            Identifier publishIf(bool (*condition)(), const std::string &address, const C &that, T (C::*getter)() const) {
                ParameterRef p = ParameterRef(new ConstGetterParameter<T, C, false>(that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionFunction(condition)));
                return publish(address, p);
            }
            
#pragma mark condition is method
            
            template <typename T, typename Condition>
            Identifier publishIf(Condition &condition, bool (Condition::*method)(), const std::string &address, T &value) {
                ParameterRef p = ParameterRef(new Parameter<T, true>(value));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename Condition>
            Identifier publishIf(Condition &condition, bool (Condition::*method)(), const std::string &address, T (*getter)()) {
                ParameterRef p = ParameterRef(new GetterFunctionParameter<T, true>(getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename C, typename Condition>
            Identifier publishIf(Condition &condition, bool (Condition::*method)(), const std::string &address, C *that, T (C::*getter)()) {
                ParameterRef p = ParameterRef(new GetterParameter<T, C, true>(*that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename C, typename Condition>
            Identifier publishIf(Condition &condition, bool (Condition::*method)(), const std::string &address, C &that, T (C::*getter)()) {
                ParameterRef p = ParameterRef(new GetterParameter<T, C, true>(that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename C, typename Condition>
            Identifier publishIf(Condition &condition, bool (Condition::*method)(), const std::string &address, const C * const that, T (C::*getter)() const) {
                ParameterRef p = ParameterRef(new ConstGetterParameter<T, C, true>(*that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename C, typename Condition>
            Identifier publishIf(Condition &condition, bool (Condition::*method)(), const std::string &address, const C &that, T (C::*getter)() const) {
                ParameterRef p = ParameterRef(new ConstGetterParameter<T, C, true>(that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
#pragma mark condition is const method
            
            template <typename T, typename Condition>
            Identifier publishIf(const Condition &condition, bool (Condition::*method)() const, const std::string &address, T &value) {
                ParameterRef p = ParameterRef(new Parameter<T, true>(value));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConstConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename Condition>
            Identifier publishIf(const Condition &condition, bool (Condition::*method)() const, const std::string &address, T (*getter)()) {
                ParameterRef p = ParameterRef(new GetterFunctionParameter<T, true>(getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConstConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename C, typename Condition>
            Identifier publishIf(const Condition &condition, bool (Condition::*method)() const, const std::string &address, C *that, T (C::*getter)()) {
                ParameterRef p = ParameterRef(new GetterParameter<T, C, true>(*that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConstConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename C, typename Condition>
            Identifier publishIf(const Condition &condition, bool (Condition::*method)() const, const std::string &address, C &that, T (C::*getter)()) {
                ParameterRef p = ParameterRef(new GetterParameter<T, C, true>(that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConstConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename C, typename Condition>
            Identifier publishIf(const Condition &condition, bool (Condition::*method)() const, const std::string &address, const C * const that, T (C::*getter)() const) {
                ParameterRef p = ParameterRef(new ConstGetterParameter<T, C, true>(*that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConstConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
            template <typename T, typename C, typename Condition>
            Identifier publishIf(const Condition &condition, bool (Condition::*method)() const, const std::string &address, const C &that, T (C::*getter)() const) {
                ParameterRef p = ParameterRef(new ConstGetterParameter<T, C, true>(that, getter));
                p->setCondition(std::shared_ptr<BasicCondition>(new ConstConditionMethod<Condition>(condition, method)));
                return publish(address, p);
            }
            
#pragma mark unpublish
            
            void unpublish(const std::string &address) {
                if(targets.find(address) == targets.end()) targets.erase(address);
            }
            
            void unpublish(const Identifier &identifier) {
                Targets::const_iterator it{findPublished(identifier)};
                if(it != targets.end()) {
                    targets.erase(it);
                }
            }
            
            void unpublish() {
                targets.clear();
            }

#pragma mark stop publish temporary
            
            void stopPublishTemporary(const std::string &address) {
                if(isPublished(address)) {
                    Targets::iterator it = targets.find(address);
                    for(std::size_t i = 0, size = targets.count(address); i < size; i++, ++it) {
                        it->second->setEnablePublish(false);
                    }
                }
            }
            
            void stopPublishTemporary(const Identifier &identifier) {
                Targets::const_iterator it{findPublished(identifier)};
                if(it != targets.end()) {
                    it->second->setEnablePublish(false);
                }
            }
            
            void resumePublish(const std::string &address) {
                if(isPublished(address)) {
                    Targets::iterator it = targets.find(address);
                    for(std::size_t i = 0, size = targets.count(address); i < size; i++, ++it) {
                        it->second->setEnablePublish(true);
                    }
                }
            }
            
            void resumePublishTemporary(const Identifier &identifier) {
                Targets::const_iterator it{findPublished(identifier)};
                if(it != targets.end()) {
                    it->second->setEnablePublish(true);
                }
            }
            
#pragma mark doRegister
            
            inline Identifier doRegister(const std::string &address, ParameterRef ref) {
                registeredTargets.insert(std::make_pair(address, ref));
                return {address, ref, destination};
            }
            
            template <typename T>
            Identifier doRegister(const std::string &address, T &value) {
                return doRegister(address, ParameterRef(new Parameter<T, false>(value)));
            }
            
            template <typename T>
            Identifier doRegister(const std::string &address, T (*getter)()) {
                return doRegister(address, ParameterRef(new GetterFunctionParameter<T, false>(getter)));
            }
            
            template <typename T, typename C>
            Identifier doRegister(const std::string &address, C *that, T (C::*getter)()) {
                return doRegister(address, ParameterRef(new GetterParameter<T, C, false>(*that, getter)));
            }
            
            template <typename T, typename C>
            Identifier doRegister(const std::string &address, C &that, T (C::*getter)()) {
                return doRegister(address, ParameterRef(new GetterParameter<T, C, false>(that, getter)));
            }
            
            template <typename T, typename C>
            Identifier doRegister(const std::string &address, const C * const that, T (C::*getter)() const) {
                return doRegister(address, ParameterRef(new ConstGetterParameter<T, C, false>(*that, getter)));
            }
            
            template <typename T, typename C>
            Identifier doRegister(const std::string &address, const C &that, T (C::*getter)() const) {
                return doRegister(address, ParameterRef(new ConstGetterParameter<T, C, false>(that, getter)));
            }
            
#pragma mark publishRegistered
            
            inline void publishRegistered(const std::string &address) {
                Targets::iterator it = registeredTargets.find(address);
                if(it == registeredTargets.end()) {
                    ofLogWarning("ofxPubSubOsc") << address << " is not registered.";
                }
                ofxOscMessage m;
                for(std::size_t i = 0, size = registeredTargets.count(address); i < size; i++, ++it) {
                    if(it->second->setMessage(m, it->first)) sender.sendMessage(m);
                    m.clear();
                }
            }
            
            inline void publishRegistered(const Identifier &identifier) {
                // TODO: implement
                Targets::const_iterator it{findRegistered(identifier)};
                if(it != registeredTargets.end()) {
                    ofxOscMessage m;
                    if(it->second->setMessage(m, it->first)) sender.sendMessage(m);
                    m.clear();
                }
            }
            
#pragma mark unregister
            
            inline void unregister(const std::string &address) {
                if(registeredTargets.find(address) == registeredTargets.end()) registeredTargets.erase(address);
            }
            
            inline void unregister(const Identifier &identifier) {
                // TODO: implement
                Targets::const_iterator it{findRegistered(identifier)};
                if(it != registeredTargets.end()) {
                    registeredTargets.erase(it);
                }
            }
            
            inline void unregister() {
                registeredTargets.clear();
            }
            
#pragma mark status
            
            inline bool isPublished() const {
                return !targets.empty();
            }
            
            inline bool isPublished(const Identifier &identifier) const {
                return isPublished() && (findPublished(identifier) != targets.end());
            }
            
            inline bool isPublished(const std::string &address) const {
                return isPublished() && (targets.find(address) != targets.end());
            }
            
            inline bool isEnabled(const std::string &address) const {
                // TODO: fix
                return isPublished(address) && targets.find(address)->second->isPublishNow();
            }
            
            inline bool isEnabled(const Identifier &identifier) const {
                Targets::const_iterator it{findPublished(identifier)};
                return (it != targets.end()) && it->second->isPublishNow();
            }
            
            inline bool isRegistered() const {
                return !registeredTargets.empty();
            }
            
            inline bool isRegistered(const std::string &address) const {
                return isRegistered() && (registeredTargets.find(address) != registeredTargets.end());
            }
            
            typedef std::shared_ptr<OscPublisher> Ref;
            
            static void setUseBundle(bool b) {
                bUseBundle = b;
            }
            
            static bool isUseBundle() {
                return bUseBundle;
            }
            
        private:
            OscPublisher(const Destination &destination)
            : destination(destination) {
                sender.setup(destination.ip, destination.port);
            }
            
            void update() {
                ofxOscMessage m;
                if(isUseBundle()) {
                    ofxOscBundle bundle;
                    for(Targets::iterator it = targets.begin(); it != targets.end(); ++it) {
                        if(it->second->setMessage(m, it->first)) bundle.addMessage(m);
                        m.clear();
                    }
                    if(bundle.getMessageCount()) sender.sendBundle(bundle);
                    bundle.clear();
                    return;
                }
                for(Targets::iterator it = targets.begin(); it != targets.end(); ++it) {
                    if(it->second->setMessage(m, it->first)) sender.sendMessage(m);
                    m.clear();
                }
                
            }
            
            Destination destination;
            ofxOscSender sender;
            Targets targets;
            Targets registeredTargets;
            static bool bUseBundle;
            friend class OscPublisherManager;
        };
        
        typedef std::map<Destination, OscPublisher::Ref> OscPublishers;
        
        static OscPublisherManager &getSharedInstance() {
            static OscPublisherManager *sharedInstance = new OscPublisherManager;
            return *sharedInstance;
        }
        
        static OscPublisher &getOscPublisher(const std::string &ip, int port) {
            OscPublishers &publishers = getSharedInstance().publishers;
            Destination destination(ip, port);
            if(publishers.find(destination) == publishers.end()) {
                publishers.insert(std::make_pair(destination, OscPublisher::Ref(new OscPublisher(destination))));
            }
            return *(publishers[destination].get());
        }
        
        void update(ofEventArgs &args) {
            for(OscPublishers::iterator it = publishers.begin(); it != publishers.end(); ++it) {
                it->second->update();
            }
        }
        OscPublisherManager() {
            ofAddListener(ofEvents().update, this, &OscPublisherManager::update, OF_EVENT_ORDER_AFTER_APP);
        }
        virtual ~OscPublisherManager() {
            ofRemoveListener(ofEvents().update, this, &OscPublisherManager::update, OF_EVENT_ORDER_AFTER_APP);
        }
        OscPublishers publishers;
        
#pragma mark iterator
    public:
        typedef OscPublishers::iterator iterator;
        typedef OscPublishers::const_iterator const_iterator;
        typedef OscPublishers::reverse_iterator reverse_iterator;
        typedef OscPublishers::const_reverse_iterator const_reverse_iterator;
        
        iterator begin() { return publishers.begin(); }
        iterator end() { return publishers.end(); }
        
        const_iterator begin() const { return publishers.begin(); }
        const_iterator end() const { return publishers.end(); }
        const_iterator cbegin() const { return publishers.begin(); }
        const_iterator cend() const { return publishers.end(); }
        
        reverse_iterator rbegin() { return publishers.rbegin(); }
        reverse_iterator rend() { return publishers.rend(); }
        
        const_reverse_iterator rbegin() const { return publishers.rbegin(); }
        const_reverse_iterator rend() const { return publishers.rend(); }
        const_reverse_iterator crbegin() const { return publishers.rbegin(); }
        const_reverse_iterator crend() const { return publishers.crend(); }
    };
    
    bool OscPublisherManager::OscPublisher::bUseBundle = false;
};
#undef type_ref

#pragma mark - syntax sugars

#pragma mark getter

typedef ofx::OscPublisherManager ofxOscPublisherManager;
typedef ofxOscPublisherManager::OscPublisher ofxOscPublisher;
typedef ofxOscPublisherManager::Identifier ofxOscPublisherIdentifier;

/// \brief get a OscPublisherManager.
/// \returns ofxOscPublisherManager

inline ofxOscPublisherManager &ofxGetOscPublisherManager() {
    return ofxOscPublisherManager::getSharedInstance();
}

/// \brief get a OscPublisher.
/// \param ip target ip is typed const std::string &
/// \param port target port is typed int
/// \returns ofxOscPublisher binded to ip & port

inline ofxOscPublisher &ofxGetOscPublisher(const std::string &ip, int port) {
    return ofxOscPublisherManager::getOscPublisher(ip, port);
}

#pragma mark publish

/// \name ofxPublishOsc
/// \{

/// \brief publish value as an OSC message with an address pattern address to ip:port every time the value has changed. 
/// If whenValueIsChanged is set to false, then the binded value is sent every frame after App::update.
/// template parameter T is suggested by value
/// \param ip target ip is typed const std::string &
/// \param port target port is typed int
/// \param address osc address is typed const std::string &
/// \param value reference of value is typed T &
/// \param whenValueIsChanged if this value to false, then we send value every update
/// \returns ofxOscPublisherIdentifier

template <typename T>
inline ofxOscPublisherIdentifier ofxPublishOsc(const std::string &ip, int port, const std::string &address, T &value, bool whenValueIsChanged = true) {
    return ofxGetOscPublisher(ip, port).publish(address, value, whenValueIsChanged);
}

// TODO: add document
template <typename T, typename U = typename std::enable_if<!std::is_pointer<T>::value>::type>
inline ofxOscPublisherIdentifier ofxPublishOsc(const std::string &ip, int port, const std::string &address, const T &value, bool whenValueIsChanged = true) {
    return ofxGetOscPublisher(ip, port).publish(address, value, whenValueIsChanged);
}

// TODO: add document
inline ofxOscPublisherIdentifier ofxPublishOsc(const std::string &ip, int port, const std::string &address, const char * const value, bool whenValueIsChanged = true) {
    return ofxGetOscPublisher(ip, port).publish(address, std::string(value), whenValueIsChanged);
}

/// \brief publish the value will be gave by function as an OSC message with an address pattern address to ip:port every time the value has changed.
///  If whenValueIsChanged is set to false, then the binded value is sent every frame after App::update.
/// template parameter T is suggested by value
/// \param ip target ip is typed const std::string &
/// \param port target port is typed int
/// \param address osc address is typed const std::string &
/// \param getter this function gives value, is typed T(*)()
/// \param whenValueIsChanged if this value to false, then we send value every update
/// \returns ofxOscPublisherIdentifier

template <typename T>
inline ofxOscPublisherIdentifier ofxPublishOsc(const std::string &ip, int port, const std::string &address, T (*getter)(), bool whenValueIsChanged = true) {
    return ofxGetOscPublisher(ip, port).publish(address, getter, whenValueIsChanged);
}

/// \brief publish the value will be gave by function as an OSC message with an address pattern address to ip:port every time the value has changed.
/// If whenValueIsChanged is set to false, then the binded value is sent every frame after App::update.
/// template parameter T is suggested by value and U is suggested by that and getter.
/// \param ip target ip is typed const std::string &
/// \param port target port is typed int
/// \param address osc address is typed const std::string &
/// \param that this object is typed U*, will bind with next parameter method. is called as (that->*getter)().
/// \param getter this method gives value, is typed T(C::*)()
/// \param whenValueIsChanged if this value to false, then we send value every update
/// \returns ofxOscPublisherIdentifier

template <typename T, typename C>
inline ofxOscPublisherIdentifier ofxPublishOsc(const std::string &ip, int port, const std::string &address, C *that, T (C::*getter)(), bool whenValueIsChanged = true) {
    return ofxGetOscPublisher(ip, port).publish(address, *that, getter, whenValueIsChanged);
{'fraction_non_alphanumeric': 0.10729600397737867, 'fraction_numerical': 0.0010564912062643714, 'mean_word_length': 1.8666825024495977, 'pattern_counts': {'":': 0, '<': 459, '<?xml version=': 0, '>': 518, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 13, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5682466', 'n_tokens_mistral': 25111, 'n_tokens_neox': 23994, 'n_words': 6458}