"use strict";

import { Platform, BaseZ80Platform  } from "../baseplatform";
import { PLATFORMS, RAM, newAddressDecoder, padBytes, noise, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeKeycodeMap } from "../emu";
import { hex } from "../util";

// from TSS
declare var MasterChannel, AudioLooper, PsgDeviceChannel;

var KONAMISOUND_PRESETS = [
];

var KonamiSoundPlatform = function(mainElement) {
  this.__proto__ = new (BaseZ80Platform as any)();

  var cpu, ram, rom, membus, iobus;
  var audio, master;
  var video, timer;
  var interruptPending = 0;
  var psgRegister = 0;
  var psg;

  var cpuFrequency = 14318000/8;
  var ioTimerFrequency = 1789750 / 1280;
  var cpuCyclesPerFrame = cpuFrequency/60;
  var cpuCyclesPerTimer = cpuFrequency/ioTimerFrequency;

  this.getPresets = function() {
    return KONAMISOUND_PRESETS;
  }

  this.start = function() {
    ram = new RAM(0x400);
    membus = {
      read: newAddressDecoder([
				[0x0000, 0x3fff, 0x3fff, function(a) { return rom ? rom[a] : null; }],
				[0x4000, 0x5fff, 0x3ff,  function(a) { return ram.mem[a]; }]
			]),
			write: newAddressDecoder([
				[0x4000, 0x5fff, 0x3ff,  function(a,v) { ram.mem[a] = v; }],
			]),
      isContended: function() { return false; },
    };
    iobus = {
      read: function(addr) {
        if (addr & 0x40) {
          if (psgRegister == 0xf) { // timer
            var bit = (cpu.getTstates() / cpuCyclesPerTimer) & 1;
            return bit ? 0xff : 0x00; // 0x00, 0x10, 0x20, 0x30, 0x40, 0x90, 0xa0, 0xb0, 0xa0, 0xd0
          }
          return psg.readRegister(psgRegister) & 0xff;
        }
        return 0;
    	},
    	write: function(addr, val) {
        if (addr & 0x80) {
          psgRegister = val & 0xf;
          //console.log('PSG reg', psgRegister);
        }
        if (addr & 0x40) {
          psg.writeRegisterAY(psgRegister, val & 0xff);
          //console.log('PSG write', psgRegister, val);
        }
    	}
    };
    this.readAddress = membus.read;
    cpu = this.newCPU(membus, iobus);
    psg = new PsgDeviceChannel();
    master = new MasterChannel();
    psg.setMode(PsgDeviceChannel.MODE_SIGNED);
    psg.setDevice(PsgDeviceChannel.DEVICE_AY_3_8910);
    master.addChannel(psg);
    audio = new AudioLooper(512);
    audio.setChannel(master);
    //audio = new SampleAudio(psg.sampleRate);
    video = new RasterVideo(mainElement,256,256);
    video.create();
    video.setKeyboardEvents(function(key,code,flags) {
      var intr = (key-49);
      if (intr >= 0 && (flags & 1)) {
        psg.writeRegister(14, intr);
        psg.writeRegister(15, 0x80);
        cpu.setIFF1(1);
        cpu.requestInterrupt(0x38);
        /*
        console.log(cpu.saveState());
        console.log(hex(intr * 8), cpu.getIFF1(), cpu.getIM());
        cpu.runFrame(cpu.getTstates() + 1);
        console.log(cpu.saveState());
        */
      }
    });
    timer = new AnimationTimer(60, () => {
			if (!this.isRunning())
				return;
      var debugCond = this.getDebugCallback();
      var targetTstates = cpu.getTstates() + cpuCyclesPerFrame;
      if (debugCond) {
        while (cpu.getTstates() < targetTstates) {
          if (debugCond && debugCond()) { debugCond = null; }
          cpu.runFrame(cpu.getTstates() + 1);
        }
      } else {
        cpu.runFrame(targetTstates);
      }
    });
  }

  this.loadROM = function(title, data) {
    rom = padBytes(data, 0x4000);
    cpu.reset();
  }

  this.loadState = function(state) {
    cpu.loadState(state.c);
    ram.mem.set(state.b);
  }
  this.saveState = function() {
    return {
      c:this.getCPUState(),
      b:ram.mem.slice(0),
    };
  }
  this.getCPUState = function() {
    return cpu.saveState();
  }

  this.isRunning = function() {
    return timer && timer.isRunning();
  }
  this.pause = function() {
    timer.stop();
    //audio.stop();
  }
  this.resume = function() {
    timer.start();
    audio.activate();
  }
  this.reset = function() {
    cpu.reset();
    if (!this.getDebugCallback()) cpu.setTstates(0); // TODO?
  }
}

PLATFORMS['sound_konami'] = KonamiSoundPlatform;
