
import { ipcMain } from 'electron';
import { blackout } from '../patterns/blackout';
import { Pattern, PatternOutput, Time } from '../patterns/proto';
import { TestPattern } from '../patterns/test';
import rust, { BeatTrackerHandle, MovingHeadState, OutputHandle } from 'rust_native_module';
import { ChaserPattern } from '../patterns/chaser';

type AppState = {
  patterns: { [key: string]: PatternOutput },
  selectedPattern: string | null,

  beatProgress: number | null,
};

class Backend {

  beatTracker: BeatTrackerHandle;
  dmxOutput: OutputHandle;

  patterns: Map<string, Pattern>;
  // patternOutputs: Map<string, PatternOutput>;
  // selectedPattern: string;

  state: AppState;

  constructor() {

    // beat tracking

    let beatTracker = rust.getBeatTracker();

    if (beatTracker.type !== 'success') {
      throw new Error("could not initialize beat tracking");
    }

    this.beatTracker = beatTracker.value;
    ipcMain.on('beat-tracking', async (_, arg) => {
      if (arg === 'tap') {
        this.beatTracker.tap();
      }
    });

    // output

    let dmxOutput = rust.openOutput("/dev/ttyUSB0");

    if (dmxOutput.type !== 'success') {
      throw new Error("could not open DMX output");
    }

    this.dmxOutput = dmxOutput.value;

    // patterns

    this.patterns = new Map();
    this.patterns.set("test", new TestPattern());
    this.patterns.set("chaser", new ChaserPattern());

    this.state = {
      patterns: {},
      selectedPattern: null,
      beatProgress: null,
    }

    let time: Time = {
      absolute: 0,
      beatRelative: this.state.beatProgress,
    }

    for (let [patternId, pattern] of this.patterns.entries()) {
      let patternOutput = pattern.render(time);
      this.state.patterns[patternId] = patternOutput;
    }

    ipcMain.handle('poll', async (_) => {
      return this.getState();
    });

  }

  getState(): AppState {
    return this.state;
  }

  update() {

    let beatProgress = this.beatTracker.getProgress();

    if (beatProgress.type == 'some') {
      this.state.beatProgress = beatProgress.value;
    } else {
      this.state.beatProgress = null;
    }

    let date = new Date();
    let time: Time = {
      absolute: date.getTime() / 1000,
      beatRelative: this.state.beatProgress,
    }

    // render all patterns and write selected pattern to DMX

    let output: Array<MovingHeadState> = blackout;

    for (let [patternId, pattern] of this.patterns.entries()) {
      let patternOutput = pattern.render(time);
      this.state.patterns[patternId] = patternOutput;

      if (patternId === this.state.selectedPattern && patternOutput) {
        output = patternOutput;
      }
    }

    this.dmxOutput.set(output);
  }

  close() {
    this.dmxOutput.close();
  }

}

export default Backend;
