#pragma once
#include "../../SparAIContainer.h"
#include "../Utils.h"
#include "../SafeList.h"
#include "Process.h"

namespace policies
{
  template<class ProcessType>
  struct DefaultProcessHandler
  {
    static void init(ProcessType * process)
    {
      process->zombifyOnTerminate();
    }
  };

  template<class ProcessType>
  struct NoOpProcessHandler
  {
    static void init(ProcessType * process) {}
  };
} // namespace policies

/**
 * @brief A process container wraps a process, setting the process pointer to NULL when the process terminates.
 *        Can also specify a handling policy with two functions (init and destroy) that specify how to handle processes when they are respectively added or removed.
          This default to "zombify on behavior" and "delete".
 * @tparam ProcessType The type of process, which should inherit from the base process class.
 * @tparam Handler A policy that specifies additional behavior when setting a process or destroying it
 */
template <class ProcessType, class Handler = policies::DefaultProcessHandler<ProcessType> >
class ProcessContainer : public EventHandler
{
public:
  ProcessContainer(const Component& owner)
    : EventHandler(owner)
    , m_process(NULL)
  {
  }
  ProcessContainer(const Component& owner, ProcessType* process)
    : EventHandler(owner)
    , m_process(NULL)
  {
    setProcess(process);
  }
  ~ProcessContainer()
  {
    if (m_process != NULL)
    {
      m_process->getTerminateEvent().unsubscribe(m_terminateId);
      m_process->getDestroyEvent().unsubscribe(m_destroyId);
    }
  }
  ProcessContainer& operator=(ProcessType* process)
  {
    setProcess(process);
    return *this;
  }
  ProcessType* getProcess() { return m_process; }
  const ProcessType* getProcess() const { return m_process; }
  operator ProcessType*() { return m_process; }
  operator const ProcessType*() const { return m_process; }
  ProcessType* operator->() { return m_process; }
  const ProcessType* operator->() const { return m_process; }
  ProcessType& operator*() { return *m_process; }
  const ProcessType& operator*() const { return *m_process; }
protected:
  void setProcess(ProcessType* process)
  {
    if (m_process != process)
    {
      if (m_process != NULL)
      {
        m_process->getTerminateEvent().unsubscribe(m_terminateId);
        m_process->getDestroyEvent().unsubscribe(m_destroyId);
      }
      m_process = process;
      if (m_process != NULL)
      {
        m_terminateId = m_process->getTerminateEvent().subscribeDelayed(this, m_process);
        m_destroyId = m_process->getDestroyEvent().subscribeDelayed(this, m_process);

        Handler::init(m_process);
      }
    }
  }
  virtual void onEvent(void* data, boost::tuple<>&)
  {
    const ProcessType* process = reinterpret_cast<const ProcessType*>(data);
    if (m_process == process)
    {
      // m_process just ended
      m_process = NULL;

      process->getTerminateEvent().unsubscribe(m_terminateId);
      process->getDestroyEvent().unsubscribe(m_destroyId);
    }
  }
  virtual void _output(std::ostream& out) const
  {
    out << "Process container(" << extractSimpleClassName(typeid(ProcessType)) << ")";
  }
  ProcessType* m_process;
  Process::OnTerminate::SubscriberID m_terminateId;
  Process::OnDestroy::SubscriberID m_destroyId;
};

namespace policies
{ 
  template<class ProcessType>
  struct DefaultProcessListHandler
  {
    static void init(ProcessType * process)
    {
      process->zombifyOnTerminate();
    }
  };
  template<class ProcessType>
  struct NoOpProcessListHandler
  {
    static void init(ProcessType * process) {}
  };
}

/**
 * @brief A process container list wraps a list of processes, removing processes from the list when they terminate.
 *        Additionally, if ZombifyOnTerminate is true processes stored in the process list are automatically scheduled 
 *        for zombification when they are put in the container.
 * @tparam ProcessType The type of process, which should inherit from the base process class.
 * @tparam ZombifyOnTerminate Whether processes stored in the container should be zombified when they terminate.
 */
template <class ProcessType, class Handler = policies::DefaultProcessListHandler<ProcessType> >
class ProcessContainerList : public EventHandler
{
  friend struct policies::DefaultProcessListHandler<ProcessType>;
private:
  struct ProcessWrapperBase
  {
    ProcessWrapperBase(ProcessType* process)
      : m_process(process)
    {}
    ProcessType* m_process;
    Process::OnTerminate::SubscriberID m_terminateId;
    Process::OnDestroy::SubscriberID m_destroyId;
  };
  struct ProcessWrapper : public ProcessWrapperBase
  {
    ProcessWrapper(ProcessType* process)
      : ProcessWrapperBase(process)
    {}
    void setPosInContainer(typename SafeList<ProcessWrapper>::iterator posInContainer)
    {
      m_posInContainer = posInContainer;
    }
    typename SafeList<ProcessWrapper>::iterator getPosInContainer()
    {
      return m_posInContainer;
    }

    typename SafeList<ProcessWrapper>::iterator m_posInContainer;
  };

public:
  ProcessContainerList(const Component& owner)
    : EventHandler(owner)
  {
  }
  ~ProcessContainerList()
  {
    struct UnregisterFromProcess
    {
      SafeListBase::IterationStep operator()(ProcessWrapper& processWrapper) const
      {
        processWrapper.m_process->getTerminateEvent().unsubscribe(processWrapper.m_terminateId);
        processWrapper.m_process->getDestroyEvent().unsubscribe(processWrapper.m_destroyId);
        return SafeListBase::IterationStep();
      }
    };
    m_processes.for_each(UnregisterFromProcess());
  }
  void add(ProcessType* process)
  {
    assert(process != NULL);

    SafeList<ProcessWrapper>::iterator it = m_processes.add(ProcessWrapper(process));
    ProcessWrapper& processWrapper = *it;
    processWrapper.setPosInContainer(it);
    processWrapper.m_terminateId = process->getTerminateEvent().subscribe(this, &processWrapper);
    processWrapper.m_destroyId = process->getDestroyEvent().subscribe(this, &processWrapper);
    Handler::init(process);
  }
  template <class Function>
  Function forEachProcess(Function function)
  {
    struct InnerFunction
    {
      InnerFunction(Function function)
        : m_function(function)
      {}
      SafeListBase::IterationStep operator()(ProcessWrapper& processWrapper)
      {
        return m_function(processWrapper.m_process);
      }
      SafeListBase::ConstIterationStep operator()(const ProcessWrapper& processWrapper)
      {
        return m_function(processWrapper.m_process);
      }
      Function getResult() const
      {
        return m_function;
      }
    private:
      Function m_function;
    };
    return m_processes.for_each(InnerFunction(function)).getResult();
  }
  size_t size() const
  {
    return m_processes.size();
  }
  bool empty() const
  {
    return m_processes.empty();
  }
protected:
  virtual void onEvent(void* data, boost::tuple<>&)
  {
    ProcessWrapper* processWrapper = reinterpret_cast<ProcessWrapper*>(data);
    processWrapper->m_process->getTerminateEvent().unsubscribe(processWrapper->m_terminateId);
    processWrapper->m_process->getDestroyEvent().unsubscribe(processWrapper->m_destroyId);

    m_processes.erase(processWrapper->getPosInContainer());
  }

  virtual void _output(std::ostream& out) const
  {
    out << "Process container(" << extractSimpleClassName(typeid(ProcessType)) << ")";
  }

  SafeList<ProcessWrapper> m_processes;
};

