#include "MetroidPrime/ScriptObjects/CScriptRandomRelay.hpp"

#include "MetroidPrime/CStateManager.hpp"

#include "rstl/math.hpp"

CScriptRandomRelay::CScriptRandomRelay(TUniqueId uid, const rstl::string& name,
                                       const CEntityInfo& info, int sendSetSize,
                                       const int sendSetVariance, const bool percentSize,
                                       const bool active)
: CEntity(uid, info, active, name)
, x34_sendSetSize(sendSetSize)
, x38_sendSetVariance(sendSetVariance)
, x3c_percentSize(percentSize) {
  if (percentSize && x34_sendSetSize > 100) {
    x34_sendSetSize = 100;
  }
}

CScriptRandomRelay::~CScriptRandomRelay() {}

void CScriptRandomRelay::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId objId,
                                         CStateManager& stateMgr) {
  CEntity::AcceptScriptMsg(msg, objId, stateMgr);
  switch (msg) {
  case kSM_SetToZero:
    if (GetActive()) {
      SendLocalScriptMsgs(kSS_Zero, stateMgr);
    }
    break;
  default:
    break;
  }
}

void CScriptRandomRelay::SendLocalScriptMsgs(EScriptObjectState state, CStateManager& stateMgr) {
  switch (state) {
  case kSS_Zero: {
    rstl::vector< rstl::pair< CEntity*, EScriptObjectMessage > > objs;
    objs.reserve(10);

    rstl::vector< SConnection >::const_iterator conn = GetConnectionList().begin();
    for (; conn != GetConnectionList().end(); ++conn) {
      if (conn->x0_state == kSS_Zero) {
        CObjectList& objList = stateMgr.ObjectListById(kOL_All);
        CStateManager::TIdListResult list = stateMgr.GetIdListForScript(conn->x8_objId);
        if (!(list.first == list.second)) {
          for (CStateManager::TIdList::const_iterator it = list.first; it != list.second; ++it) {
            CEntity* ent = objList.GetObjectById(it->second);
            if (ent && ent->GetActive()) {
              objs.push_back(rstl::pair< CEntity*, EScriptObjectMessage >(ent, conn->x4_msg));
            }
          }
        }
      }
    }

    int targetSetSize = x3c_percentSize ? int(0.5f + (float(x34_sendSetSize * objs.size()) / 100.f))
                                        : x34_sendSetSize;
    targetSetSize +=
        int(float(x38_sendSetVariance) * (stateMgr.Random()->Float() * 2.0f)) - x38_sendSetVariance;

    targetSetSize = rstl::min_val(rstl::max_val(0, targetSetSize), 64);

    while (objs.size() > targetSetSize) {
      const int randomRemoveIdx = int(stateMgr.Random()->Float() * float(objs.size()) * 0.99f);
      rstl::vector< rstl::pair< CEntity*, EScriptObjectMessage > >::iterator it = objs.begin();
      for (int i = 0; i < randomRemoveIdx; ++i) {
        ++it;
        if (it == objs.end()) {
          break;
        }
      }
      if (it != objs.end()) {
        objs.erase(it);
      }
    }

    for (rstl::vector< rstl::pair< CEntity*, EScriptObjectMessage > >::iterator it = objs.begin();
         it != objs.end(); ++it) {
      stateMgr.DeliverScriptMsg(it->first, GetUniqueId(), it->second);
    }
    break;
  }
  default:
    SendScriptMsgs(state, stateMgr, kSM_None);
    break;
  }
}

void CScriptRandomRelay::Accept(IVisitor& visitor) { visitor.Visit(*this); }
