import 'dart:html' as html;

import 'package:rules_of_living/src/Grid.dart';

class Engine {
  // Elapsed Time Counter - useful for Safety Timeout
  Stopwatch _elapsed = new Stopwatch();

  // Game Tick Rate - *does* impact game speed TODO add configurable option
  int _MS_PER_STEP = 1000 ~/ 3;

  // Max Frame (i.e. Rendering) rate - does *not* impact game speed
  final int _MS_PER_FRAME = 1000 ~/ 30;

  // ms stuck in updateloop after which game will declare itself unresponsive
  final int SAFETY_TIMEOUT = 2000;

  // Grid Size TODO add as configurable option
  static final GRID_X = 100;
  static final GRID_Y = 100;

  num _updateLag = 0.0;
  num _drawLag = 0.0;

  final html.CanvasElement canvas;
  Grid _grid = new Grid(GRID_X, GRID_Y);
  bool _running = false;

  Engine([this.canvas]) {
    _elapsed.start();
    _grid.addPattern(amount: 15, dispersal: 5);
  }

  void reset() {
    _grid.reset();
    running = false;
  }

  void clear() {
    _grid = new Grid(100, 100);
    running = false;
  }

  void process(num now) {
    _drawLag += _elapsed.elapsedMilliseconds;
    _updateLag += _elapsed.elapsedMilliseconds;
    _elapsed.reset();

    while (_updateLag >= _MS_PER_STEP) {
      if (_elapsed.elapsedMilliseconds > SAFETY_TIMEOUT) {
        // TODO stub - give warning etc when this occurs
        print("ERROR STUCK IN UPDATE LOOP");
        break;
      }
      if (running == true) update();
      _updateLag -= _MS_PER_STEP;
    }

    if (_drawLag >= _MS_PER_FRAME) {
      render(_updateLag / _MS_PER_STEP);
      _drawLag = 0;
    }
  }

  void update() {
    if (!_grid.update()) running = false;
  }

  void step() {
    running = false;
    _grid.update();
  }

  void render([num interp]) {
//    print("rendering");
    _grid.render(canvas, interp);
  }

  void addPattern(
      {CellPattern pattern,
      int x,
      int y,
      int amount,
      int dispersal,
      int seed}) {
    _grid.addPattern(
        pattern: pattern,
        x: x,
        y: y,
        amount: amount,
        dispersal: dispersal,
        seed: seed);
  }

  void toggleEdgeRendering() {
    _grid.renderEdges = !_grid.renderEdges;
  }

  void set running(bool on) => _running = on;
  bool get running => _running;
}