const PlaylistManager = require("./PlaylistManager");
const LogDB = require("../db/logDB");
const _ = require("lodash");
const ini = require("ini");
const fs = require("fs");

const djPlayer = class djPlayer1 {
  constructor({
    db,
    moment,
    config,
    utils,
    schedulesdb,
    decodeOnly = true,
    logger
  } = {}) {
    this.db = db;
    this.moment = moment;
    this.config = config;
    this.utils = utils;
    this.schedulesdb = schedulesdb;
    this.decodeOnly = decodeOnly;
    this.logger = logger;
    this.logsDB = new LogDB();
    this.activePlaylist = null;
    this.currentList = "";
    this.isLooping = this.config.DJ.DJLoop != '0';
    this.autoStart = this.config.DJ.DJAutoStart != '0';
    this.enableShuffle = false;
    this.lastPlayedArray = [];
    this.activeFile = null;
    this.channel = 0;
    this.isPlaying = false;
    this.scheduleID = this.config.DJ.DJScheduleID;
    this.enableAnnounces = false;
    this.pl = new PlaylistManager({
      moment,
      utils,
      config,
      isLooping: this.isLooping,
      autoStart: true,
      fadeInEnabled: true,
      musicVolume: 100,
      decodeOnly: this.decodeOnly,
      logger
    });
    this.pl.isDJ = true;

    if (this.autoStart) {
      this.Play(this.scheduleID, true, this.isLooping, this.enableShuffle);
    }

    this.eventTypes = {
      playlistFinished: "playlistFinished",
      stateChanged: "stateChanged",
      fileChanged: "fileChanged",
      positionChange: "positionChange",
      vumeter: "vumeter",
      djPlayerStarted: "djPlayerStarted",
      djPlayerStopped: "djPlayerStopped"
    };

    this.nowPlaying = {
      FileName: "",
      OriginalName: "",
      TotalFiles: 0,
      TotalDownloaded: 0,
      Position: 0,
      Duration: 0,
      vumeterLeft: 0,
      vumeterRight: 0,
      FilePosition: 0
    };

    this.pl.on(
      this.pl.playerEvents.fileChanged,
      async (chan, FileName, activeFile, duration, index) => {
        this.nowPlaying.TotalFiles = this.pl.files.length;
        this.nowPlaying.TotalDownloaded = this.pl.totalDownloaded;
        this.nowPlaying.FilePosition = index + 1;
        this.nowPlaying.FileName = activeFile.FileName;
        this.nowPlaying.OriginalName = activeFile.OriginalName;
        this.emit(
          this.eventTypes.fileChanged,
          chan,
          FileName,
          activeFile,
          duration,
          index
        );
        this.activeFile = activeFile;
        this.setLastPlayedSong(activeFile);
        this.logger.PlayingDj(
          `${activeFile.FileName} - ${activeFile.OriginalName} - ${this.moment({
            hour: 0,
            minute: 0,
            second: 0
          })
            .second(duration)
            .format("mm:ss")}`
        );
      }
    );
    this.pl.on(this.pl.playerEvents.playlistFinished, () => {
      if(!this.isLooping) {
        this.isPlaying = false;
        this.emit(this.eventTypes.djPlayerStopped);
      }
    });

    this.pl.on(
      this.pl.playerEvents.positionChange,
      (activeFile, pos, dur, position, duration) => {
        this.nowPlaying.Duration = duration;
        this.nowPlaying.Position = position;
        this.emit(
          this.eventTypes.positionChange,
          activeFile,
          pos,
          dur,
          position,
          duration
        );
      }
    );

    this.pl.on(this.pl.playerEvents.stateChanged, state => {
      this.emit(this.eventTypes.stateChanged, state);
    });

    this.pl.on(this.pl.playerEvents.vumeter, (left, right) => {
      if (this.isPlaying) {
        this.nowPlaying.vumeterLeft = left;
        this.nowPlaying.vumeterRight = right;
        this.emit(this.eventTypes.vumeter, left, right);
      }
    });

    this._initSystem();
  }

  async _initSystem() {
    this.pl.isTimeManagementEnabled = false;
    this.logger.PlayerEventsDJ("Dj player started");
  }

  get mixer() {
    return this.pl.mixer;
  }

  async initdb() {
    await this.db.initsync();
  }

  // 8.7.12 last played song from playlist in memory
  setLastPlayedSong(activeFile) {
    let currentList = this.lastPlayedArray.where(c => c.playlistID == activeFile.scheduleID);

    if(currentList.length == 0) {
      this.lastPlayedArray.push({playlistID: activeFile.scheduleID, FileName: activeFile.FileName});
    }
    else {
      currentList[0].FileName = activeFile.FileName;
    }
  }

  async Play(scheduleID, canAnnouncePlay, isLooping, shuffle) {
    this.scheduleID = scheduleID;
    this.isLooping = isLooping;
    this.config.DJ.DJScheduleID = this.scheduleID;
    this.config.DJ.DJLoop = this.isLooping ? 1 : 0;

    // write changes to config.ini
    try {
      fs.writeFileSync(
        this.utils._filepath,
        ini.stringify(this.config, { section: "" })
      );
    } catch (e) {
      this.logger.PlayerEventsDJ(
        `Error writing schedule: ${this.scheduleID} and looping: ${
          this.isLooping
        } to config.ini, err: ${e.message}`
      );
    }

    // define player settings
    this.enableAnnounces = canAnnouncePlay;
    this.pl.isLooping = isLooping;
    this.enableShuffle = shuffle;

    let schedules = await this.schedulesdb.getSchedules();
    if (!schedules || schedules.length == 0) {
      setTimeout(() => {
        this.Play(
          this.scheduleID,
          this.enableAnnounces,
          this.pl.isLooping,
          this.enableShuffle
        );
      }, 500);
      this.isPlaying = false;
      return;
    }

    let schedulesToPlay = schedules.where(c => c.scheduleID == this.scheduleID);
    if (!schedulesToPlay || schedulesToPlay.length == 0) {
      this.logger.PlayerEventsDJ(
        `selected schedule is not present in db, schedule ID: ${
          this.scheduleID
        }`
      );
      setTimeout(() => {
        this.Play(
          this.scheduleID,
          this.enableAnnounces,
          this.pl.isLooping,
          this.enableShuffle
        );
      }, 500);
      this.isPlaying = false;
      return;
    }

    let filesToPlay = schedulesToPlay[0].Schedules_Details;
    if (!filesToPlay || filesToPlay.length == 0) {
      this.logger.PlayerEventsDJ(
        `there are no schedule details available for selected playlist: ${
          this.scheduleID
        }`
      );
      this.isPlaying = false;
      this.emit(this.eventTypes.djPlayerStopped);
      return;
    }

    this.activePlaylist = schedulesToPlay;
    this.currentList = schedulesToPlay[0].scheduleName;
    this.isPlaying = true;
    this.emit(this.eventTypes.djPlayerStarted);
    // v 8.7.12 dj playlist last song state in memory
    let lastSong = null;
    let lastPlayed = this.lastPlayedArray.where(c => c.playlistID == scheduleID);
    if(lastPlayed.length > 0) {
      lastSong = lastPlayed[0];
    }

    if (this.enableShuffle) {
      this.pl.addFiles(_.shuffle(filesToPlay), lastSong);
    } else {
      this.pl.addFiles(filesToPlay, lastSong);
    }
    this.logger.PlayerEventsDJ(
      `added scheduleID: ${this.scheduleID} - ${
        filesToPlay.length
      } songs to playlist manager`
    );
  }

  playNext() {
    this.logger.PlayerEventsDJ("playing next song");
    if (this.pl.activeChannel && this.pl.activeChannel != 0) {
      this.pl.activeScheduleChannels.remove(this.pl.activeChannel);
      this.pl.player.StopChannel(this.pl.activeChannel);
      this.pl.Play();
    }
  }

  Stop() {
    this.isPlaying = false;
    this.emit(this.eventTypes.djPlayerStopped);
    if (this.pl.activeChannel != 0) {
      this.nowPlaying.TotalFiles = 0;
      this.nowPlaying.Duration = 0;
      this.nowPlaying.Position = 0;
      this.nowPlaying.FileName = "";
      this.nowPlaying.FilePosition = 0;
      this.nowPlaying.OriginalName = "";
      this.pl.clearFiles();
      if (this.pl.activeChannel != 0) {
        this.pl.activeScheduleChannels.remove(this.pl.activeChannel);
        this.pl.player.StopChannel(this.pl.activeChannel);
      }
    }
  }
  Shuffle() {
    this.logger.PlayerEventsDJ(
      "Clearing playlist manager files, shuffling active list and readding to playlist manager files"
    );
    this.pl.clearFiles();
    //this.enableShuffle = true;
    if (this.enableShuffle && this.isPlaying) {
      this.logger.PlayerEventsDJ(
        "Shuffle is enabled while already playing, clearing list and adding shuffled list."
      );
      let shuffled = _.shuffle(this.activePlaylist[0].Schedules_Details);
      //add files method requires last song played, therefor we pass null
      this.pl.addFiles(shuffled, null);
    } else {
      if (this.isPlaying) {
        this.logger.PlayerEventsDJ(
          "Shuffle is disabled while already playing, clearing list and adding unshuffled list"
        );
        this.pl.addFiles(this.activePlaylist[0].Schedules_Details);
      }
    }
  }

  Loop() {
    this.logger.PlayerEventsDJ(`Setting loop ${this.isLooping}`);
    this.pl.isLooping = this.isLooping;
  }

  async getCurrentPlaylist() {
    let schedules = await this.schedulesdb.getSchedules();
    let downloaded = schedules
      .select(c => {
        return {
          scheduleID: c.scheduleID,
          scheduleName: c.scheduleName,
          total: c.Schedules_Details.where(d => d.isDownloaded == 1).length
        };
      })
      .orderBy(d => d.scheduleName);
    schedules = null;
    return downloaded;
  }
};
const util = require("util");
const EventEmitter = require("events").EventEmitter;
util.inherits(djPlayer, EventEmitter);
module.exports = djPlayer;
