#pragma once
#include "../QTreeWidgetItemCustom.h"
#include "Unit.h"
#include <QtCore/QString>
#include <Utils/Process/Process.h>
#include <DecisionMaking/Layer1Reactive/Layer1FSM/Behaviors/Behavior.h>
#include <DecisionMaking/Layer2Actions/Interface/Actions/TacticalAction.h>

class ProcessWrapper;
struct CompareProcessWrapper
{
  inline bool operator()(const ProcessWrapper* wrapper1, const ProcessWrapper* wrapper2) const;
};

class ProcessWrapper
{
public:
  class QTreeWidgetItemProcess : public QTreeWidgetItemCustom<size_t>
  {
  public:
    QTreeWidgetItemProcess(QTreeWidget *view, const void* key) : QTreeWidgetItemCustom<size_t>(view, key) {}
    QTreeWidgetItemProcess(QTreeWidgetItem *item, const void* key) : QTreeWidgetItemCustom<size_t>(item, key) {}
    virtual void addUnits(UnitsToSelect &units) const
    {
      units.insertProcess(m_data);
    }
  };

  ProcessWrapper(const Process* process)
    : m_key(process)
    , m_name(QString::fromStdString(toString(process)))
    , m_id(process->getID())
  {
    Process::UnitRecordGroup unitsInvolved = process->_getUnitsInvolved();
    for (Process::UnitRecordGroup::const_iterator it = unitsInvolved.begin(); it != unitsInvolved.end(); ++it)
    {
      m_unitsInvolved.insert(UnitRecordWrapper(*it));
    }

    std::pair<Process::ProcessIterator, Process::ProcessIterator> childProcesses = process->getCurrentChildProcesses();
    for (Process::ProcessIterator it = childProcesses.first; it != childProcesses.second; ++it)
    {
      const Process* childProcess = *it;
      if (childProcess->isExecuting())
      {
        m_childProcesses.insert(createWrapper(childProcess));
      }
    }
  }

  virtual ~ProcessWrapper()
  {
    for (std::set<ProcessWrapper*, CompareProcessWrapper>::const_iterator it = m_childProcesses.begin(); it != m_childProcesses.end(); ++it)
    {
      delete *it;
    }
  }

  size_t getID() const
  {
    return m_id;
  }

  std::set<ProcessWrapper*, CompareProcessWrapper>& getChildProcesses()
  {
    return m_childProcesses;
  }

  virtual void display(QTreeWidget* parent, bool actionsOnly, bool displayUnits) const
  {
    displayT(parent, actionsOnly, displayUnits);
  }
  virtual void display(QTreeWidgetItem* parent, bool actionsOnly, bool displayUnits) const
  {
    displayT(parent, actionsOnly, displayUnits);
  }

  static ProcessWrapper* createWrapper(const Process* process);

protected:
  template <class Parent>
  void displayT(Parent* parent, bool actionsOnly, bool displayUnits) const
  {
    if (actionsOnly)
    {
      for (std::set<ProcessWrapper*, CompareProcessWrapper>::const_iterator it = m_childProcesses.begin(); it != m_childProcesses.end(); ++it)
      {
        const ProcessWrapper* childProcess = *it;
        childProcess->display(parent, actionsOnly, displayUnits);
      }
    }
    else
    {
      QTreeWidgetItemProcess* item = new QTreeWidgetItemProcess(parent, m_key);
      displayIn(item, false, displayUnits);
    }
  }

  void displayIn(QTreeWidgetItemProcess* item, bool actionsOnly, bool displayUnits) const;
  virtual void displayInImpl(QTreeWidgetItemProcess* item, bool actionsOnly, bool displayUnits) const;

  const size_t m_id;

  const void* const m_key;

  const QString m_name;

  std::set<UnitRecordWrapper> m_unitsInvolved;

  std::set<ProcessWrapper*, CompareProcessWrapper> m_childProcesses;
};

class BehaviorWrapper : public ProcessWrapper
{
public:
  BehaviorWrapper(const Behavior* behavior)
    : ProcessWrapper(behavior)
  {
    std::vector<const FSMState*> states = behavior->getAllStates();
    const FSMState* currentState = behavior->getCurrentState();
    for (std::vector<const FSMState*>::const_iterator it = states.begin(); it != states.end(); ++it)
    {
      std::list<BehaviorStateWrapper>::iterator itState = m_states.insert(m_states.end(), BehaviorStateWrapper(*it, (*it) == currentState));
    }
  }

protected:
  virtual void displayInImpl(QTreeWidgetItemProcess* item, bool actionsOnly, bool displayUnits) const;

  class BehaviorStateWrapper
  {
  public:
    BehaviorStateWrapper(const FSMState* state, bool isCurrentState)
      : m_name(QString::fromStdString(state->getName()))
      , m_key(state)
      , m_isCurrentState(isCurrentState)
    {
    }
    void displayIn(QTreeWidgetItemKey* item) const;
    bool isCurrentState() const { return m_isCurrentState; }
  protected:
    const QString m_name;
    const void* const m_key;
    bool m_isCurrentState;
  };

  std::list<BehaviorStateWrapper> m_states;
};

class TacticalActionWrapper : public ProcessWrapper
{
public:
  TacticalActionWrapper(const TacticalAction* tacticalAction)
    : ProcessWrapper(tacticalAction)
    , m_priority(tacticalAction->getPriority())
  {
  }
  size_t getPriority() const
  {
    return m_priority;
  }
  virtual void display(QTreeWidget* parent, bool actionsOnly, bool displayUnits) const
  {
    unused(actionsOnly);
    QTreeWidgetItemProcess* item = new QTreeWidgetItemProcess(parent, m_key);
    displayIn(item, false, displayUnits);
  }
  virtual void display(QTreeWidgetItem* parent, bool actionsOnly, bool displayUnits) const
  {
    unused(actionsOnly);
    QTreeWidgetItemProcess* item = new QTreeWidgetItemProcess(parent, m_key);
    displayIn(item, false, displayUnits);
  }
protected:
  virtual void displayInImpl(QTreeWidgetItemProcess* item, bool actionsOnly, bool displayUnits) const;

  const size_t m_priority;
};

bool CompareProcessWrapper::operator()(const ProcessWrapper* wrapper1, const ProcessWrapper* wrapper2) const
{
  const TacticalActionWrapper* twrapper1 = dynamic_cast<const TacticalActionWrapper*>(wrapper1);
  const TacticalActionWrapper* twrapper2 = dynamic_cast<const TacticalActionWrapper*>(wrapper2);
  if (twrapper1 != NULL)
  {
    if (twrapper2 != NULL)
    {
      if (twrapper1->getPriority() > twrapper2->getPriority())
        return true;
      else if (twrapper1->getPriority() < twrapper2->getPriority())
        return false;
      else
        return wrapper1->getID() < wrapper2->getID();
    }
    else
    {
      return true;
    }
  }
  else
  {
    if (twrapper2 != NULL)
    {
      return false;
    }
    else
    {
      return wrapper1->getID() < wrapper2->getID();
    }
  }
}