/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2008 INRIA
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Authors: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
 */
#ifndef ATTRIBUTE_ACCESSOR_HELPER_H
#define ATTRIBUTE_ACCESSOR_HELPER_H

#include "attribute.h"

namespace ns3 {

/**
 * \ingroup AttributeHelper
 */
template <typename V, typename T1>
Ptr<const AttributeAccessor>
MakeAccessorHelper (T1 a1);

/**
 * \ingroup AttributeHelper
 */
template <typename V, typename T1, typename T2>
Ptr<const AttributeAccessor>
MakeAccessorHelper (T1 a1, T2 a2);

} // namespace ns3

/***************************************************************
 *        The implementation of the above functions.
 ***************************************************************/

#include "type-traits.h"

namespace ns3 {

template <typename T>
struct AccessorTrait
{
  typedef typename TypeTraits<typename TypeTraits<T>::ReferencedType>::NonConstType Result;
};

template <typename T, typename U>
class AccessorHelper : public AttributeAccessor
{
public:
  AccessorHelper () {}

  virtual bool Set (ObjectBase * object, const AttributeValue & val) const {
    const U *value = dynamic_cast<const U *> (&val);
    if (value == 0)
      {
	return false;
      }
    T *obj = dynamic_cast<T *> (object);
    if (obj == 0)
      {
	return false;
      }
    return DoSet (obj, value);
  }

  virtual bool Get (const ObjectBase * object, AttributeValue &val) const {
    U *value = dynamic_cast<U *> (&val);
    if (value == 0)
      {
	return false;
      }
    const T *obj = dynamic_cast<const T *> (object);
    if (obj == 0)
      {
	return false;
      }
    return DoGet (obj, value);
  }

private:
  virtual bool DoSet (T *object, const U *v) const = 0;
  virtual bool DoGet (const T *object, U *v) const = 0;
};

template <typename V, typename T, typename U>
Ptr<const AttributeAccessor>
DoMakeAccessorHelperOne (U T::*memberVariable)
{
  class MemberVariable : public AccessorHelper<T,V>
    {
    public:
      MemberVariable (U T::*memberVariable)
	: AccessorHelper<T,V> (),
	m_memberVariable (memberVariable)
	{}
    private:
      virtual bool DoSet (T *object, const V *v) const {
        typename AccessorTrait<U>::Result tmp;
        bool ok = v->GetAccessor (tmp);
        if (!ok)
          {
            return false;
          }
	(object->*m_memberVariable) = tmp;
	return true;
      }
      virtual bool DoGet (const T *object, V *v) const {
	v->Set (object->*m_memberVariable);
	return true;
      }
      virtual bool HasGetter (void) const {
        return true;
      }
      virtual bool HasSetter (void) const {
        return true;
      }
      
      U T::*m_memberVariable;
    };
  return Ptr<const AttributeAccessor> (new MemberVariable (memberVariable), false);
}

template <typename V, typename T, typename U>
Ptr<const AttributeAccessor>
DoMakeAccessorHelperOne (U (T::*getter) (void) const)
{
  class MemberMethod : public AccessorHelper<T,V>
    {
    public:
      MemberMethod (U (T::*getter) (void) const)
	: AccessorHelper<T,V> (),
	m_getter (getter)
	{}
    private:
      virtual bool DoSet (T *object, const V *v) const {
	return false;
      }
      virtual bool DoGet (const T *object, V *v) const {
	v->Set ((object->*m_getter) ());
	return true;
      }
      virtual bool HasGetter (void) const {
        return true;
      }
      virtual bool HasSetter (void) const {
        return false;
      }
      U (T::*m_getter) (void) const;
    };
  return Ptr<const AttributeAccessor> (new MemberMethod (getter), false);
}


template <typename V, typename T, typename U>
Ptr<const AttributeAccessor>
DoMakeAccessorHelperOne (void (T::*setter) (U))
{
  class MemberMethod : public AccessorHelper<T,V>
    {
    public:
      MemberMethod (void (T::*setter) (U))
	: AccessorHelper<T,V> (),
	m_setter (setter)
	{}
    private:
      virtual bool DoSet (T *object, const V *v) const {
        typename AccessorTrait<U>::Result tmp;
        bool ok = v->GetAccessor (tmp);
        if (!ok)
          {
            return false;
          }
	(object->*m_setter) (tmp);
	return true;
      }
      virtual bool DoGet (const T *object, V *v) const {
	return false;
      }
      virtual bool HasGetter (void) const {
        return false;
      }
      virtual bool HasSetter (void) const {
        return true;
      }
      void (T::*m_setter) (U);
    };
  return Ptr<const AttributeAccessor> (new MemberMethod (setter), false);
}

template <typename W, typename T, typename U, typename V>
Ptr<const AttributeAccessor>
DoMakeAccessorHelperTwo (void (T::*setter) (U), 
			  V (T::*getter) (void) const)
{
  class MemberMethod : public AccessorHelper<T,W>
    {
    public:
      MemberMethod (void (T::*setter) (U), 
		    V (T::*getter) (void) const)
	: AccessorHelper<T,W> (),
	m_setter (setter),
	m_getter (getter)
	{}
    private:
      virtual bool DoSet (T *object, const W *v) const {
        typename AccessorTrait<U>::Result tmp;
        bool ok = v->GetAccessor (tmp);
        if (!ok)
          {
            return false;
          }
	(object->*m_setter) (tmp);
	return true;
      }
      virtual bool DoGet (const T *object, W *v) const {
	v->Set ((object->*m_getter) ());
	return true;
      }
      virtual bool HasGetter (void) const {
        return true;
      }
      virtual bool HasSetter (void) const {
        return true;
      }
      void (T::*m_setter) (U);
      V (T::*m_getter) (void) const;
    };
  return Ptr<const AttributeAccessor> (new MemberMethod (setter, getter), false);
}

template <typename W, typename T, typename U, typename V>
Ptr<const AttributeAccessor>
DoMakeAccessorHelperTwo (V (T::*getter) (void) const, 
			  void (T::*setter) (U))
{
  return DoMakeAccessorHelperTwo<W> (setter, getter);
}

template <typename W, typename T, typename U, typename V>
Ptr<const AttributeAccessor>
DoMakeAccessorHelperTwo (bool (T::*setter) (U), 
                         V (T::*getter) (void) const)
{
  class MemberMethod : public AccessorHelper<T,W>
    {
    public:
      MemberMethod (bool (T::*setter) (U), 
		    V (T::*getter) (void) const)
	: AccessorHelper<T,W> (),
	m_setter (setter),
	m_getter (getter)
	{}
    private:
      virtual bool DoSet (T *object, const W *v) const {
        typename AccessorTrait<U>::Result tmp;
        bool ok = v->GetAccessor (tmp);
        if (!ok)
          {
            return false;
          }
	ok = (object->*m_setter) (tmp);
        return ok;
      }
      virtual bool DoGet (const T *object, W *v) const {
	v->Set ((object->*m_getter) ());
	return true;
      }
      virtual bool HasGetter (void) const {
        return true;
      }
      virtual bool HasSetter (void) const {
        return true;
      }
      bool (T::*m_setter) (U);
      V (T::*m_getter) (void) const;
    };
  return Ptr<const AttributeAccessor> (new MemberMethod (setter, getter), false);
}

template <typename W, typename T, typename U, typename V>
Ptr<const AttributeAccessor>
DoMakeAccessorHelperTwo (bool (T::*getter) (void) const, 
                         void (T::*setter) (U))
{
  return DoMakeAccessorHelperTwo<W> (setter, getter);
}

template <typename V, typename T1>
Ptr<const AttributeAccessor>
MakeAccessorHelper (T1 a1)
{
  return DoMakeAccessorHelperOne<V> (a1);
}

template <typename V, typename T1, typename T2>
Ptr<const AttributeAccessor>
MakeAccessorHelper (T1 a1, T2 a2)
{
  return DoMakeAccessorHelperTwo<V> (a1, a2);
}

} // namespace ns3

#endif /* ATTRIBUTE_ACCESSOR_HELPER_H */
