const Player = require('./clsPlayer').clsPlayer;
//const Logger = require('../logs/clsLogger').clsLogger;

const streamPlayer = class streamPlayer {
  constructor({ db, utils, config, musicVolume = 100, decodeOnly = false, logger } = {}) {
    this.db = db;
    this.utils = utils;
    this.config = config;
    this.musicVolume = musicVolume;
    this.mixerVolume = musicVolume;
    this.decodeOnly = decodeOnly;
    this.logger = logger;
    this.volume = 0;
    this.channel = 0;
    this.player = 0;
    this.position = 0;
    this.positionInterval = null;
    this.isPlaying = false;
    this.retryCnt = 0;
    this.maxRetryCnt = 6;
    this.streamEvents = {
        streamPlayerStarted: 'streamPlayerStarted',
        streamPlayerStopped: 'streamPlayerStopped',
        streamPlayerError: 'streamPlayerError',
        vumeter: 'vumeter',
        positionChange: 'positionChange',
        fileChanged: 'filesChanged',
        stateChanged: 'stateChanged',
        urlChanged: 'urlChanged'
    };

    let strPlayer = new Player();
    this._strPlayer = strPlayer;
    this.streamUrl = '';
    // events
    strPlayer.on(strPlayer.playerEvents.vumeter, (left, right) => {
      if(this.isPlaying) {
          this.emit(this.streamEvents.vumeter, left, right);
      }
    });
    strPlayer.on(strPlayer.playerEvents.positionChange, (pos, dur, chan) => {
        this.emit(this.streamEvents.positionChange, pos, dur, chan);
    });


    strPlayer.on(strPlayer.playerEvents.fileChanged, () => {

    });
    strPlayer.on(strPlayer.playerEvents.stateChanged, (state) => {
        this.emit(this.streamEvents.stateChanged, state);
    });
    this.db.on(this.db.eventTypes.StreamUrlChanged, (url) => {
        this.emit(this.streamEvents.urlChanged, url);
    });

    this.db.on(this.db.eventTypes.StreamUnavailable, () => {
      this.stopStream();
    });
      // events

    strPlayer.initSoundCard(this.config.Settings.soundCard, strPlayer.speakerAttributes.BASS_DEVICE_SPEAKERS);
    let streamFlags = strPlayer.bassAttributes.BASS_SAMPLE_FLOAT;
    let speaker = strPlayer.bassAttributes.BASS_SAMPLE_DEFAULT;
    let chanCount = 2;
    this.addChannelFlags = strPlayer.mixerAttributes.BASS_MIXER_BUFFER;
    //check config mono out and what channel to play

    if (this.config.MultiOutput.isEnabled) {
      switch(parseInt(this.config.MultiOutput.Channel)) {
        case 1:
          speaker = strPlayer.speakerAttributes.BASS_SPEAKER_FRONT;
          break;
        case 2:
          speaker = strPlayer.speakerAttributes.BASS_SPEAKER_REAR;
          break;
        case 3:
          speaker = strPlayer.speakerAttributes.BASS_SPEAKER_CENLFE;
          chanCount = 1;
          break;
        case 4:
          speaker = strPlayer.speakerAttributes.BASS_SPEAKER_REAR2;
          break;
        case 5:
          speaker = strPlayer.speakerAttributes.BASS_SPEAKER_LEFT;
          chanCount = 1;
          this.addChannelFlags = strPlayer.mixerAttributes.BASS_MIXER_DOWNMIX | strPlayer.mixerAttributes.BASS_MIXER_BUFFER;
          break;
        case 6:
          speaker = strPlayer.speakerAttributes.BASS_SPEAKER_RIGHT;
          chanCount = 1;
          this.addChannelFlags = strPlayer.mixerAttributes.BASS_MIXER_DOWNMIX | strPlayer.mixerAttributes.BASS_MIXER_BUFFER;
          break;
        case 7:
          speaker = strPlayer.speakerAttributes.BASS_SPEAKER_FRONTLEFT;
          chanCount = 1;
          this.addChannelFlags = strPlayer.mixerAttributes.BASS_MIXER_DOWNMIX | strPlayer.mixerAttributes.BASS_MIXER_BUFFER;
          break;
        case 8:
          speaker = strPlayer.speakerAttributes.BASS_SPEAKER_FRONTRIGHT;
          chanCount = 1;
          this.addChannelFlags = strPlayer.mixerAttributes.BASS_MIXER_DOWNMIX | strPlayer.mixerAttributes.BASS_MIXER_BUFFER;
          break;
        case 9:
          speaker = strPlayer.speakerAttributes.BASS_SPEAKER_REARLEFT;
          chanCount = 1;
          this.addChannelFlags = strPlayer.mixerAttributes.BASS_MIXER_DOWNMIX | strPlayer.mixerAttributes.BASS_MIXER_BUFFER;
          break;
        case 10:
          speaker = strPlayer.speakerAttributes.BASS_SPEAKER_REARRIGHT;
          chanCount = 1;
          this.addChannelFlags = strPlayer.mixerAttributes.BASS_MIXER_DOWNMIX | strPlayer.mixerAttributes.BASS_MIXER_BUFFER;
          break;
        case 11:
          speaker = strPlayer.speakerAttributes.BASS_SPEAKER_CENTER;
          chanCount = 1;
          this.addChannelFlags = strPlayer.mixerAttributes.BASS_MIXER_DOWNMIX | strPlayer.mixerAttributes.BASS_MIXER_BUFFER;
          break;
        case 12:
          speaker = strPlayer.speakerAttributes.BASS_SPEAKER_LFE;
          chanCount = 1;
          this.addChannelFlags = strPlayer.mixerAttributes.BASS_MIXER_DOWNMIX | strPlayer.mixerAttributes.BASS_MIXER_BUFFER;
          break;
        case 13:
          speaker = strPlayer.speakerAttributes.BASS_SPEAKER_REAR2LEFT;
          chanCount = 1;
          this.addChannelFlags = strPlayer.mixerAttributes.BASS_MIXER_DOWNMIX | strPlayer.mixerAttributes.BASS_MIXER_BUFFER;
          break;
        case 14:
          speaker = strPlayer.speakerAttributes.BASS_SPEAKER_REAR2RIGHT;
          chanCount = 1;
          this.addChannelFlags = strPlayer.mixerAttributes.BASS_MIXER_DOWNMIX | strPlayer.mixerAttributes.BASS_MIXER_BUFFER;
          break;
        default:
          chanCount = 2;
          break;
      }
    }
    if(this.decodeOnly) {
      speaker = strPlayer.bassAttributes.BASS_STREAM_DECODE;
    }

    //create mixer
    let mixer = strPlayer.createMixer(chanCount, speaker);
    this.mixer = mixer;
    strPlayer.setVolume(mixer, this.mixerVolume);
    //this.playStream();
    strPlayer.on(strPlayer.playerEvents.streamPlayerError, () => {
      this.retryCnt++;
      // max retry count set to 6 for total of 30 secs of retries. clsPlayer checks every 5 secs.
      if(this.retryCnt % this.maxRetryCnt == 0) {
        // after 30 secs of retrying, emit stop, so that if schedules are enabled, schedule pl will play.
        this.emit(this.streamEvents.streamPlayerStopped);
        this.isPlaying = false;
        this.retryCnt = 0;
      } else {
        this.logger.StreamUrl(`retrying opening stream, try: ${this.retryCnt}`);
        this.playStream();
      }
    });
  }

  async getStream() {
    let streams = (await this.db.Streams.findAll());
    if (streams) {
      let activeStream = streams.where(c => c.isActive);
      return activeStream;
    } else{
      return null;
    }
  }

  async playStream() {
    let availablePlayTime = await this.db.getAvailablePlayTime();
    if(availablePlayTime) {
      let isNowOkForPlay = await this.db.IsNowOkForPlayback(
          availablePlayTime.startTime, availablePlayTime.endTime);
      if (!isNowOkForPlay) {
          if(this.streamUrl == '') {
              this.isPlaying = false;
              this.emit(this.streamEvents.streamPlayerStopped);
              if(this.channel != 0) {
                  this.player.StopChannel(this.channel);
                  this.channel = 0;
              }
              return;
          }
        this.logger.StreamUrl(`Not a good time to play stream, time must be between ${availablePlayTime.startTime} and ${availablePlayTime.endTime}`);
        this.isPlaying = false;
        setTimeout(() => {
          this.playStream();
        }, 2000);
        return;
      }
    }
    let activeStream = await this.getStream();
    if(activeStream.length == 0 || activeStream == null) {
        this.isPlaying = false;
      this.emit(this.streamEvents.streamPlayerStopped);
      if(this.channel != 0) {
        this.player.StopChannel(this.channel);
        this.channel = 0;
      }
      this.logger.StreamUrl('There are no active streams in database.');
      return;
    }
    let activeStreams=activeStream.select(c=>c.dataValues);
    this.streamUrl = (activeStreams || activeStreams.length > 0) ? activeStreams[0].url: '';
    if(this.streamUrl == '' || this.streamUrl == undefined) {
        this.isPlaying = false;
      setTimeout(() => {
        this.emit(this.streamEvents.streamPlayerStopped);
        if(this.channel != 0) {
          this.player.StopChannel(this.channel);
          this.channel = 0;
        }
        this.volume = 0;
        this.logger.StreamUrl(`Stopping Stream player, stream url is empty or undefined`);
        this.playStream();
      }, 500);
      return;
    }

    let p = new Player();
    this.player = p;
    let chan = p.openStreamUrl(this.streamUrl, this._strPlayer.bassAttributes.BASS_STREAM_DECODE);
    let err = parseInt(p.getError());
    if (chan == 0 || err != 0) {
      this.logger.StreamUrl(`Error opening stream url, err: ${p.getError()}, stream url: ${this.streamUrl}`);
        this.isPlaying = false;
      this.emit(this.player.playerEvents.streamPlayerError, err);
    }

    this.channel = chan;
      this.isPlaying = true;
    this.emit(this.streamEvents.streamPlayerStarted);
    this.logger.StreamUrl(`playing stream: ${this.streamUrl}`);
    this.positionInterval = setInterval(() =>{
      this.position++;
        this.emit(this.streamEvents.positionChange, this.position);
    }, 1000);
    let result = this._strPlayer.addChannelToMixer(chan, this.addChannelFlags);
  }

  stopStream() {
      this.isPlaying = false;
      this.emit(this.streamEvents.streamPlayerStopped);
    if(this.channel != 0) {
      this.player.StopChannel(this.channel);
      this.logger.StreamUrl(`Stopping stream`);
      if(this.positionInterval != null) {
          clearInterval(this.positionInterval);
      }
      this.position = 0;
      this.channel = 0;
    }
  }

  //test code
  /*p(){
    let p = new Player();
    let chan = p.openUrl('http://carrefour.smg.com.tr:8000/hiper', this._strPlayer.bassAttributes.BASS_STREAM_DECODE);
    if (chan == 0 || parseInt(p.getError()) != 0) {
      this.logger.StreamUrl(`Error opening stream url, err: ${this._strPlayer.getError()}`);

    }
    let result = this._strPlayer.addChannelToMixer(chan, this.addChannelFlags);
  }*/
};

const util = require('util');
const EventEmitter = require('events').EventEmitter;
util.inherits(streamPlayer, EventEmitter);
module.exports = streamPlayer;