let moment=require('moment');
let clsLogger=require('../../logs/clsLogger').clsLogger;
let logger=new clsLogger();
let schedule = require('node-schedule');
let rootPath=process.cwd();
let path=require('path');
let PlayListPlayer = require('./newPlaylistPlayer');
let pl = new PlayListPlayer();
require('linqjs');
let _ = require('lodash');
class schedulePlayer{
    constructor({db,moment,config,utils,logger,schedulesdb,announcesdb}){
        this.db=db;
        this.moment=moment;
        this.config=config;
        this.utils=utils;
        this.logger=logger;
        this.schedulesdb=schedulesdb;
        this.announcesdb=announcesdb;
        this._initSystem()
    }

    async _initSystem(){
        /*
        let PlaylistPlayer=require('./clsPlaylistPlayer').PlaylistPlayer
        this.pl=new PlaylistPlayer()
        pl.on(pl.eventTypes.playlistFinished,(chan)=>{})
        pl.on(pl.eventTypes.positionChange,(position,duration,channel)=>{})
        pl.on(pl.eventTypes.stateChanged,()=>{})
        pl.on(pl.eventTypes.vumeter,()=>{})
        pl.on(pl.eventTypes.Error,()=>{})
        pl.on(pl.eventTypes.fadeInStarted,()=>{})
        pl.on(pl.eventTypes.fadeOutStarted,()=>{})
*/
        //TODO: when running globally, just use global.db instead of this instance..

        //if there is a change on db, then reschedule recurrent jobs.
        this.db.on(this.db.eventTypes.TimeExceptions_bulkInsert,(fileArray)=>{
            this.setSchedules();
            this.checkNextPlay()
        });
        this.db.on(this.db.eventTypes.GlobalTimeExceptions_bulkInsert,(fileArray)=>{
            this.setSchedules();
            this.checkNextPlay()
        });
        this.db.on(this.db.eventTypes.Schedules_Details_bulkInsert,(fileArray)=>{
            this.setSchedules();
            this.checkNextPlay()
        });
        //this.db=global.db;
        this.checkNextPlayInterval=2000;
       await this.setSchedules();

       logger.PlayerEventsSchedules('schedulePlayer started');

        await this.checkNextPlay();
    }

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

    //------------------ scheduling---------------------------------------------------
    async setSchedules(){
        //instead of checking next play, we get all active schedules and we create recurrence jobs for each.
        //this jobs will fire between start and end dates on selected time and 'days'

        //first lets clear previous jobs if we have
        logger.PlayerEventsSchedules('creating recurrence jobs for schedules');
        this.clearSchedules();
        //get all active schedules
        let q1=await this.schedulesdb.getSchedules();
        let q=(q1).where(c=>c.isActive);
        logger.PlayerEventsSchedules(` we have ${q.length} active Schedules..creating jobs for them.. `);
        //now schedule each of them
        q.forEach(c=>this.setSchedule(c.scheduleID,c.startDate,c.endDate,c.startTime,c.daysEffected,this.checkNextPlay));
        logger.PlayerEventsSchedules('creating jobs completed')
    }

    setSchedule(scheduleID,startDate,endDate,startTime,daysEffected,targetFunction){
        let arr=[];
        for (let i=0;i<7;i++){if((daysEffected & Math.pow(2,i))==Math.pow(2,i))arr.push(i)}
        let start=startTime.toObject();
        let rule = new schedule.RecurrenceRule();
        rule.dayOfWeek = arr;
        rule.hour = start.hours;
        rule.minute =start.minutes;
        rule.second=start.seconds;
        let j = schedule.scheduleJob('schedule' + scheduleID,{start:startDate,end:endDate, rule:rule}, ()=>{
            if (targetFunction && (typeof targetFunction == "function")) {
                targetFunction();
            }
        });
    }

    clearSchedules(){
        logger.PlayerEventsSchedules('Clearing old jobs');
        for(let key in schedule.scheduledJobs){
            if(key.startsWith('schedule')) {
                schedule.scheduledJobs[key].cancel()
            }
        }
    }
    //------------------ scheduling---------------------------------------------------


    async checkNextPlay(){
        logger.PlayerEventsSchedules('checking next playlist');
        let availablePlayTime=await this.db.getAvailablePlayTime();
        let IsNowOkForPlayback=this.db.IsNowOkForPlayback(availablePlayTime.startTime,availablePlayTime.endTime);
        if(!IsNowOkForPlayback){
            logger.PlayerEventsSchedules('not a good time to play, time must be between' + availablePlayTime.startTime.toJSON() + ' and ' + availablePlayTime.endTime.toJSON());
            //TODO: set volume of playlist manager to 0
            setTimeout(()=>{this.checkNextPlay()},this.checkNextPlayInterval);
            return;
        }
        //TODO: set volume of playlist manager to original Value
        let schedulesToPlay=await this.schedulesdb.getSchedulesToPlay();
        if (schedulesToPlay==null || !schedulesToPlay.startTime){
            logger.PlayerEventsSchedules('no files to play.. ');
            //TODO: clear playlist, there is no file to play
            pl.clearPlaylist();
            setTimeout(() => {this.checkNextPlay()}, this.checkNextPlayInterval);
            return;
        }
        let filesToPlay= await schedulesToPlay.Schedules_Details;
        //TODO:if there is no file to play, then checkNextPlay again.
        if (filesToPlay==null || filesToPlay.length==0){
            logger.PlayerEventsSchedules('no files in details to play.. ');
            //TODO: clear playlist, there is no file to play
            pl.clearPlaylist();
            setTimeout( ()=>{ this.checkNextPlay()},this.checkNextPlayInterval);
        }

        //TODO: set playlist manager's start and end Time
        let startTime=schedulesToPlay.startTime.isAfter(availablePlayTime.startTime)?schedulesToPlay.startTime:availablePlayTime.startTime;
        let endTime=schedulesToPlay.endTime.isBefore(availablePlayTime.endTime)?schedulesToPlay.endTime:availablePlayTime.endTime;
        console.log('dur');
        //todo: now send this list (schedulesToPlay)  to playlist player..
        if(schedulesToPlay.Schedules_Details.length > 0) {
            schedulesToPlay = await schedulesToPlay.Schedules_Details;
            let filesWeHave = await this.checkFileExists(schedulesToPlay); // remove half downloaded songs from schedulesToPlay and push clean array to playlist player.
            filesWeHave = await filesWeHave.select(c => { return c.FileName });
            pl.songs.push(filesWeHave);
            await pl.start();
        }
    }
    //todo: differenceBy returns all database files ignoring what we have. find a fix using lodash
    async checkFileExists(files) {
      let fpath = this.utils.SchedulesSaveDir;
      let localFiles = await this.utils.getFilesInfo(fpath);
      if (localFiles == undefined || localFiles == null) return [];
      let filesToPlay = await files.select(c => { return { FileName: c.FileName, FileSize: c.FileSize } });
      localFiles = await localFiles.select(c => {
        return { FileName: c.file, FileSize: c.size }
      });
      // we take intersection of what we already have and files to play and return whats supposed to play from local drive.
      let filesWeHave = await _.intersectionBy(filesToPlay, localFiles, 'FileName');
      if(filesWeHave.length > 0) {
        return filesWeHave;
       }//else {
      //     //todo: return localFiles if we dont have any of the files to play in drive?
      //   return localFiles;
      // }
    }
}

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