#ifndef _SIGNAL_H_
#define _SIGNAL_H_
//----------------------------------------------------------------------------
///
/// \file   signal.h
///
/// \brief  trivial signal handling
///
///         See implementation files for further information
///
/// \date   20170127
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//----------------------------------------------------------------------------


//---Includes------------------------------------------------------------------


//---General--------------------------

#include <stdint.h>
#include <lepto/list.h>

//---Declarations--------------------------------------------------------------


template <typename sigReturn, typename ... sigTypes>
class CFunctor
{
   public:
      virtual sigReturn emitSignal( sigTypes ... args ) const = 0;
};


template <typename sigReturn, typename ... sigTypes>
class CFunctorFunction: public CFunctor<sigReturn, sigTypes...>
{

      sigReturn (*functionPtr)( sigTypes ... args );
   public:
      CFunctorFunction( sigReturn (*_functionPtr)( sigTypes ... args ))
      {
         functionPtr=_functionPtr;
      }

      virtual sigReturn emitSignal( sigTypes ... args ) const
      {
         return( functionPtr(args...) );
      }
};


template <typename sigReturn, class slotClass, typename ... sigTypes>
class CFunctorMethod:public CFunctor<sigReturn, sigTypes...>
{
   public:
      slotClass *slotObject;
      sigReturn (slotClass::*methodPtr)( sigTypes ... args );

      constexpr CFunctorMethod( slotClass *_slotObject, sigReturn (slotClass::*_methodPtr)( sigTypes ... args ))
      {
         methodPtr=_methodPtr;
         slotObject=_slotObject;
      }
      virtual sigReturn emitSignal( sigTypes ... args ) const
      {
         return( (slotObject->*(this->methodPtr))( args... ) );
      }
};

#if 1

template <typename sigReturn, class slotClass, typename ... sigTypes>
class CSignalMethod:public CFunctor<sigReturn, sigTypes...>
{
   public:
      //ret (*func)( types ... args );
      slotClass *slotObject;
      sigReturn (slotClass::*methodPtr)( sigTypes ... args );

      CSignalMethod( )
         :slotObject(nullptr)
         ,methodPtr(nullptr)
      {}
      //CFunctorMethod( slotClass *_slotObject, sigReturn (slotClass::*_methodPtr)( sigTypes ... args ))
      void connect( slotClass *_slotObject, sigReturn (slotClass::*_methodPtr)( sigTypes ... args ))
      {
         methodPtr=_methodPtr;
         slotObject=_slotObject;
      }
      virtual sigReturn emitSignal( sigTypes ... args )
      {
         return( (slotObject->*(this->methodPtr))( args... ) );
      }
};

#endif

class CPendingSignal;

#if 1
// Size is 4 Bytes
template <typename sigReturn, typename ... sigTypes>
class CSignal
{
   private:

      const CFunctor<sigReturn, sigTypes...> *functor;

   public:

      constexpr CSignal()
         :functor{0}
      {
      };
      ~CSignal()
      {
      }
      void connect( sigReturn (*_func)( sigTypes ... args ) )
      {
         functor=new CFunctorFunction<sigReturn, sigTypes...>(_func);
      };

      // An connection 'costs' 24 Bytes of RAM
      template <class slotClass >
      void connect( slotClass *slotObject, sigReturn (slotClass::*_methodPtr)( sigTypes ... args ))
      {
         functor=new CFunctorMethod<sigReturn, slotClass, sigTypes...>(slotObject, _methodPtr);
      };

      // An connection 'costs' 16 Bytes of RAM;
      void connectCaller( const CFunctor<sigReturn, sigTypes...> *fp )
      {
         functor=fp;
      };

      sigReturn emitSignal( sigTypes ... args ) const
      {
         if(functor)
         {
            return( functor->emitSignal( args ... ) );
         }

         return( (sigReturn)0 );
      };
      const CSignal<sigReturn, sigTypes...> *operator =(const CFunctor<sigReturn, sigTypes...> *_functor)
      {
         functor=_functor;
         return(this);
      }
      CPendingSignal* createPendingSignal(sigTypes ... args)
      {
         return( XcreatePendingSignal(*this, args...) );
         //return( new CPendingSignal2< sigTypes... >( *this, args...) );
      }



};
#endif


#if 0

class CPendingSignalBase
{
public:
   virtual void shot()=0;
   virtual ~CPendingSignalBase() {};
};


template <typename sigReturn, typename sigType1>
class CPendingSignal: public CPendingSignalBase
{
   CSignalMethod<sigReturn, sigType1> &m_signal;
   sigType1 m_storedArg;

   public:
      CPendingSignal( CSignal<sigReturn, sigType1> &signal, sigType1 arg )
         :m_signal(signal)
         ,m_storedArg( arg )
      {
      };
      virtual void shot() // overload;
      {
         m_signal.emitSignal( m_storedArg );
      };
};

template <typename sigReturn>
class CPendingSignal0:public  CPendingSignalBase
{
   CSignal<sigReturn> &m_signal;

   public:
      CPendingSignal0( CSignal<sigReturn> &signal )
         :m_signal(signal)
      {
      };
      virtual void shot() // overload
      {
         m_signal.emitSignal( );
      };
};

#endif

extern "C++"
{
   #if 1
   //void queueSignal(CPendingSignalBase *signal);
   //void handlePendingSignals();
   #endif
}

//---fin.----------------------------------------------------------------------
#endif // ! ? _SIGNAL_H_
