import type { ComponentMap } from './component-map.js';
import type { EntityMap } from './entity-map.js';
import type { ComponentClass, ComponentInstances, IteratorResult } from './types.js';

/**
 * @category Iterators
 * @example
 * // construct an instance
 * const iterator = new ComponentIterator(entityMap, Player, Position)
 * 
 * // iterate each component value that is related to the Player
 * for(const [entityId, player, position] of iterator) { }
 * 
 * // you can also reset the iterator back to the start 
 * // without having to create a new ComponentIterator instance
 * iterator.reset()
 * for(const [entityId, player, position] of iterator) { }
 */
export class ComponentIterator<
  TComponentKey extends ComponentClass<any>,
  TComponentClasses extends ComponentClass<any>[]
> {

  private keyMap: ComponentMap<any>
  private components: ComponentMap<any>[]
  // iteration state
  private entries: MapIterator<[number, TComponentKey]>

  /**
   * {@link ComponentNotRegistered} when any of specified component(s) are not registered
   */
  constructor(public ecs: EntityMap, public key: TComponentKey, ...components: TComponentClasses) {
    this.keyMap = this.ecs.getMap(key)!;
    this.entries = this.keyMap.entries();
    this.components = components.map(x => this.ecs.getMap(x)!);
  }

  /**
   * gets the next iterator value
   */
  next(): IteratorResult<ComponentInstances<[ComponentClass<number>, TComponentKey, ...TComponentClasses]>> {
    const entry = this.entries.next();
    const { value, done } = entry;
    if (done) return { value: value as any, done };

    const [entityId, entityValue] = value;
    const results = [entityId, entityValue]
    for (const x of this.components) {
      results.push(x.get(entityId));
    }

    return { value: results as any, done: false };
  }

  /**
   * resets the iterator back to the first entry
   */
  reset() {
    this.entries = this.keyMap.entries();
  }

  [Symbol.iterator]() { return this; }

}