#include "stdafx.h"
#include <math.h>

#include <vector>

#include "DataOutputStream.h"
#include "DataInputStream.h"

#include "PartikelSystem.h"

// MFC - Assists in finding memory leaks:
#ifdef MFC_DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

/****************************************************************************/
/** \brief Konstruktor
*****************************************************************************/
PartikelSystem::PartikelSystem() 
{
m_typ         = 0;
m_maxPartikel = 1;
m_respawn     = false;
m_lifeTime    = 10000;
m_spriteCount = 0;
//m_spriteID    = 0;
}

/****************************************************************************/
/** \brief Destruktor
*****************************************************************************/
PartikelSystem::~PartikelSystem()
{
clear();
}

/****************************************************************************/
/** \brief Entfernt alle Partikel aus dem System
*****************************************************************************/
void PartikelSystem::clear(void)
{
for (unsigned int i=0; i < m_partikelList.size(); i++)
   {
   Partikel* p = (Partikel*)m_partikelList[i];
   delete (p);
   }

m_partikelList.clear();
}

/******************************************************************************/
/** \brief Das Partikelsystem wird initialisiert
    \param spriteList Liste mit alle geladenen Sprites
*******************************************************************************/
void PartikelSystem::init(std::vector<Sprite*>& spriteList)
{
switch (m_typ) 
   { 
   case 0:  init_0(); break; // Testpartikel
   case 1:  init_1(); break;
   case 2:  init_2(); break; // schraeger Wurf
   case 3:  init_3(); break; // Feuer (animiert)
   case 4:  init_4(); break; // Lavafonatine
   case 5:  init_5(); break; // Kerze
   case 6:  init_6(); break;
   case 7:  init_7(); break;
   default: init_0(); break;
   }

for (unsigned int n=0; n < m_partikelList.size(); n++)
   {
   Partikel* p = (Partikel*)m_partikelList[n];
   for (unsigned int i = 0; i < (unsigned int)m_spriteCount; i++)
      {
      int sid = m_spriteID[i];
      Sprite* sprite = spriteList[sid];
      p->setSize((float)sprite->getWidth(), (float)sprite->getHeight()); // Alle Sprites zu eine Partikel muessen die gleiche Groessen haben
      p->addSprite(sprite);
      }
   }
}

/******************************************************************************/
/** \brief Es wird ein Partikel gerendert. keine Animation, keine Bewegung, keine Lifetime
*******************************************************************************/
void PartikelSystem::init_0(void)
{
Vec velocity, force;

velocity.set(0, 0, 0);
force.set(0,0, 0);

m_maxPartikel = 0;
for (unsigned int i=0; i < (unsigned int)m_maxPartikel; i++)
   {
   Partikel* p = new Partikel(m_zone, Partikel::NO_RESPAWN);
   p->setMotion(m_startPosOrg, velocity, force);
   p->setONtime(0);            
   m_partikelList.push_back(p);
   }
}

/******************************************************************************/
/** \brief Animation: Bewegt sich oszillierend in y-Richtung
*******************************************************************************/
void PartikelSystem::init_1(void)
{
Vec velocity, force;

velocity.set(0, 1, 0);
force.set(0, 0, 0);

m_maxPartikel = 0;
for (unsigned int i=0; i < (unsigned int)m_maxPartikel; i++)
   {
   Partikel* p = new Partikel(m_zone, Partikel::RESPAWN_REFLECTON);
   p->setMotion(m_startPosOrg, velocity, force);
   p->setONtime(1000);            // Lebensdauer des Partikels
   m_partikelList.push_back(p);
   }
}

/******************************************************************************/
/** \brief Animation: Schraeger Wurf
*******************************************************************************/
void PartikelSystem::init_2(void)
{
Vec velocity, force;

velocity.set(5, 5, 0);
force.set(0, 0.1f, 0);

m_maxPartikel = 1;
for (unsigned int i=0; i < (unsigned int)m_maxPartikel; i++)
   {
   Partikel* p = new Partikel(m_zone, Partikel::RESPAWN_AT_EMITTER);
   p->setMotion(m_startPosOrg, velocity, force);
   p->setONtime(10000);            // Lebensdauer des Partikels
   m_partikelList.push_back(p);
   }
}

/******************************************************************************/
/** \brief Feuer
*******************************************************************************/
void PartikelSystem::init_3(void)
{
Vec velocity, force;
m_maxPartikel = 1;

for (unsigned int i=0; i < (unsigned int)m_maxPartikel; i++)
   {
   Partikel* p = new Partikel(m_zone, Partikel::RESPAWN_AT_EMITTER);

   velocity.set(0,0,0);
   p->setMotion(m_startPosOrg, velocity, force);

   p->setONtime(0);            // Lebensdauer des Partikels
   p->setROLLtime(50);

   m_partikelList.push_back(p);
   }
}

/******************************************************************************/
/** \brief Lavafontaine
*******************************************************************************/
void PartikelSystem::init_4(void)
{
Vec force, randomMin, randomMax;
float speed;
m_maxPartikel = 10;

for (unsigned int i=0; i < (unsigned int)m_maxPartikel; i++)
   {
   Partikel* p = new Partikel(m_zone, Partikel::RESPAWN_AT_EMITTER);

   p->scaleEnable(false);
   
   force.set(0, 0.4f, 0);
   randomMin.set(-5,  80, -5);
   randomMax.set( 5, 100,  5);

   speed = 10;
   //p->setMotion(m_startPosOrg, velocity, force);
   p->setMotion(m_startPosOrg, randomMin, randomMax, speed, force);

   int lt = (int)getRandom(5000);
   p->setONtime(lt);            // Lebensdauer des Partikels

   m_partikelList.push_back(p);
   }
}

/******************************************************************************/
/** \brief Kerze
*******************************************************************************/
void PartikelSystem::init_5(void)
{
Vec velocity, force;

m_maxPartikel = 3;
for (unsigned int i=0; i < (unsigned int)m_maxPartikel; i++)
   {
   Partikel* p = new Partikel(m_zone, Partikel::RESPAWN_AT_EMITTER);

   velocity.set(0,1,0);
   velocity.normalize();
   velocity.mul(2);
   p->scaleEnable(true);
   p->setScale(0.5f);
   p->setMotion(m_startPosOrg, velocity, force);
   p->setFading(true);

   int lt = i * 250; 
   p->setOFFtime(lt);
   lt = 500;
   p->setONtime(lt);            // Lebensdauer des Partikels

   m_partikelList.push_back(p);
   }
}

/******************************************************************************/
/** \brief Rauch Dust
*******************************************************************************/
void PartikelSystem::init_6(void)
{
Vec force, randomMin, randomMax;
float speed;
m_maxPartikel = 5;
force.set(0,0,0);

for (unsigned int i=0; i < (unsigned int)m_maxPartikel; i++)
   {
   Partikel* p = new Partikel(m_zone, Partikel::RESPAWN_AT_EMITTER);
   p->setFading(true);
   
   randomMin.set(-5,  0, -5);
   randomMax.set( 5,  1,  5);

   speed = 1.5f;
   //p->setMotion(m_startPosOrg, velocity, force);
   p->setMotion(m_startPosOrg, randomMin, randomMax, speed, force);


   p->setOFFtime(i*2000);

   int lt = 10000; // (int)getRandom(5000);
   p->setONtime(lt);            // Lebensdauer des Partikels

   m_partikelList.push_back(p);
   }
}

/******************************************************************************/
/** \brief Animiert, Stationaeres Positionslicht
*******************************************************************************/
void PartikelSystem::init_7(void)
{
Vec velocity, force;
m_maxPartikel = 1;

for (unsigned int i=0; i < (unsigned int)m_maxPartikel; i++)
   {
   Partikel* p = new Partikel(m_zone, Partikel::RESPAWN_AT_EMITTER);

   velocity.set(0,0,0);
   p->setMotion(m_startPosOrg, velocity, force);

   p->setOFFtime(10000); 
   p->setONtime(0);            // Lebensdauer des Partikels
   p->setROLLtime(100);

   m_partikelList.push_back(p);
   }
}

/******************************************************************************/
/** \brief Animiert alle Partikel
*******************************************************************************/
void PartikelSystem::animate(void) 
{
for (unsigned int i=0; i < m_partikelList.size(); i++)
   {
   Partikel* p = (Partikel*)m_partikelList[i];
   p->animate();
   }
}

/******************************************************************************/
/** \brief Transformiert alle Partikel
    \param matTranslate Translationsmatrix
    \param matRotate Rotationsmatrix
*******************************************************************************/
void PartikelSystem::transform(Matrix& matTranslate, Matrix& matRotate)
  {
  m_startPos.set(m_startPosOrg);

  m_startPos.transform(matTranslate);
  m_startPos.transform(matRotate);

  for (unsigned int i=0; i < m_partikelList.size(); i++)
      {
      Partikel* p = (Partikel*)m_partikelList[i];
      p->transform(matTranslate, matRotate);
      }
  }

/******************************************************************************/
/** \brief Projiziert alle Partikel auf die 2D-NearPlane, Windowfenster
    \brief nearDist
    \brief farDist
*******************************************************************************/
void PartikelSystem::project(float nearDist, float farDist) 
{
   m_nearDist = nearDist;
   m_farDist  = farDist;

  for (unsigned int i=0; i < m_partikelList.size(); i++)
      {
      Partikel* p = (Partikel*)m_partikelList[i];
      p->project(nearDist, farDist);
      }
}

/******************************************************************************/
/** \brief liefert eine Zufallszahl
    \param max maximaler Wert der Zufallszahl
    \return Zufallszahl
*******************************************************************************/
float PartikelSystem::getRandom(int max)
{
 float r = (float)(rand() % max);
 return (r);
}

/****************************************************************************/
/** \brief Liest das Partikelsystem aus dem Inputstream
*   \param dataIn DataInputStream
*******************************************************************************/
void PartikelSystem::load(DataInputStream& dataIn) 
{
   int i=0;
   try
      {
      m_zone = dataIn.readInt();
      m_typ  = dataIn.readInt();
      m_startPosOrg.m_x = (float)dataIn.readDouble();
      m_startPosOrg.m_y = (float)dataIn.readDouble();
      m_startPosOrg.m_z = (float)dataIn.readDouble();
      m_spriteCount = dataIn.readInt();
      for (i=0; i < m_spriteCount; i++)
         m_spriteID[i] = dataIn.readInt();
      }
   catch (std::exception eof)
      {
      printf("EOF");  // TODO: Logfile schreiben
      }
}

/******************************************************************************/
/** \brief Serialisiert das PartikelSystem in den Outputstream
    \param dataOut DataOutputStream
    \throws IOException 
*******************************************************************************/
void PartikelSystem::save(DataOutputStream& dataOut) 
{
   int i=0;

   dataOut.writeInt(m_zone);
   dataOut.writeInt(m_typ);
   dataOut.writeDouble(m_startPosOrg.m_x);
   dataOut.writeDouble(m_startPosOrg.m_y);
   dataOut.writeDouble(m_startPosOrg.m_z);
   dataOut.writeInt(m_spriteCount);
   for (i=0; i < m_spriteCount; i++)
      dataOut.writeInt(m_spriteID[i]);
}
