#pragma once
#include "../Component.h"
#include "../Event.h"
#include "../Logger.h"
#include "../SafeList.h"
#include "../SafeListAdaptor.h"
#include "../Utils/Utils.h"
#include "../Utils/Functors.h"
#include <boost/iterator/transform_iterator.hpp>
#include <cassert>
#include <iostream>
#include <algorithm>

class UnitRecord;
template <class Process, class Handler> class ProcessContainer;
template <class Process, class Handler> class ProcessContainerList;

/**
 * @brief A process is anything that can be executed and terminated, and whose state changes over time.
 */
class Process : public virtual Component
{
protected:
  template <class Process, class Handler> friend class ProcessContainer;
  template <class Process, class Handler> friend class ProcessContainerList;

  /**
   * @brief Base class for the type of modes the process may be in.
   */
  class Mode
  {
  public:
    Mode(bool initialMode, bool executionMode, bool terminationMode)
      : m_initialMode(initialMode)
      , m_executionMode(executionMode)
      , m_terminationMode(terminationMode)
    {}

    bool isInitialMode() const { return m_initialMode; }
    bool isExecutionMode() const { return m_executionMode; }
    bool isTerminationMode() const { return m_terminationMode; }

    friend bool operator== (const Mode& mode1, const Mode& mode2) { return &mode1 == &mode2; }
    friend bool operator!= (const Mode& mode1, const Mode& mode2) { return &mode1 != &mode2; }
  
  private:
    Mode() {}
    bool m_initialMode;
    bool m_executionMode;
    bool m_terminationMode;
  };

  /**
   * @brief Initial mode is a type of mode in which a process is waiting to execute.
   */
  class InitialMode : public Mode
  {
  public:
    InitialMode()
      : Mode(true, false, false)
    {}
  };

  /**
   * @brief Execution mode is a type of mode in which a process is being executed.
   */
  class ExecutionMode : public Mode
  {
  public:
    ExecutionMode()
      : Mode(false, true, false)
    {}
  };

  /**
   * @brief Termination mode is a type of mode in which a process is terminated.
   */
  class TerminationMode : public Mode
  {
  public:
    TerminationMode()
      : Mode(false, false, true)
    {}
  };
private:
  struct ProcessWrapper;
  struct extract_process_const;
  template <class Function>
  struct ProcessFunction : std::unary_function<Process*, SafeListBase::IterationStep>
  {
    ProcessFunction(Function function) : m_function(function)
    {}
    SafeListBase::IterationStep operator()(Process* process) const
    {
      return m_function(process);
    }
    Function m_function;
  };
public:
  /**
   * @brief Creates a new process.
   * @param parent The parent of this process, or NULL if the process is actually the SPAR instance.
   */
  Process(const Process* parent)
    : m_id(s_id++)
    , m_currentMode(&WaitingToExecute)
    , m_parent(parent)
    //, m_childProcessTerminatedEventHandler(*this)
    , m_startEvent(*this)
    , m_terminateEvent(*this)
    , m_destroyEvent(*this)
    , m_useCount(0)
    , m_zombifyOnTerminate(false)
    //, m_zombified(false)
  {
    if (m_parent != NULL)
      m_positionInParent = m_parent->_onNewChildProcess(this);

    if (m_parent != NULL && m_parent->isTerminated() /*&& !m_zombified*/)
      m_currentMode = &ExternalEvent;
    else
      addUser();
  }

  /**
   * Process' destructor.
   */
  virtual ~Process();

  /**
   * @return The unique ID associated with this process (the lower the ID, the older the process).
   */
  size_t getID() const
  {
    return m_id;
  }

  void addUser() const;

  void removeUser() const;

  /**
   * @brief Schedules the process for zombification when it terminates.
   */
  void zombifyOnTerminate()
  {
    m_zombifyOnTerminate = true;

    if (m_useCount == 0)
      Component::addZombieComponent(this);
  }

  /**
   * @return The parent of this process.
   */
  const Process* getParent() const
  {
    return m_parent;
  }

  /**
   * @return The initial ancestor of this process, or the process itself if it has no ancestor.
   */
  const Process* getInitialAncestor() const
  {
    const Process* ancestor = this;
    while (ancestor->getParent() != NULL)
      ancestor = ancestor->getParent();
    return ancestor;
  }

  /**
   * @return The mode the process is currently in.
   */
  const Mode& getCurrentMode() const
  {
    return *m_currentMode;
  }

  /**
   * @brief Returns the mode the process terminated in. Only defined if the process is actually terminated.
   * @return The mode the process terminated in.
   */
  const TerminationMode& getTerminationMode() const
  {
    assert(isTerminated());
    return *static_cast<const TerminationMode*>(m_currentMode);
  }

  // Either "waiting to start", "running", or "terminated" (with several possible states when running or terminated)

  /**
   * @return Whether the process is currently waiting to be executed.
   */
  bool isWaitingToExecute() const
  {
    return m_currentMode->isInitialMode();
  }
  /**
   * @return Whether the process is currently executing.
   */
  bool isExecuting() const
  {
    return m_currentMode->isExecutionMode();
  }
  /**
   * @return Whether the process is currently terminated.
   */
  bool isTerminated() const
  {
    return m_currentMode->isTerminationMode();
  }

  /**
   * @brief Executes the process, which is assumed to not having started yet.
   *        Note that if its parent has terminated the process won't be executed, which is typically a useful
   *        restriction when the game ends.
   *        Conversely if its parent has not started yet the process' execution will be deferred to when its
   *        parent actually starts.
   *        Also note that although start() cannot be reimplemented, onStart() can.
   */
  void start()
  {
    struct StartIfWaitingForParent
    {
      typedef SafeListBase::IterationStep result_type;

      SafeListBase::IterationStep operator()(Process* process) const
      {
        if (&process->getCurrentMode() == &WaitingForParentToExecute)
          process->start();
        return SafeListBase::IterationStep();
      }
    };

    //assert(isWaitingToExecute()); // Can also be a process that terminated right away due to its parent being terminated as well

    // Can't execute a process if parent process has terminated, which typically happens when the game has ended
    // but decision making modules still try to execute stuff.
    if (m_parent == NULL || m_parent->isExecuting())
    {
      m_currentMode = &Executing;

      onStart();

      m_startEvent.raise();

      reverseForEachChildProcess(StartIfWaitingForParent());
    }
    // If parent hasn't started yet, execute the process once parent starts up.
    else if (m_parent->isWaitingToExecute())
    {
      m_currentMode = &WaitingForParentToExecute;
    }
  }

  /**
   * @brief Forces the termination of this process via an external event.
   *        It is up to the derived class implementation to gracefully handle this type of termination.
   */
  void externalTerminate()
  {
    terminate(ExternalEvent);
  }

  /**
   * @brief The OnStart event is raised when the process starts.
   */
  DECLARE_EVENT(OnStart);

  /**
   * @return The OnStart event associated to this process.
   */
  OnStart& getStartEvent() const { return m_startEvent; }

  /**
   * @brief The OnTerminate event is raised when the process terminates, before its children have been forced to do so.
   */
  DECLARE_EVENT(OnTerminate);

  /**
   * @return The OnTerminate event associated to this process.
   */
  OnTerminate& getTerminateEvent() const { return m_terminateEvent; }

  /**
   * @return The number of child processes, either waiting to start or in execution, of this process.
   *         Terminated processes are not included.
   */
  size_t getNbChildProcesses() const
  {
    return m_childProcesses.size();
  }

  template <class Function>
  Function forEachChildProcess(Function function)
  {
    //ProcessFunction functionTraits(function);
    //composite_functor<ProcessFunction, extract_process> composite(functionTraits);
    //composite_functor<ProcessFunction, extract_process> composite2 = m_childProcesses.for_each(composite);
    typedef ProcessFunction<Function> ProcessFunction;
    return m_childProcesses.for_each(composite_functor<ProcessFunction, extract_process>(ProcessFunction(function))).m_f1.m_function;
  }

  template <class Function>
  Function reverseForEachChildProcess(Function function)
  {
    typedef ProcessFunction<Function> ProcessFunction;
    return m_childProcesses.reverse_for_each(composite_functor<ProcessFunction, extract_process>(ProcessFunction(function))).m_f1.m_function;
  }

  /**
   * @brief Process has not started yet.
   */
  static const InitialMode WaitingToExecute;
  /**
   * @brief Process has been told to start but is still waiting for its parent to do so.
   */
  static const InitialMode WaitingForParentToExecute;
  /**
   * @brief Process is running.
   */
  static const ExecutionMode Executing;
  /**
   * @brief Regular termination.
   */
  static const TerminationMode Success;
  /**
   * @brief An external event caused this process to terminate.
   */
  static const TerminationMode ExternalEvent;

  /////////
  // GUI //
  /////////
  typedef boost::transform_iterator<extract_process_const, ConstSafeListAdaptor<ProcessWrapper>::const_iterator> ProcessIterator;
  std::pair<ProcessIterator, ProcessIterator> getCurrentChildProcesses() const
  {
    ConstSafeListAdaptor<ProcessWrapper> adaptor(m_childProcesses);
    return std::make_pair(ProcessIterator(adaptor.unsafe_begin()), ProcessIterator(adaptor.unsafe_end()));
  }
  class UnitRecordGroup
  {
  public:
    UnitRecordGroup() {}
    template <class iterator>
    UnitRecordGroup(iterator begin, iterator end)
    {
      insert(begin, end);
    }
    void insert(BWAPI::Unit* unit);
    void insert(UnitRecord* unitRecord)
    {
      m_group.insert(unitRecord);
    }
    template <class iterator>
    void insert(iterator begin, iterator end)
    {
      for (; begin != end; ++begin)
        insert(*begin);
    }
    typedef std::set<UnitRecord*>::const_iterator const_iterator;
    const_iterator begin() const { return m_group.begin(); }
    const_iterator end() const { return m_group.end(); }
  protected:
    std::set<UnitRecord*> m_group;
  };
  /**
   * @brief Returns the units involved with this process. For display only.
   * @return The set of units involved with this process.
   */
  virtual UnitRecordGroup _getUnitsInvolved() const
  { 
    return UnitRecordGroup();
  }

protected:
  struct TerminateProcess
  {
    typedef SafeListBase::IterationStep result_type;

    SafeListBase::IterationStep operator()(Process* process) const
    {
      process->externalTerminate();

      return SafeListBase::IterationStep();
    }
  };

  DECLARE_EVENT(OnDestroy);

  OnDestroy& getDestroyEvent() const { return m_destroyEvent; }

  /**
   * @brief Changes the process' execution mode, which is only possible when the process is actually executing.
   * @param newMode The new execution mode.
   */
  void changeMode(const ExecutionMode& newMode)
  {
    assert(isExecuting());

    m_currentMode = &newMode;
  }

  /**
   * @brief Terminates this process. This method is either called externally from externalTerminate()
   *        or internally by the process iself.
   *        Note that although terminate() cannot be reimplemented, onEnd() can.
   */
  void terminate(const TerminationMode& mode);

  /**
   * @brief Called when a process starts. To be implemented in derived classes if necessary.
   */
  virtual void onStart() {}

  /**
   * @brief Called when a process terminates. To be implemented in derived classes if necessary.
   */
  virtual void onEnd() {}

  /**
   * @brief This function is called when a sub-process has just been created. The sub-process is added to the list
   *        of sub-processes.
   * @param process The newly created sub-process.
   */
  SafeList<ProcessWrapper>::iterator _onNewChildProcess(Process* process) const
  {
    // No duplicates
    assert(std::find(ConstSafeListAdaptor<ProcessWrapper>(m_childProcesses).unsafe_begin(),
                     ConstSafeListAdaptor<ProcessWrapper>(m_childProcesses).unsafe_end(),
                     ProcessWrapper(process)) == ConstSafeListAdaptor<ProcessWrapper>(m_childProcesses).unsafe_end());

    SafeList<ProcessWrapper>::iterator pos = m_childProcesses.add(ProcessWrapper(process));
    ProcessWrapper& wrapper = *pos;
    wrapper.setPosInContainer(pos);
//    Process::OnTerminate::SubscriberID sid = process->getTerminateEvent().subscribe(&m_childProcessTerminatedEventHandler, &wrapper);
//#ifdef _DEBUG_COMPONENT
//    wrapper.m_processTerminationID = sid;
//#else
//    unused(sid);
//#endif
    return pos;
  }

  /**
   * @brief This function is called when a sub-process has been destroyed. The sub-process is removed from the list
   *        of sub-processes.
   * @param childPosition The position of the child in the list of children processes.
   */
  void _onChildProcessDestroyed(SafeList<ProcessWrapper>::iterator childPosition) const
  {
//    ProcessWrapper* wrapper = reinterpret_cast<ProcessWrapper*>(data);
//#ifdef _DEBUG_COMPONENT
//    wrapper->m_process->getTerminateEvent().unsubscribe(wrapper->m_processTerminationID);
//#endif
//    m_childProcesses.erase(wrapper->getPosInContainer());

    m_childProcesses.erase(childPosition);
  }

private:
  struct ProcessWrapperBase
  {
    ProcessWrapperBase(Process* process)
      : m_process(process)
    { }

    bool operator==(const ProcessWrapper& other) const
    {
      return m_process == other.m_process;
    }

    Process* const m_process;

#ifdef _DEBUG_COMPONENT
    /**
     * @brief The subscriber ID of the sub-process for the OnTerminate event
     */
    //Process::OnTerminate::SubscriberID m_processTerminationID;
#endif
  private:
    //const member prevent the use of the assigment operator
    ProcessWrapperBase& operator=(const ProcessWrapperBase&);
  };

  struct ProcessWrapper : public ProcessWrapperBase
  {
    ProcessWrapper(Process* process)
      : ProcessWrapperBase(process)
    { }

    void setPosInContainer(SafeList<ProcessWrapper>::iterator posInContainer)
    {
      m_posInContainer = reinterpret_cast<SafeList<ProcessWrapperBase>::iterator&>(posInContainer);
    }

    SafeList<ProcessWrapper>::iterator getPosInContainer()
    {
      return reinterpret_cast<SafeList<ProcessWrapper>::iterator&>(m_posInContainer);
    }

    /**
     * @brief The position of the sub-process in the sub-processes list.
     */
    SafeList<ProcessWrapperBase>::iterator m_posInContainer; // Pointer is required to reference SafeList<ProcessWrapper>::iterator type

  private:
    //ProcessWrapperBase cannot be assigned to
    ProcessWrapper& operator= (const ProcessWrapper&);
  };
  struct extract_process : std::unary_function<ProcessWrapper&,Process*>
  {
    Process* operator()(ProcessWrapper& wrapper) const { return wrapper.m_process; }
  };
  struct extract_process_const : std::unary_function<const ProcessWrapper&,const Process*>
  {
    const Process* operator()(const ProcessWrapper& wrapper) const { return wrapper.m_process; }
  };

protected:
  /**
   * @brief The ID of this process.
   */
  const size_t m_id;
  /**
   * @brief The parent of this process.
   */
  const Process* const m_parent;

private:
  SafeList<ProcessWrapper>::iterator m_positionInParent;

  /**
   * @brief The current mode of this process; should never be NULL.
   */
  const Mode* m_currentMode;
  /**
   * @brief The OnStart event.
   */
  EVENT(OnStart) m_startEvent;
  /**
   * @brief The OnTerminate event.
   */
  EVENT(OnTerminate) m_terminateEvent;

  EVENT(OnDestroy) m_destroyEvent;
  /**
   * @brief The handler used to deal with sub-process termination events.
   */
  //mutable EVENT_HANDLER(Process, _onChildProcessTerminated) m_childProcessTerminatedEventHandler;
  /**
   * @brief The list of current sub-processes.
   */
  mutable SafeList<ProcessWrapper> m_childProcesses;

  mutable unsigned int m_useCount;

  /**
   * @brief Whether the process should be sent to the zombie list upon termination.
   */
  mutable bool m_zombifyOnTerminate;

  //bool m_zombified;
  /** 
   * @brief The static process ID counter.
   */
  static size_t s_id;

  Process& operator=(const Process&);
};
