//  BlockyFroggy
//  Copyright © 2017 John Ryland.
//  All rights reserved.
#pragma once
#ifndef ENTITY_COMPONENT_SYSTEM_H
#define ENTITY_COMPONENT_SYSTEM_H

/*!

@page Topics
@subpage EntityComponentSystem
@page EntityComponentSystem Entity Component System

### Introduction

Concept is to use composition instead of inheritance to create a
variety of different types of objects.

 - Entity : general purpose object
 - Component : the raw data for one aspect of an object
 - System : processes streams of components of a particular type

[More info](https://en.wikipedia.org/wiki/Entity_component_system)

Related is AOP - Aspect Oriented Programming, however part of the
goal of ECS is to facility Data-Oriented Design to improve performance
by better utilitizing CPU code and data caches. AOP implemented using
OOP (Object-Oriented Programming) techniques will miss out on the
benefits of ECS which is taking a DOD approach.

An ECS very strictly enforces seperation of concerns or orthogonality
of the aspects of an entity. An entity is conceptually just a tag or
GUID. It doesn't contain any data or code, and it doesn't need to even
be implemented as an associated list of components.

A component in ECS is mostly just data. Typically lists of components
of the same type are stored contiguously in memory, and are tagged with
the entity ID they belong to. But each component is independent of the
other components that are tagged as belonging to an entity. This means
they are quite orthogonal by design.

A relational database is a good way to think about what an entity and
a component is. A table maps entities to components. Each type of component
would be a table in the database containing columns of data with a row
for each entity that has that component.

### Entity

Consists of an ID (usually instead of a pointer) and is associated with
components. However it doesn't have to be associated with components by
there being an explicit mapping of an entity to a list of components.
If it does, this just happens to be an implementation detail, not a
neccessity of an ECS system.

### Component

Usually a struct or POD type that is the data for this component or
aspect of the objects which have this component type.

### System

Able to efficiently process streams of components or sets of components
often in a thread. The system is considered a continuous process that
is always running.

### Multithreading

The systems need to be applied sequentially to a given entity. It should
be avoided having multiple threads reading and writing to the same
memory or neighbouring memory. However if the systems are run sequentially
one might wonder how performance can be improved by using multithreading
to increase concurrency. The solution is to process different entities
in parallel, so if there are N entities and M threads, split the N
entities up in to M sets of N/M entities, and process each set on a
different thread. Each thread can run each system in sequence each frame.


*/

#include <cstdint>
#include <vector>
#include <cstdlib>


class ECS;
class EntityManager;
class ComponentManager;
class SystemManager;

//! An entity is just an ID which is an index / foreign key
typedef uint32_t EntityID;

//! A component is an aspect of an entity
typedef uint32_t ComponentTypeID;

//! Base component type
struct Component
{
  ComponentTypeID m_type;  // <- might be hard to remove
  EntityID        m_owner; // <- perhaps don't need
};

template <typename ComponentType>
class ComponentFactory
{
public:
  static ComponentTypeID s_componentType;

  virtual ~ComponentFactory() = default;
  virtual ComponentTypeID GetComponentType() = 0;
  virtual Component* NewComponent() = 0;
  virtual void DeleteComponent(Component* a_component) = 0;

  // Allocates like components together from memory pools
  // Can serialize/deserialize a component
  std::deque<ComponentType> m_components;
  
  // Perhaps another way is to map entityIDs to components here:
  // But I wonder if this will lose the contiguous memory layout a vector will give
  std::map<EntityID, ComponentType> m_components;
};

//! Component list belonging to an entity

class ComponentManager
{
public:
  //! This happens once at the beginning of a level / game
  void AddComponentFactory(std::shared_ptr<ComponentFactory> a_componentFactory);
  //! Possibly, at the end of a level this might be needed if different levels support different components
  //! but perhaps generally this might not really be needed
  void RemoveComponentFactory(std::shared_ptr<ComponentFactory> a_componentFactory);
};

class ECS
{
public:
  EntityManager* m_entityManager;
  ComponentManager* m_componentManager;
  SystemManager* m_systemManager;
};

class System
{
public:
  virtual ~System() = default;
  virtual std::vector<ComponentTypeID> ReadComponentTypes() const = 0;  // Entity needs to have these
  virtual std::vector<ComponentTypeID> WriteComponentTypes() const = 0; // Possibly the system ends up creating these if the entity didn't originally have them
  virtual void Process(const std::vector<EntityID>& a_entities) const = 0;

  EntityManager* m_entityManager;
  SystemManager* m_systemManager;
};

class SystemManager
{
public:
  //! Should be added in the order they should logically be applied
  //! Examples are input system, render system, animation system etc
  void AddSystem(std::shared_ptr<System> a_system);
  //! Definately systems could be dynamically added and removed to apply/disable effects or processing
  //! Imagine different settings like turning on and off shadows, this could be adding/removing a shadow system
  void RemoveSystem(std::shared_ptr<System> a_system);

  //! Run the systems over the entities
  void Update();
};

class EntityManager
{
public:
  typedef std::vector<ComponentTypeID> Archetype; // EntityTemplate

  //! Dynamically could be added during the game, eg: spawning new players or spawning weapons of effects
  EntityID NewEntity(std::string a_name, Archetype a_componentTypeList = {});
  //! Possibly a character could pick up an item (like a gun) and this adds a component to them
  void AddComponentToEntity(EntityID a_entityID, ComponentTypeID a_componentType);
  //! When an item is dropped, dynamically removing a component
  void RemoveComponentFromEntity(EntityID a_entityID, ComponentTypeID a_componentType);
  //! Dynamically entities could be removed when they are finished with, eg a bullet is short lived
  void DeleteEntity(EntityID a_entityID);


  // Accessors

  class EntityIterator
  {
  };
  EntityIterator GetEntitiesWithComponents(Archetype a_componentList);

  template <typename ComponentType>
  EntityIterator GetEntitiesOfType();

  //! Access the associated component data of ComponentType that belongs to the entity
  template <typename ComponentType>
  ComponentType& GetEntityComponentData(EntityID a_entityID);

private:
  struct Entity
  {
    // std::vector<Component*>  m_components;
    std::map<ComponentTypeID, Component*>  m_components;
    
    template <typename ComponentType>
    ComponentType* As() {
      // Using a static_cast here where a dynamic_cast would normally be expected
      // so as to avoid the cost of a dynamic_cast, but potentially this is unsafe
      // if we aren't using some other mechanism to ensure we know it will be of the
      // correct type.
      return static_cast<ComponentType*>(m_components[ComponentType::s_componentType]);
    }
  };

  //! Systems
  std::vector<std::shared_ptr<System>>            m_systems;

  //! Components
  std::vector<std::shared_ptr<ComponentFactory>>  m_components;

  //! Entities
  std::deque<Entity>                              m_entities;    // Perhaps can do away with this - just entityID in the component is enough
  std::map<EntityID, std::string>                 m_entityNames; // Probably only needed from editing/debug/dev point of view
                                                                 // With final baked release data, this would not be strictly needed
                                                                 // Also in the editor environment, conceptually there could be a heirarchy
                                                                 // but this becomes a pathed name eg:  root/players/player01 which is
                                                                 // the string which maps to the flat list of EntityIDs

  struct EntityTemplates {
    uint32_t      entityTemplateID; // unique/primary key
    std::string   defaultLabel;
    std::string   officialName;
    std::string   description;
  };
  std::vector<EntityTemplates>             m_table1;

  struct EntityTemplateComponents {
    uint32_t      entityTemplateID; // foreign key
    uint32_t      componentID;      // foreign key
  };
  std::vector<EntityTemplateComponents>    m_table2;

  struct ComponentTables {
    uint32_t      componentID;
    std::string   officialName;
    std::string   description;
    std::string   tableName;  // unique/primary key (sort of)
  };
  std::vector<ComponentTables>             m_table3;

  struct Entities {
    uint32_t      entityID; // unique/primary key
    std::string   label;    // for debugging
  };
  std::vector<Entities>                    m_table4;

  struct EntityComponentsN {
    uint32_t      entityID; // foreign key
    //uint32_t      componentID; // foreign key
    uint32_t      componentDataID; // foreign key
  };
  std::vector< std::vector<EntityComponents> >  m_table5;

  // Not really C++
  struct ComponentDataN {
    uint32_t      componentDataID; // unique/primary key
    // ... data 
  };
  std::vector< std::vector<ComponentDataN> >    m_table5+N;



  struct PerThreadData
  {
    std::vector< std::vector<EntityID> >   m_systemEntities;  // Per system list of entities
  };

  std::vector<PerThreadData>    m_perThreadData;

  // Lists of entities by system

  // Different schemes are used to figure out which entities need to be processed by the different systems
  // Some involve iterating every entity with every system, and maintaining a set of bitflags for an entity for which components it has
  // Some involve caching the entities that the system should process, or a combination of both these
  // Another idea is maintaining bitflags of which systems apply to an entity
  // Probably for large numbers of entities, think effects particles, it would be desirable to keep a cache in the system
  // as to its entity list. When an entity is added/removed or components added/removed from an entity, a notification should
  // be sent to the systems. Perhaps this can be done in an update cycle after the systems have done their processing and be batched
  // up to be processed at once.

  // thread-pool, execution strategy and scheduling etc
};














typedef int FamilyId;
struct EntitySystem; 

struct Component {
};

struct Entity {
  static EntitySystem *entitySystem;
  Entity();
  template<typename Type> Type *getAs();
  std::map<FamilyId, Component*> mComponents;
};
EntitySystem *Entity::entitySystem = 0;
struct EntitySystem {
  EntitySystem() {
    Entity::entitySystem = this;
  }
  template<typename T> T *getComponent(Entity *e) {
    return (T*)e->mComponents[T::familyId];
  }
  template<typename T> void getEntities(std::vector<Entity*> &result) {
    auto iterPair = mComponentStore.equal_range(T::familyId);
    for(auto iter = iterPair.first; iter != iterPair.second; ++iter) {
      result.push_back(iter->second);
    }
  }
  template<typename T> void addComponent(Entity *e, T* comp) {
    mComponentStore.insert(std::pair<FamilyId, Entity*>(T::familyId, e));
    e->mComponents.insert(std::pair<FamilyId, Component*>(T::familyId, comp));
  }
  protected:
  std::multimap<FamilyId, Entity*> mComponentStore;
};

Entity::Entity() {
}

template<typename Type> Type *Entity::getAs() {
  return entitySystem->getComponent<Type>(this);
}


void test()
{
  std::vector<Entity*> entitiesWithPosition;
  entitySystem.getEntities<CompPosition3D>(entitiesWithPosition); 

  NBodyIntegrationStep integrationStep(entitiesWithPosition, (float)0.001);
  bOk = ParallelFor(&integrationStep, ParallelRange(0, entitiesWithPosition.size(), 20));
  //the integration step uses the following components: Position3D, TemporaryPosition3D and Velocity

  NBodyCopyTempPos copyTempPos(entitiesWithPosition);
  bOk = ParallelFor(&copyTempPos, ParallelRange(0, entitiesWithPosition.size(), 20));
  //following components are used: TemporaryPosition3D and Position3D

  Project3Dto2D project3Dto2D(entitiesWithPosition);
  bOk = ParallelFor(&project3Dto2D, ParallelRange(0, entitiesWithPosition.size(), 20));
  //the following components are used: Position3D and Position2D
}






#endif // ENTITY_COMPONENT_SYSTEM_H

