// ObjectResource.hh for Fluxbox
// Copyright (c) 2007 Henrik Kinnunen (fluxgen at fluxbox dot org)
//                and Simon Bowden    (rathnor at users.sourceforge.net)
// 
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

// $Id$

#ifndef OBJECTRESOURCE_HH
#define OBJECTRESOURCE_HH

/* This is a generic resource that can be used as an accessor to a value in an object.
   The constructors allow to select between:
     1a. giving an object of ObjectType, OR
     1b. giving a function returning an object of ObjectType

     2a. a function that sets a value
     2b. a function that toggles a value 
 */

template <typename ObjectType, typename ValueType>
class ObjectResource {
public:
    typedef ValueType (ObjectType::* getResourceType)() const;
    typedef void (ObjectType::* setResourceType)(ValueType);
    typedef void (ObjectType::* toggleResourceType)();
    typedef ObjectType* (*getResourceObject)();

    ObjectResource(ObjectType *object, getResourceType get, setResourceType set, ValueType a_default) :
        m_get(get), m_set(set), m_istoggle(false), m_object(object), 
        m_default(a_default), m_use_accessor(false) {
    }

    ObjectResource(ObjectType *object, getResourceType get, toggleResourceType toggle, ValueType a_default) :
        m_get(get), m_toggle(toggle), m_istoggle(true), m_object(object),
        m_default(a_default), m_use_accessor(false) {
    }

    ObjectResource(getResourceObject object_accessor, getResourceType get, setResourceType set, ValueType a_default) :
        m_get(get), m_set(set), m_istoggle(false), m_object_accessor(object_accessor),
        m_default(a_default), m_use_accessor(true) {
    }

    ObjectResource(getResourceObject object_accessor, getResourceType get, toggleResourceType toggle, ValueType a_default) :
        m_get(get), m_toggle(toggle), m_istoggle(true), m_object_accessor(object_accessor),
        m_default(a_default), m_use_accessor(true) {
    }
    
    ObjectResource<ObjectType, ValueType>& operator = (const ValueType newvalue) {
        ObjectType * obj = getObject();
        if (!obj)
            return *this;

        if (m_istoggle) {
            if (newvalue != (operator*)())
                (obj->*m_toggle)();
        } else {
            (obj->*m_set)(newvalue);
        }
        return *this;
    }

    ObjectResource<ObjectType, ValueType>& operator += (const ValueType newvalue) {
        ObjectType * obj = getObject();
        if (obj && !m_istoggle)
            (obj->*m_set)((operator*)()+newvalue);
        return *this;
    }

    ObjectResource<ObjectType, ValueType>& operator -= (const ValueType newvalue) {
        ObjectType * obj = getObject();
        if (obj && !m_istoggle)
            (obj->*m_set)((operator*)()-newvalue);
        return *this;
    }

    // this is a touch dirty, but it makes us compatible with FbTk::Resource<int> in IntResMenuItem 
    ObjectResource<ObjectType, ValueType>& get() {
        return *this;
    }

    ValueType operator*() {
        ObjectType * obj = getObject();
        if (!obj)
            return m_default;

        return (obj->*m_get)();
    }

    const ValueType operator*() const {
        ObjectType * obj = getObject();
        if (!obj)
            return m_default;

        return (obj->*m_get)();
    }

private:
    // choose one get and one set function

    ObjectType * getObject() {
        if (m_use_accessor) 
            return (*m_object_accessor)();
        else
            return m_object;
    }

    getResourceType m_get;

    union {
        setResourceType m_set;
        toggleResourceType m_toggle;
    };

    bool m_istoggle;

    union {
        ObjectType *m_object;
        getResourceObject m_object_accessor;
    };

    // default is only used when object isn't set (saves crashes)
    ValueType m_default; 

    bool m_use_accessor;
};


#endif // OBJECTRESOURCE_HH