

#include "trenderer.h"

#include <QMutexLocker>

#include "particlesmanager.h"

/*
EXPLANATION:

ParticlesManager improves the old particles system as follows - the particles
manager stores the
last particles configuration which had some particle rendered by a thread.
Under normal cicumstances, this means that every thread has the particles
configuration that rendered
last. In case a trail was set, such frame is that beyond the trail.
This managemer works well on the assumption that each thread builds particle in
an incremental timeline.
*/

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

DEFINE_CLASS_CODE(ParticlesManager::FxData, 110)

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

typedef std::map<double, ParticlesManager::FrameData> FramesMap;

//************************************************************************************************
//    Preliminaries
//************************************************************************************************

class ParticlesManagerGenerator final : public TRenderResourceManagerGenerator {
public:
  ParticlesManagerGenerator() : TRenderResourceManagerGenerator(true) {}

  TRenderResourceManager *operator()(void) override {
    return new ParticlesManager;
  }
};

MANAGER_FILESCOPE_DECLARATION(ParticlesManager, ParticlesManagerGenerator);

//************************************************************************************************
//    FrameData implementation
//************************************************************************************************

ParticlesManager::FrameData::FrameData(FxData *fxData)
    : m_fxData(fxData)
    , m_frame((std::numeric_limits<int>::min)())
    , m_calculated(false)
    , m_maxTrail(-1)
    , m_totalParticles(0) {
  m_fxData->addRef();
}

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

ParticlesManager::FrameData::~FrameData() { m_fxData->release(); }

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

void ParticlesManager::FrameData::buildMaxTrail() {
  // Store the maximum trail of each particle
  std::list<Particle>::iterator it;
  for (it = m_particles.begin(); it != m_particles.end(); ++it)
    m_maxTrail = std::max(m_maxTrail, it->trail);
}

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

void ParticlesManager::FrameData::clear() {
  m_frame = (std::numeric_limits<int>::min)();
  m_particles.clear();
  m_random         = TRandom();
  m_calculated     = false;
  m_maxTrail       = -1;
  m_totalParticles = 0;
}

//************************************************************************************************
//    FxData implementation
//************************************************************************************************

ParticlesManager::FxData::FxData() : TSmartObject(m_classCode) {}

//************************************************************************************************
//    ParticlesContainer implementation
//************************************************************************************************

ParticlesManager *ParticlesManager::instance() {
  return static_cast<ParticlesManager *>(
      ParticlesManager::gen()->getManager(TRenderer::renderId()));
}

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

ParticlesManager::ParticlesManager() : m_renderStatus(-1) {}

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

ParticlesManager::~ParticlesManager() {
  // Release all fxDatas
  std::map<unsigned long, FxData *>::iterator it, end = m_fxs.end();
  for (it = m_fxs.begin(); it != end; ++it) it->second->release();
}

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

void ParticlesManager::onRenderStatusStart(int renderStatus) {
  m_renderStatus = renderStatus;
}

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

ParticlesManager::FrameData *ParticlesManager::data(unsigned long fxId) {
  QMutexLocker locker(&m_mutex);

  std::map<unsigned long, FxData *>::iterator it = m_fxs.find(fxId);
  if (it == m_fxs.end()) {
    it = m_fxs.insert(std::make_pair(fxId, new FxData)).first;
    it->second->addRef();
  }

  FxData *fxData = it->second;
  FrameData *d   = fxData->m_frames.localData();
  if (!d) {
    d = new FrameData(fxData);
    fxData->m_frames.setLocalData(d);
  }

  return d;
}
