#include "stdafx.h"
#include "UnitRecordType/TransportUnitRecordType.h"
#include "UnitRecordType/BunkerUnitRecordType.h"
#include "UnitRecordManager.h"
#include "StaticUnitRecord.h"
#include "DynamicUnitRecord.h"

UnitRecordManager UnitRecordManager::s_instance;
UnitRecordManager::UnitTypeInstantiate UnitRecordManager::s_unitTypeInstantiate[BWAPI::NB_UNITS_TYPE];

//
// Unit Records
//

class UnitRecordFactoryImpl : public UnitRecordManager
{
public:
  template <class UnitRecordType>
  class InstantiateStaticUnitRecord : public UnitRecordManager::InstantiateUnitRecord
  {
  public:
    virtual StaticUnitRecord* instantiate(BWAPI::Unit* unit)
    {
      return new StaticUnitRecordT<UnitRecordType>(unit);
    }
  };

  class InstantiateDynamicUnitRecord : public UnitRecordManager::InstantiateUnitRecord
  {
  public:
    virtual DynamicUnitRecord* instantiate(BWAPI::Unit* unit)
    {
      return new DynamicUnitRecord(unit);
    }
  };

  //
  // Unit Record Types
  //

  //class InstantiateInvalid : public UnitRecordManager::InstantiateUnitRecordType
  //{
  //public:
  //  virtual UnitRecordType* instantiate(UnitRecord* unitRecord, BWAPI::UnitType unitType)
  //  {
  //    SPAR_LOG(LogTypes.GENERAL_ERROR, "Unit record \"%s\" should not morph!", toString(unitRecord).c_str());
  //    throw std::runtime_error("Invalid unit record type instantiation");
  //  }
  //};

  template <class CustomUnitRecordType>
  class InstantiateCustomUnitRecordType : public UnitRecordManager::InstantiateUnitRecordType
  {
  public:
    virtual UnitRecordType* instantiate(UnitRecord* unitRecord, BWAPI::UnitType unitType)
    {
      return new CustomUnitRecordType(unitRecord, unitType);
    }
  };

  static InstantiateDynamicUnitRecord s_instantiateDynamicUnitRecord;
  //static InstantiateInvalid s_instantiateInvalidUnitRecordType;

  template <class CustomUnitRecordType>
  static void initializeForUnitType(BWAPI::UnitType unitType)
  {
    static InstantiateCustomUnitRecordType<CustomUnitRecordType> s_instantiateCustomUnitRecordType;

    if (BWAPI::getImmediateMorphTypes(unitType).empty())
    {
      static InstantiateStaticUnitRecord<CustomUnitRecordType> s_instantiateStaticUnitRecord;

      s_unitTypeInstantiate[unitType.getID()].instantiateUnitRecord = &s_instantiateStaticUnitRecord;
      // Exception on instantiate()
      s_unitTypeInstantiate[unitType.getID()].instantiateUnitRecordType = &s_instantiateCustomUnitRecordType;
    }
    else
    {
      s_unitTypeInstantiate[unitType.getID()].instantiateUnitRecord = &s_instantiateDynamicUnitRecord;
      s_unitTypeInstantiate[unitType.getID()].instantiateUnitRecordType = &s_instantiateCustomUnitRecordType;
    }  
  }
};

UnitRecordFactoryImpl::InstantiateDynamicUnitRecord UnitRecordFactoryImpl::s_instantiateDynamicUnitRecord;
//UnitRecordFactoryImpl::InstantiateInvalid UnitRecordFactoryImpl::s_instantiateInvalidUnitRecordType;

void UnitRecordManager::initialize()
{
  // Default stuff
  for (size_t i = 0; i < array_size(s_unitTypeInstantiate); ++i)
  {
    UnitRecordFactoryImpl::initializeForUnitType<UnitRecordType>(BWAPI::UnitType(i));
  }
  
  UnitRecordFactoryImpl::initializeForUnitType<BunkerUnitRecordType>(BWAPI::UnitTypes::Terran_Bunker);
  UnitRecordFactoryImpl::initializeForUnitType<TransportUnitRecordType>(BWAPI::UnitTypes::Terran_Dropship);
  UnitRecordFactoryImpl::initializeForUnitType<TransportUnitRecordType>(BWAPI::UnitTypes::Protoss_Shuttle);
  UnitRecordFactoryImpl::initializeForUnitType<TransportUnitRecordType>(BWAPI::UnitTypes::Zerg_Overlord);
}

void UnitRecordManager::terminate()
{
}
