import {ChannelGroupIds as channelGroupIds } from './helpers/globals.js';


export class FakeJamClient {
  constructor(app, p2pMessageFactory) {
    this.ChannelGroupIds = channelGroupIds;
    this.logger = window.console;
    this.logger.info("*** Fake JamClient instance initialized. ***");

    // Change this to false if you want to see FTUE with fake jam client
    this.ftueStatus = true;
    this.eventCallbackName = '';
    this.alertCallbackName = '';
    this.eventCallbackRate = 5000;
    this.vuValue = -70;
    this.vuChange = 10;
    this.callbackTimer = null;
    this._mix = -30;
    this.device_id = -1;
    this.latencyCallback = null;
    this.frameSize = 2.5;
    this.fakeJamClientRecordings = null;
    this.p2pCallbacks = null;
    this.videoShared = false;
    this.metronomeActive = false;
    this.metronomeBPM = false;
    this.metronomeSound = false;
    this.metronomeMeter = 0;
    this.backingTrackPath = "";
    this.backingTrackLoop = false;
    this.simulateNoInputs = false;
    this.connected = true;
    this.clientID = "devtester";
  }

  GetClientID() {
    return this.clientID;
  }

  // Utility methods
  dbg(msg) {
    this.logger.debug('FakeJamClient: ' + msg);
  }

  GetDetailedOS() {
    return "MacOSX M1";
  }

  getClientParentChildRole() {
    // 0 = client
    // 1 = parent
    return 1;
  }

  getParentClientId() {
    return null;
  }

  applySubscriptionPolicy() {}

  OpenSystemBrowser(href) {
    this.dbg("OpenSystemBrowser('" + href + "')");
    window.open(href);
  }

  RestartApplication() {}

  ShutdownApplication() {}

  // FTUE methods
  FTUEPageEnter() {}

  FTUEPageLeave() {}

  FTUECancel() {}

  FTUEGetMusicProfileName() {
    if (this.simulateNoInputs) {
      return "System Default (Playback Only)";
    } else {
      return "FTUEAttempt-1";
    }
  }

  FTUESetMusicProfileName() {}

  FTUESetPreferredMixerSampleRate() {}

  FTUESetPreferredOutputSampleRate() {}

  FTUESetPreferredChatSampleRate() {}

  FTUEgetInputDeviceSampleRate() {
    return 44100;
  }

  FTUEgetOutputDeviceSampleRate() {
    return 44100;
  }

  FTUESelectVideoCaptureDevice(device, settings) {
    return true;
  }

  FTUESetVideoEncodeResolution(resolution) {}

  testVideoRender() {}

  FTUEGetVideoCaptureDeviceNames() {
    return {"xy323ss": "Built-in Webcam HD"};
  }

  FTUECurrentSelectedVideoDevice() {
    return {"xy323ss": "Built-in Webcam HD"};
  }

  FTUEGetAvailableEncodeVideoResolutions() {
    return {
      1: "CIF    (352X288)",
      2: "VGA    (640X480)",
      3: "4CIF   (704X576)",
      4: "1/2WHD (640X360)",
      5: "WHD    (1280X720)",
      6: "FHD    (1920x1080)"
    };
  }

  FTUEGetVideoCaptureDeviceCapabilities() {
    return {};
  }

  FTUESetSendFrameRates(fps) {}

  FTUEGetSendFrameRates() {
    return {20: 20, 24: 24, 30: 30};
  }

  GetCurrentVideoResolution() {
    return 3;
  }

  GetCurrentVideoFrameRate() {
    return 30;
  }

  GetSampleRate() {
    return 48;
  }

  FTUESetVideoShareEnable() {}

  FTUEGetVideoShareEnable() {
    return true;
  }

  isSessVideoShared() {
    return this.videoShared;
  }

  SessStopVideoSharing() {
    this.videoShared = false;
  }

  SessStartVideoSharing(bitrate) {
    if (!bitrate || typeof(bitrate) === "undefined") {
      bitrate = 0;
    }
    this.videoShared = true;
  }

  IsVstLoaded() {
    return false;
  }

  hasVstAssignment() {
    return false;
  }

  FTUEGetInputLatency() {
    this.dbg("FTUEGetInputLatency");
    return 2;
  }

  FTUESetInputLatency(latency) {
    this.dbg("FTUESetInputLatency:" + latency);
  }

  FTUEGetOutputLatency() {
    this.dbg("FTUEGetOutputLatency");
    return 2;
  }

  FTUESetOutputLatency(latency) {
    this.dbg("FTUESetOutputLatency:" + latency);
  }

  FTUEGetVolumeRanges() {
    this.dbg("FTUEGetVolumeRanges");
    return {
      input_maximum: 20,
      input_minimum: -80,
      output_maximum: 20,
      output_minimum: -80
    };
  }

  FTUEHasControlPanel() {
    this.dbg("FTUEHasControlPanel");
    return true;
  }

  FTUEGetFrameSize(newSize) {
    this.dbg("FTUEGetFrameSize");
    return this.frameSize;
  }

  FTUESetFrameSize(newSize) {
    this.dbg("FTUESetFrameSize:" + newSize);
    // one of 2.5, 5 or 10
    this.frameSize = newSize;
  }

  FTUEOpenControlPanel() {
    this.dbg("FTUEOpenControlPanel");
    window.alert("Simulated ASIO Dialog");
  }

  FTUEInit() {
    this.dbg("FTUEInit");
  }

  FTUERefreshDevices() {
    this.dbg("FTUERefreshDevices()");
  }

  FTUESave(b) {
    this.dbg("FTUESave(" + b + ")");
    return {};
  }

  FTUEGetStatus() {
    return this.ftueStatus;
  }

  FTUESetStatus(b) {
    this.ftueStatus = b;
  }

  FTUESetMusicDevice(id) {
    this.dbg("FTUESetMusicDevice");
  }

  FTUEGetAudioDevices() {
    return {
      "devices": [{
        "display_name": "Built-in",
        "guid": "Built-in",
        "input_count": 1,
        "name": "Built-in",
        "output_count": 1,
        "port_audio_name": "Built-in"
      }, {
        "display_name": "JamKazam Virtual Monitor",
        "guid": "JamKazam Virtual Monitor",
        "input_count": 0,
        "name": "JamKazam Virtual Monitor",
        "output_count": 1,
        "port_audio_name": "JamKazam Virtual Monitor"
      }]
    };
  }

  FTUEGetDevices() {
    this.dbg('FTUEGetMusicDevices');
    return {
      "ASIO4ALL v2": "ASIO4ALL v2 - ASIO",
      "M-Audio FW ASIO": "M-AUDIO FW ASIO - ASIO"
    };
  }

  FTUEGetMusicInputs() {
    this.dbg('FTUEGetMusicInputs');
    return {
      "i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~1": "Multichannel (FWAPMulti) - Channel 1/Multichannel (FWAPMulti) - Channel 2"
    };
  }

  FTUEGetMusicOutputs() {
    this.dbg('FTUEGetMusicOutputs');
    return {
      "o~11~Multichannel (FWAPMulti)~0^o~11~Multichannel (FWAPMulti)~1": "Multichannel (FWAPMulti) - Channel 1/Multichannel (FWAPMulti) - Channel 2"
    };
  }

  FTUEGetChatInputs() {
    this.dbg('FTUEGetChatInputs');
    return {
      "i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~1": "Multichannel (FWAPMulti) - Channel 1/Multichannel (FWAPMulti) - Channel 2"
    };
  }

  FTUEGetChannels() {
    return {
      "inputs": [
        {
          "assignment": 1,
          "chat": false,
          "device_id": "Built-in Microph",
          "device_type": 5,
          "id": "i~5~Built-in Microph~0~0~Built-in",
          "name": "Built-in Microph - Left",
          "number": 0
        },
        {
          "assignment": 0,
          "chat": false,
          "device_id": "Built-in Microph",
          "device_type": 5,
          "id": "i~5~Built-in Microph~1~0~Built-in",
          "name": "Built-in Microph - Right",
          "number": 1
        }
      ],
      "outputs": [
        {
          "assignment": -1,
          "chat": false,
          "device_id": "Built-in Output",
          "device_type": 5,
          "id": "o~5~Built-in Output~0~0~Built-in",
          "name": "Built-in Output - Left",
          "number": 0
        },
        {
          "assignment": -1,
          "chat": false,
          "device_id": "Built-in Output",
          "device_type": 5,
          "id": "o~5~Built-in Output~1~0~Built-in",
          "name": "Built-in Output - Right",
          "number": 1
        }
      ]
    };
  }

  FTUEClearChannelAssignments() {}

  FTUEClearChatInput() {}

  FTUEStartIoPerfTest() {}

  FTUEGetIoPerfData() {
    return {
      "in_var": 0.15,
      "out_var": 0.25,
      "in_median": 399.9,
      "out_median": 400.3,
      "in_target": 400,
      "out_target": 400
    };
  }

  FTUESetInputMusicDevice() {}

  FTUESetOutputMusicDevice() {}

  FTUEGetInputMusicDevice() {
    return 'Built-in';
  }

  FTUEGetOutputMusicDevice() {
    return 'Built-in';
  }

  FTUESetMusicInput() {
    this.dbg('FTUESetMusicInput');
  }

  FTUESetChatInput() {
    this.dbg('FTUESetChatInput');
  }

  FTUESetMusicOutput() {
    this.dbg('FTUESetMusicOutput');
  }

  FTUEGetInputVolume() {
    this.dbg('FTUEGetInputVolume');
    return -60;
  }

  FTUESetInputVolume() {
    this.dbg('FTUESetInputVolume');
  }

  FTUEGetOutputVolume() {
    this.dbg('FTUEGetOutputVolume');
    return -40;
  }

  FTUESetOutputVolume() {
    this.dbg('FTUESetOutputVolume');
  }

  FTUEGetChatInputVolume() {
    this.dbg('FTUEGetChatInputVolume');
    return -10;
  }

  FTUESetChatInputVolume() {
    this.dbg('FTUESetChatInputVolume');
  }

  FTUERegisterVUCallbacks() {
    this.dbg('FTUERegisterVUCallbacks');
  }

  FTUERegisterLatencyCallback(functionName) {
    this.dbg('FTUERegisterLatencyCallback');
    this.latencyCallback = functionName;
  }

  FTUEStartLatency() {
    const cb = () => {
      // Change 4192 to modify latency MS results (in microseconds)
      eval(`${this.latencyCallback}(10.0)`);
    };
    window.setTimeout(cb, 1000);
  }

  FTUEGetExpectedLatency() {
    return {latencyknown: true, latency: 5};
  }

  FTUEGetGoodConfigurationList() {
    return ['default'];
  }

  FTUEGetAllAudioConfigurations() {
    return ['default'];
  }

  FTUEGetGoodAudioConfigurations() {
    return ['default'];
  }

  FTUEGetConfigurationDevice() {
    return 'Good Device';
  }

  FTUELoadAudioConfiguration() {
    return true;
  }

  FTUEIsMusicDeviceWDM() {
    return false;
  }

  FTUECreateUpdatePlayBackProfile() {
    return true;
  }

  RegisterVolChangeCallBack(functionName) {
    this.dbg('RegisterVolChangeCallBack');
  }

  // General methods
  GetOS() {
    return 100000000;
  }

  GetOSAsString() {
    return "MacOSX";
  }

  LatencyUpdated(map) {
    this.dbg('LatencyUpdated:' + JSON.stringify(map));
  }

  LeaveSession(map) {
    this.dbg('LeaveSession:' + JSON.stringify(map));
  }

  // P2P methods
  RegisterP2PMessageCallbacks(callbacks) {
    this.p2pCallbacks = callbacks;
  }

  P2PMessageReceived(from, payload) {
    this.dbg('P2PMessageReceived');
    // payload is a JSON ready string
    try {
      payload = JSON.parse(payload);
    } catch (e) {
      this.logger.warn("unable to parse payload as JSON from client %o, %o, %o", from, e, payload);
    }

    const callback = this.p2pCallbacks[payload.type];
    if (callback) {
      callback(from, payload);
    }
  }

  JoinSession(msg) {
    this.dbg('JoinSession:' + JSON.stringify(msg));
  }

  ParticipantLeft(session, participant) {
    this.dbg('ParticipantLeft:' + JSON.stringify(session) + ',' + JSON.stringify(participant));
  }

  ParticipantJoined(session, participant) {
    this.dbg('ParticipantJoined:' + JSON.stringify(session) + ',' + JSON.stringify(participant));
  }

  RecordTestBegin() {
    this.dbg('RecordTestBegin');
  }

  RecordTestEnd() {
    this.dbg('RecordTestBegin');
  }

  RecordTestPlayback() {
    this.dbg('RecordTestBegin');
  }

  SendP2PMessage(s1, s2) {
    this.dbg('SendP2PMessage:' + s1 + ',' + s2);
  }

  SetASIOEnabled(i1, i2, i3, i4, i5, i6) {
    this.dbg('SetASIOEnabled(' + i1 + ',' + i2 + ',' + i3 + ',' + i4 + ',' + i5 + ',' + i6 + ')');
  }

  SignalLatencyUpdated(client) {
    this.dbg('SignalLatencyUpdated:' + JSON.stringify(client));
  }

  SignalSendP2PMessage(s1, ba) {
    this.dbg('SignalSendP2PMessage:' + JSON.stringify(arguments));
  }

  StartPlayTest(s) {
    this.dbg('StartPlayTest' + JSON.stringify(arguments));
  }

  StartRecordTest(s) {
    this.dbg('StartRecordTest' + JSON.stringify(arguments));
  }

  StartRecording(recordingId, groupedClientTracks) {
    this.dbg('StartRecording');
    this.fakeJamClientRecordings.StartRecording(recordingId, groupedClientTracks);
  }

  StopPlayTest() {
    this.dbg('StopPlayTest');
  }

  StopRecording(recordingId, groupedTracks, errorReason, detail) {
    this.dbg('StopRecording');
    this.fakeJamClientRecordings.StopRecording(recordingId, groupedTracks, errorReason, detail);
  }

  AbortRecording(recordingId, errorReason, errorDetail) {
    this.dbg('AbortRecording');
    this.fakeJamClientRecordings.AbortRecording(recordingId, errorReason, errorDetail);
  }

  OnTrySyncCommand(cmd) {}

  GetRecordingManagerState() {
    return {running: false};
  }

  TestASIOLatency(s) {
    this.dbg('TestASIOLatency' + JSON.stringify(arguments));
  }

  TestLatency(clientID, callbackFunctionName, timeoutCallbackName) {
    this.logger.debug("Fake JamClient: TestLatency called with client, " + clientID + " and callback function name: " + callbackFunctionName);
    const response = {
      clientID: clientID,
      latency: 50
    };
    const js = callbackFunctionName + "(" + JSON.stringify(response) + ");";
    eval(js);
  }

  IsMyNetworkWireless() {
    return 1;
  }

  SetNetworkTestScore(numClients) {}

  SetVideoNetworkTestScore(numClients) {}

  GetNetworkTestScore() {
    return 8;
  }

  GetVideoNetworkTestScore() {
    return 8;
  }

  SetLatencyTestBlocked(blocked) {}

  isLatencyTestBlocked() {
    return false;
  }

  GetLastLatencyTestTimes() {
    return {initiated: 10000, requested: 10000};
  }

  GetASIODevices() {
    const response = [{
      "device_id": 0,
      "device_name": "Realtek High Definition Audio",
      "device_type": 0,
      "interfaces": [{
        "interface_id": 0,
        "interface_name": "Realtek HDA SPDIF Out",
        "pins": [{"is_input": false, "pin_id": 0, "pin_name": "PC Speaker"}]
      }, {
        "interface_id": 1,
        "interface_name": "Realtek HD Audio rear output",
        "pins": [{"is_input": false, "pin_id": 0, "pin_name": "PC Speaker"}]
      }, {
        "interface_id": 2,
        "interface_name": "Realtek HD Audio Mic input",
        "pins": [{"is_input": true, "pin_id": 0, "pin_name": "Recording Control"}]
      }, {
        "interface_id": 3,
        "interface_name": "Realtek HD Audio Line input",
        "pins": [{"is_input": true, "pin_id": 0, "pin_name": "Recording Control"}]
      }, {
        "interface_id": 4,
        "interface_name": "Realtek HD Digital input",
        "pins": [{"is_input": true, "pin_id": 0, "pin_name": "Capture"}]
      }, {
        "interface_id": 5,
        "interface_name": "Realtek HD Audio Stereo input",
        "pins": [{"is_input": true, "pin_id": 0, "pin_name": "Recording Control"}]
      }],
      "wavert_supported": false
    }, {
      "device_id": 1,
      "device_name": "M-Audio FW Audiophile",
      "device_type": 1,
      "interfaces": [{
        "interface_id": 0,
        "interface_name": "FWAPMulti",
        "pins": [{"is_input": false, "pin_id": 0, "pin_name": "Output"}, {
          "is_input": true,
          "pin_id": 1,
          "pin_name": "Input"
        }]
      }, {
        "interface_id": 1,
        "interface_name": "FW AP 1/2",
        "pins": [{"is_input": false, "pin_id": 0, "pin_name": "Output"}, {
          "is_input": true,
          "pin_id": 1,
          "pin_name": "Input"
        }]
      }, {
        "interface_id": 2,
        "interface_name": "FW AP SPDIF",
        "pins": [{"is_input": false, "pin_id": 0, "pin_name": "Output"}, {
          "is_input": true,
          "pin_id": 1,
          "pin_name": "Input"
        }]
      }, {
        "interface_id": 3,
        "interface_name": "FW AP 3/4",
        "pins": [{"is_input": false, "pin_id": 0, "pin_name": "Output"}]
      }],
      "wavert_supported": false
    }, {
      "device_id": 2,
      "device_name": "Virtual Audio Cable",
      "device_type": 2,
      "interfaces": [{
        "interface_id": 0,
        "interface_name": "Virtual Cable 2",
        "pins": [{"is_input": true, "pin_id": 0, "pin_name": "Capture"}, {
          "is_input": false,
          "pin_id": 1,
          "pin_name": "Output"
        }]
      }, {
        "interface_id": 1,
        "interface_name": "Virtual Cable 1",
        "pins": [{"is_input": true, "pin_id": 0, "pin_name": "Capture"}, {
          "is_input": false,
          "pin_id": 1,
          "pin_name": "Output"
        }]
      }],
      "wavert_supported": false
    }, {
      "device_id": 3,
      "device_name": "WebCamDV WDM Audio Capture",
      "device_type": 3,
      "interfaces": [{
        "interface_id": 0,
        "interface_name": "WebCamDV Audio",
        "pins": [{"is_input": true, "pin_id": 0, "pin_name": "Recording Control"}, {
          "is_input": false,
          "pin_id": 1,
          "pin_name": "Volume Control"
        }]
      }],
      "wavert_supported": false
    }];
    return response;
  }

  // Session methods
  SessionCurrrentJamTrackPlayPosMs() {
    return 0;
  }

  GetJamTrackSettings() {
    return {tracks: []};
  }

  SessionGetJamTracksPlayDurationMs() {
    return 60000;
  }

  SessionAddTrack() {}

  SessionAudioResync() {
    this.logger.debug("Fake JamClient: SessionAudioResync()");
  }

  getConnectionDetail(arg1, arg2) {
    return {};
  }

  SessionGetAllControlState(isMasterOrPersonal) {
    const mixerIds = this.SessionGetIDs();
    return this.SessionGetControlState(mixerIds, isMasterOrPersonal);
  }

  SessionGetControlState(mixerIds, isMasterOrPersonal) {
    this.dbg("SessionGetControlState");
    const groups = [
      this.ChannelGroupIds.MasterGroup,
      this.ChannelGroupIds.MonitorGroup,
      this.ChannelGroupIds.AudioInputMusicGroup,
      this.ChannelGroupIds.AudioInputChatGroup,
      this.ChannelGroupIds.AudioInputChatGroup,
      this.ChannelGroupIds.UserMusicInputGroup,
      this.ChannelGroupIds.UserChatInputGroup,
      this.ChannelGroupIds.PeerMediaTrackGroup,
      this.ChannelGroupIds.JamTrackGroup,
      this.ChannelGroupIds.MetronomeGroup
    ];
    const names = [
      "FWAPMulti",
      "FWAPMulti",
      "FWAPMulti",
      "FWAPMulti",
      "",
      "",
      "",
      "",
      "",
      ""
    ];

    const media_types = [
      "Master",
      "Monitor",
      "AudioInputMusic",
      "AudioInputChat",
      "StreamOutMusic",
      "UserMusicInput",
      "PeerAudioInputMusic",
      "PeerMediaTrack",
      "JamTrack",
      "MetronomeTrack"
    ];
    const clientIds = [
      "",
      "",
      "",
      "",
      "3933ebec-913b-43ab-a4d3-f21dc5f8955b",
      "",
      "",
      "",
      "",
      ""
    ];
    const response = [];
    for (let i = 0; i < mixerIds.length; i++) {
      // for testing no inputs, set simulateNoInputs = true
      if (this.simulateNoInputs && i === 2) continue;

      response.push({
        client_id: clientIds[i],
        group_id: groups[i],
        id: mixerIds[i] + (isMasterOrPersonal ? 'm' : 'p'),
        master: isMasterOrPersonal,
        media_type: media_types[i],
        monitor: !isMasterOrPersonal,
        mute: false,
        name: names[i],
        range_high: 20,
        range_low: -80,
        record: true,
        stereo: true,
        volume_left: -40,
        volume_right: -40,
        pan: 0,
        instrument_id: 50, // see globals.js
        mode: isMasterOrPersonal,
        rid: mixerIds[i]
      });
    }
    return response;
  }

  SessionGetIDs() {
    return [
      "FWAPMulti_0_10000",
      "FWAPMulti_1_10100",
      "FWAPMulti_2_10200",
      "FWAPMulti_3_10500",
      "User@208.191.152.98#",
      "User@208.191.152.98_*"
    ];
  }

  RegisterRecordingManagerCallbacks(commandStart, commandProgress, commandStop, commandsChanged) {}

  RegisterRecordingCallbacks(startRecordingCallbackName, stopRecordingCallbackName, startedRecordingCallbackName, stoppedRecordingCallbackName, abortedRecordingCallbackName) {
    this.fakeJamClientRecordings.RegisterRecordingCallbacks(startRecordingCallbackName, stopRecordingCallbackName, startedRecordingCallbackName, stoppedRecordingCallbackName, abortedRecordingCallbackName);
  }

  SessionRegisterCallback(callbackName) {
    //console.log("SessionRegisterCallback: " + callbackName);
    this.eventCallbackName = callbackName;
    if (this.callbackTimer) {
      window.clearInterval(this.callbackTimer);
    }
    if (this.eventCallbackName) {
      this.callbackTimer = window.setInterval(() => this.doCallbacks(), this.eventCallbackRate);
    }
  }

  SessionSetAlertCallback(callback) {
    this.alertCallbackName = callback;
    // simulate a backend alert
    // setTimeout(function() {
    //   eval(this.alertCallbackName + '(27)');
    // }, 3000);
  }

  SessionSetConnectionStatusRefreshRate(milliseconds) {
    //this.dbg('SessionSetConnectionStatusRefreshRate: ' + milliseconds);
  }

  SessionSetControlState(stringValue, isMasterOrPersonal) {
    this.dbg('SessionSetControlState: ' + stringValue + " m/p=" + isMasterOrPersonal);
  }

  SessionSetRecordingFilename(filename) {}

  SessionSetRecordingFolder(folderName) {}

  SessionStartPlay() {}

  SessionStartRecording() {}

  SessionStopPlay() {}

  SessionAddPlayTrack() {
    return true;
  }

  SessionRemoveAllPlayTracks() {}

  isSessionTrackPlaying() {
    return false;
  }

  SessionCurrrentPlayPosMs() {
    return 0;
  }

  SessionGetTracksPlayDurationMs() {
    return 0;
  }

  SessionGetDeviceLatency() {
    return 10.0;
  }

  SessionPageEnter() {
    this.logger.debug("FakeJamClient: SessionPageEnter");
    return {};
  }

  SessionPageLeave() {
    this.logger.debug("FakeJamClient: SessionPageLeave");
  }

  SetMixerMode(mode) {}

  SessionGetMasterLocalMix() {
    this.logger.debug('SessionGetMasterLocalMix. Returning: ' + this._mix);
    return this._mix;
  }

  SessionSetMasterLocalMix(level) {
    this.logger.debug('SessionSetMasterLocalMix(' + level + ')');
    this._mix = level;
  }

  SessionSetUserName(client_id, name) {}

  doCallbacks() {
    const names = ["vu"];
    const ids = ["i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~1",
      "i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~2"];

    const args = [];
    for (let i = 0; i < ids.length; i++) {
      for (let j = 0; j < names.length; j++) {
        args.push('"' + names[j] + '"');
        args.push('"' + ids[i] + '"');
        args.push(this.vuValue);
      }
    }
    const js = this.eventCallbackName + '(' + args.join(',') + ')';
    console.log("exec javascript", js);
    eval(js);

    this.vuValue += this.vuChange;
    if (this.vuValue > 10 || this.vuValue < -70) {
      this.vuChange = this.vuChange * -1;
    }
  }

  SetVURefreshRate(rateMS) {
    this.eventCallbackRate = rateMS;
    if (this.callbackTimer) {
      window.clearInterval(this.callbackTimer);
    }
    if (this.eventCallbackName) {
      this.callbackTimer = window.setInterval(() => this.doCallbacks(), this.eventCallbackRate);
    }
  }

  // Track methods
  TrackGetChannels() {
    const devices = [
      "M-Audio FW Audiophile: FW AP 1/2"
    ];
    const suffixes = [" - Left", " - Right", " - Left", " - Right"];
    const lefts = [true, false, true, false];
    const inputs = [true, true, false, false];
    const types = ["music", "non-music", "music", "non-music"];
    const response = [];
    let name, o, i, j;
    for (j = 0; j < 4; j++) {
      for (i = 0; i < devices.length; i++) {
        name = devices[i] + suffixes[j];
        o = {
          device_id: name,
          full_id: name,
          id: name,
          input: inputs[j],
          left: lefts[j],
          name: name,
          device_type: types[j]
        };
        response.push(o);
      }
    }
    return response;
  }

  TrackGetCount() {
    return 1;
  }

  TrackGetInstrument(trackNumber) {
    if (trackNumber === 1) {
      return 100;
    } else {
      return 200;
    }
  }

  TrackGetChatEnable() {
    return true;
  }

  TrackSetChatEnable(chat) {}

  TrackSaveAssignments() {}

  TrackGetDevices() {
    const response = {
      "Built-in": "Built-in",
      "ASIO4ALL v2": "ASIO4ALL v2 - ASIO",
      "BEHRINGER USB AUDIO": "BEHRINGER USB AUDIO - ASIO",
      "{2B7D1F21-FA6E-11D4-0002-DDCA793267F2}": "SoundMAX HD Audio O - WDM",
      "{ABCC5A6C-C263-463B-A72F-A5BF64C86EBA}": "Generic USB Audio Device - WDM"
    };
    return response;
  }

  TrackDeleteProfile(id) {}

  TrackGetMusicDeviceID() {
    return this.device_id;
  }

  TrackSetMusicDevice(id) {
    this.device_id = id;
  }

  TrackGetMusicDeviceNames(input) {
    const names = [];
    names.push("Built-in");
    return names;
  }

  TrackHasControlPanel() {
    return true;
  }

  TrackIsMusicDeviceType(type) {
    return true;
  }

  TrackGetChatUsesMusic() {
    return 0;
  }

  TrackSetChatUsesMusic(usesMusic) {}

  TrackOpenControlPanel() {
    return;
  }

  TrackLoadAssignments() {}

  TrackSave() {}

  TrackSetAssignment(chanId, isInput, assignment) {}

  TrackGetAssignment(chanId, isInput) {
    return 0;
  }

  TrackSetCount(count) {}

  TrackSetInstrument(track, instrumentId) {}

  JamTrackStopPlay() {}

  JamTrackPlay() {
    return true;
  }

  JamTrackIsPlayable() {
    return true;
  }

  JamTrackGetTrackDetail() {
    return {key_state: 'unknown'};
  }

  JamTrackKeysRequest() {}

  JamTrackDownload() {}

  UpdateMixer(mixerId) {}

  // Scoring knobs
  GetScoreWorkTimingInterval() {
    return {interval: 1000, backoff: 60000};
  }

  SetScoreWorkTimingInterval(knobs) {
    return true;
  }

  SessionOpenBackingTrackFile(path, loop) {
    this.backingTrackPath = path;
    this.backingTrackLoop = loop;
  }

  SessionSetBackingTrackFileLoop(path, loop) {
    this.backingTrackPath = path;
    this.backingTrackLoop = loop;
  }

  SessionCloseBackingTrackFile(path) {
    this.backingTrackPath = "";
  }

  SessionOpenMetronome(bpm, click, meter, mode) {
    this.logger.debug("Setting metronome BPM: ", bpm);
    this.metronomeActive = true;
    this.metronomeBPM = bpm;
    this.metronomeSound = click;
    this.metronomeMeter = meter;
  }

  SessionSetMetronome(bpm, click, meter, mode) {
    this.SessionOpenMetronome(bpm, click, meter, mode);
  }

  SessionCloseMetronome() {
    this.metronomeActive = false;
  }

  setMetronomeOpenCallback(callback) {}

  getMyNetworkState() {
    return {
      ntp_stable: Math.random() > 0.5
    };
  }

  getPeerState(clientId) {
    return {
      ntp_stable: Math.random() > 0.5
    };
  }

  // stun
  NetworkTestResult() {
    return {remote_udp_blocked: false};
  }

  // Client Update Functions
  IsAppInWritableVolume() {
    return true;
  }

  ClientUpdateVersion() {
    return "Compiled 1.2.3";
  }

  ClientUpdateStartDownload(url, progressCallback, successCallback, failureCallback) {
    // simulate a bunch of download callbacks
    let count = 0;
    const max = 100;
    let bytesReceived = 0;
    const bytesTotal = 10000;

    const fire = () => {
      count++;
      window.setTimeout(() => {
        bytesReceived = (count / max) * bytesTotal;
        //window.JK.ClientUpdate.DownloadProgressCallback(bytesReceived, bytesTotal, 0, 0);

        if (count < max) {
          fire();
        } else {
          //window.JK.ClientUpdate.DownloadFailureCallback("/some/path/here");
        }
      }, 50);
    };

    fire();
  }

  getBackingTrackList() {
    return {
      backing_tracks: [
        {name: "This is a really long name for a song dude.mp3", size: 4283},
        {name: "foo.mp3", size: 325783838}
      ]
    };
  }

  ClientUpdateStartUpdate(path, successCallback, failureCallback) {}

  // fake jam client methods
  SetFakeRecordingImpl(fakeRecordingsImpl) {
    this.fakeJamClientRecordings = fakeRecordingsImpl;
  }

  TestNetworkPktBwRate(targetClientId, successCallback, timeoutCallback, testType, duration, numClients, payloadSize) {
    const progress = {
      progress: true,
      upthroughput: .95,
      downthroughput: .95,
      upjitter: 2.3,
      downjitter: 2.3
    };
    let count = 0;
    const interval = setInterval(() => {
      eval(`${successCallback}(${JSON.stringify(progress)});`);

      if (progress.upthroughput < 1) {
        progress.upthroughput += .05;
      }
      if (progress.downthroughput < 1) {
        progress.downthroughput += .05;
      }

      count++;
      if (count === duration) {
        clearInterval(interval);

        delete progress['progress'];
        progress.pass = true;
        eval(`${successCallback}(${JSON.stringify(progress)});`);
      }
    }, 1000);
  }

  StopNetworkTest(targetClientId) {}

  OnLoggedIn(userId, sessionToken) {}

  OnLoggedOut() {}

  UserAttention(option) {}

  SessionGetMacHash() {
    return null;
  }

  IsFrontendVisible() {
    return true;
  }

  LastUsedProfileName() {
    console.log("FakeJamClient: LastUsedProfileName");
    return 'default';
  }

  SetLastUsedProfileName(name) {
    return true;
  }

  log(level, message) {
    console.log("beep : " + message);
  }

  getOperatingMode() {
    if (window.location.pathname === '/latency_tester') {
      return 'server';
    } else {
      return 'client';
    }
  }

  // passed an array of recording objects from the server
  GetLocalRecordingState(recordings) {
    const result = {recordings: []};
    const recordingResults = result.recordings;

    const possibleAnswers = ['HQ', 'RT', 'MISSING', 'PARTIALLY_MISSING'];

    recordings.forEach((recording) => {
      // just make up a random yes-hq/yes-rt/missing answer
      const recordingResult = {};
      recordingResult['aggregate_state'] = possibleAnswers[Math.floor((Math.random() * 4))];
      recordingResults.push(recordingResult);
    });

    return result;
  }

  OpenRecording(claimedRecording) {
    return {success: true};
  }

  PreviewRecording(claimedRecording) {
    return this.OpenRecording(claimedRecording);
  }

  CloseRecording() {}

  ClosePreviewRecording() {
    this.CloseRecording();
  }

  OnDownloadAvailable() {}

  SaveToClipboard(text) {}

  IsNativeClient() {
    return false;
  }

  IsAudioStarted() {
    this.logger.debug("FakeJamClient: IsAudioStarted");
    return false;
  }

  StopAudio() {
    this.logger.debug("FakeJamClient: StopAudio");
  }

  ResetPageCounters() {
    this.logger.debug("FakeJamClient: ResetPageCounters");
  }

  ReloadAudioSystem(leaveRunning, loadLastProfile, reinitAssignments) {
    this.logger.debug("FakeJamClient: ReloadAudioSystem");
    return {};
  }

  SessionLiveBroadcastStart(host, port, mount, sourceUser, sourcePass, preferredClientId, bitrate) {
    this.logger.debug("SessionLiveBroadcastStart requested");
  }

  SessionLiveBroadcastStop() {
    this.logger.debug("SessionLiveBroadcastStop requested");
  }

  RegisterQuitCallback() {}

  LeaveSessionAndMinimize() {}

  SetAutoStart() {}

  GetAutoStart() {
    return true;
  }

  SaveSettings() {}

  GetUseStaticPort() {
    return false;
  }

  SetUseStaticPort() {}

  GetStaticPort() {
    return 0;
  }

  SetStaticPort() {}

  VSTLoad() {}

  VSTScan(callback) {
    setTimeout(() => eval(`${callback}()`), 1000);
  }

  hasVstHost() {
    return false;
  }

  getPluginList() {
    return {vsts: []};
  }

  clearPluginList() {}

  listTrackAssignments() {
    return {};
  }

  UpdateSessionInfo(info) {
    this.logger.debug("FakeJamClient: UpdateSessionInfo: %o", info);
    //this.sessionInfo = info;
  }
}
