//=============================================================================

#include <algorithm>
#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/Color.h"
#include "cinder/gl/gl.h"
#include "cinder/Vector.h"
#include "Ship.h"
#include "ExplosionShip.h"

using namespace ci;
using namespace ci::app;
using namespace gl;
using namespace std;

//-----------------------------------------------------------------------------

class CinderApp : public App
{
public:
   static void prepareSettings(Settings* settings);
   void setup() override;
   void update() override;
   void draw() override;

   virtual void keyDown(KeyEvent event) override;
   virtual void keyUp(KeyEvent event) override;

private:
   void collisions();

   bool mKeyRight=false, mKeyLeft=false, mKeyUp=false, mKeyDown=false;
   bool mKeyFire = false;

   ShipPtr mShip;
   vector<TorpedoPtr> mTorpedos;
   vector<ExplosionPtr> mExplosions;
};

//-----------------------------------------------------------------------------

void CinderApp::prepareSettings(Settings* settings)
{
   settings->setTitle("Cinder 16 - Kollisionen - Friendly Fire");
   settings->setResizable(false);
   settings->setWindowPos(100, 100);
   settings->setWindowSize(640, 480);
   settings->setFrameRate(30.0f);
}

//-----------------------------------------------------------------------------

void CinderApp::setup()
{
   Figure::sScreenWidth = static_cast<float>(getWindowWidth());
   Figure::sScreenHeigth = static_cast<float>(getWindowHeight());

   mShip = make_shared<Ship>();
}

//-----------------------------------------------------------------------------

void CinderApp::update()
{
   if (mKeyUp && mShip)
   {
      mShip->accelerate(+1.f);
   }
   if (mKeyDown && mShip)
   {
      mShip->accelerate(-1.f);
   }
   if (mKeyRight && mShip)
   {
      mShip->rotate(+9.f);
   }
   if (mKeyLeft && mShip)
   {
      mShip->rotate(-9.f);
   }

   if (mKeyFire && mShip)
   {
      TorpedoPtr f = mShip->fire();
      mTorpedos.push_back(f);
   }

   if (mShip)
   {
      mShip->update();
   }
   auto eit = remove_if(begin(mTorpedos), end(mTorpedos), [](const TorpedoPtr& p) { return !p->update(); });
   mTorpedos.erase(eit, end(mTorpedos));
   auto eit2 = remove_if(begin(mExplosions), end(mExplosions), [](const ExplosionPtr& p) { return !p->update(); });
   mExplosions.erase(eit2, end(mExplosions));
   collisions();
}

//-----------------------------------------------------------------------------

void CinderApp::draw()
{
   clear(Color(0, 0, 0));

   if (mShip)
   {
      mShip->draw();
   }
   for_each(cbegin(mTorpedos), cend(mTorpedos), [](const TorpedoPtr& p) { p->draw(); });
   for_each(cbegin(mExplosions), cend(mExplosions), [](const ExplosionPtr& p) { p->draw(); });
}

//-----------------------------------------------------------------------------

void CinderApp::keyDown(KeyEvent event)
{
   switch (event.getCode())
   {
   case KeyEvent::KEY_RIGHT:
      mKeyRight = true;
      break;
   case KeyEvent::KEY_LEFT:
      mKeyLeft = true;
      break;
   case KeyEvent::KEY_UP:
      mKeyUp = true;
      break;
   case KeyEvent::KEY_DOWN:
      mKeyDown = true;
      break;
   case KeyEvent::KEY_x:
      mKeyFire = true;
      break;
   }
}

//-----------------------------------------------------------------------------

void CinderApp::keyUp(KeyEvent event)
{
   switch (event.getCode())
   {
   case KeyEvent::KEY_RIGHT:
      mKeyRight = false;
      break;
   case KeyEvent::KEY_LEFT:
      mKeyLeft = false;
      break;
   case KeyEvent::KEY_UP:
      mKeyUp = false;
      break;
   case KeyEvent::KEY_DOWN:
      mKeyDown = false;
      break;
   case KeyEvent::KEY_x:
      mKeyFire = false;
      break;
   }
}

//-----------------------------------------------------------------------------

void CinderApp::collisions()
{
   vector<TorpedoPtr> torpedoRemovables;
   vector<ExplosionPtr> explosionNews;
   bool removeShip = false;

   // Kollision zwischen Schiff und Torpedo ("friendly (self) fire")
   if (mShip)
   {
      for_each(cbegin(mTorpedos), cend(mTorpedos), [&](const TorpedoPtr& p)
      {
         if (distance(mShip->getLocation(), p->getLocation())<(Ship::Radius()+Torpedo::Radius()))
         {
            explosionNews.push_back(make_shared<ExplosionShip>(p->getLocation()));
            torpedoRemovables.push_back(p);
            removeShip = true;
         }
      });
   }

   // Ergebnisse der Kollisionen festschreiben
   // - Torpedos entfernen
   // - Neue Explosionen uebernehmen
   // - Schiff zerstoeren, wenn notwendig
   for_each(cbegin(torpedoRemovables), cend(torpedoRemovables), [this](const TorpedoPtr& p) 
   { 
      auto eit = remove(begin(mTorpedos), end(mTorpedos), p); 
      mTorpedos.erase(eit, end(mTorpedos));
   });
   copy(cbegin(explosionNews), cend(explosionNews), back_inserter(mExplosions));
   if (removeShip)
   {
      mShip.reset();
   }
}

//-----------------------------------------------------------------------------

CINDER_APP(CinderApp, RendererGl(), &CinderApp::prepareSettings)
