#pragma once
#include "SparQtGUI/mainwindow.h"
#include <SparObserver.h>
#include <SparAIModule.h>
#include <QtGui/QApplication>
#include <QtGui/QCloseEvent>
#include <QtCore/QThread>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>

// Should be event-driven instead of polling every x frames
// Should use signal/slot instead of events

namespace detail
{
  class SparQtGUIBase : public SparObserver
  {
  public:
    static const int DEFAULT_REFRESH_NB_FRAMES = 20;

    SparQtGUIBase(SparAIContainer* spar)
      : SparObserver(spar)
      , m_spar(spar)
      , m_application(NULL)
      , m_mainWindow(NULL)
      , m_threadGUI(this)
      , m_refreshNbFrames(DEFAULT_REFRESH_NB_FRAMES)
    {
    }

    virtual ~SparQtGUIBase()
    {
    }

    virtual void onStart();

    virtual void onEnd();

    virtual void onFrame();

  protected:
    virtual void _output(std::ostream& out) const
    {
      out << "SPAR GUI";
    }

    class QThreadGUI : public QThread
    {
    public:
      QThreadGUI(SparQtGUIBase* gui)
        : QThread(NULL)
        , m_gui(gui)
      {
      }

      void closeNow()
      {
        if (isRunning())
        {
          QApplication::postEvent(m_gui->m_application, new QCloseEvent());
        }
      }

      void run()
      {
        int argc = 0;
        {
          MainWindow::initialize();

          QApplication application(argc, NULL);
          MainWindow mainWindow(m_gui->m_selectedProcessesUnits, m_gui->m_selectedProcessesUnitsMutex);

          m_gui->m_application = &application;
          m_gui->m_mainWindow = &mainWindow;

          m_gui->m_GUIreadyCondition.notify_one();

          mainWindow.show();
          mainWindow.lower();
          application.exec();

          m_gui->m_application = NULL;
          m_gui->m_mainWindow = NULL;
        }
      }
    private:
      SparQtGUIBase* m_gui;
    };
    friend class QThreadGUI;

    SparAIContainer* const m_spar;
    int m_refreshNbFrames;

    boost::condition_variable m_GUIreadyCondition;
    boost::mutex m_GUIreadyMutex;

    boost::mutex m_selectedProcessesUnitsMutex;
    UnitsToSelect m_selectedProcessesUnits;

    // These pointers should actually be accessed via a mutex, but laziness has won the battle for now
    QApplication* m_application;
    MainWindow* m_mainWindow;

    QThreadGUI m_threadGUI;
  };
}

template <class S1, class S2, class S3, class S4, class D1, class D2, class D3, class D4>
class SparQtGUI : public detail::SparQtGUIBase
{
protected:
  typedef SparAIModule<S1,S2,S3,S4,D1,D2,D3,D4> Spar;

public:
  SparQtGUI(Spar* spar)
    : detail::SparQtGUIBase(spar)
    , m_spar(spar)
  {
  }

  virtual void onFrame()
  {
    // This callback takes place in the main StarCraft thread.
    // Data must be either copied or reference-counted since stuff may have been deleted once the
    // GUI thread gets a hold of it.

    if (!m_threadGUI.isRunning())
    {
      terminate(Success);
    }
    else
    {
      if (BWAPI::Broodwar->getFrameCount() % m_refreshNbFrames == 0)
      {
        QApplication::postEvent(m_mainWindow, new UpdateLayer2Event(m_spar->getD2()));
        QApplication::postEvent(m_mainWindow, new UpdateLayer3Event(m_spar->getD3()));
      }

      detail::SparQtGUIBase::onFrame();
    }
  }

protected:
  Spar* const m_spar;
};