let moment=require('moment');
let clsLogger=require('../logs/clsLogger').clsLogger;
let logger=new clsLogger();
let schedule = require('node-schedule');
let path=require('path');
let PlaylistManager=require('./PlaylistManager')
let pl=null;
const LogDB = require('../db/logDB');
require('linqjs');
let _ = require('lodash');
let announcePlayer=class announcePlayer{
    constructor({db,moment,config,utils,logger,schedulesdb,announcesdb,decodeOnly=true}) {
        this.db = db;
        this.moment = moment;
        this.config = config;
        this.utils = utils;
        this.logger = logger;
        this.schedulesdb = schedulesdb;
        this.announcesdb = announcesdb;
        this.decodeOnly = decodeOnly;
        this.logDB = new LogDB;
        pl = new PlaylistManager({
            moment, utils, config,
            isLooping: false,
            shuffle: false  ,
            autoStart: false,
            minFileCountToPlay: 1,
            fadeInEnabled: false,
            musicVolume: 100,
            decodeOnly: this.decodeOnly
        })
        this.activePlaylist = null;
        this.pl = pl;
        
        this._initSystem();
    }

    async _initSystem(){


        /*
       todo:implement these events
        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,()=>{})
    */


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

        this.db.on(this.db.eventTypes.Schedules_Details_bulkUpdate,async(fileArray)=>{
            //received update of file

            for(let i=0;i<fileArray.length;i++){
                pl.setFileStatus(fileArray[i].FileName,fileArray[i].isDownloaded)
            }

        });

        
        this.checkNextPlayInterval=2000;
        //todo: also set schedules for globalExceptions and timeExceptions
        await this.setSchedules();
        //await this.setGlobalExceptions();
       // await this.setTimeExceptions();

        logger.PlayerEventsAnnounces('announcePlayer started');


    }

    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.PlayerEventsAnnounces('creating recurrence jobs for schedules');
        this.clearSchedules();
        //get all active schedules
        let q1=await this.announcesdb.getAnnounces();
        if (!q1)return;
        let q=await (q1).where(c=>c.isActive && c.isOpeningAnnounce==0 && c.isClosingAnnounce==0);
        logger.PlayerEventsAnnounces(` we have ${q.length} active announces..creating jobs for them.. `);
        //now schedule each of them
        //first for the startTimes
        await q.forEach(c=>this.setSchedule(c.scheduleID,c.startDate,c.endDate,c.startTime,c.daysEffected));

        // now get the opening and closings
       let q2=await (q1).where(c=>c.isActive && (c.isOpeningAnnounce==1 || c.isClosingAnnounce==1) && c.delayTime!=0).select(c=>c.delayTime).distinct();
        await  this.setTimeExceptions(q2)
        await  this.setGlobalExceptions(q2)

        //todo: this is only for test of setschedule and calling the method twice.. remove on production
        //this.setSchedule(12345,new moment(),new moment('2018-08-30T23:59:59'),new moment().add(5,'seconds'),127)

        var tmp=schedule.scheduledJobs;
        logger.PlayerEventsAnnounces('creating jobs completed')
    }

    async setGlobalExceptions(delays) {
        let now = new moment().utc(true);
        //clear old jobs first
        logger.PlayerEventsAnnounces('creating recurrence jobs for global ex');
        this.clearEx('globalEx');
        //get all global exceptions
        let globalEx = await this.db.getGlobalExceptions();
        if(!globalEx) return;
        // globalEx = await globalEx.where(c => now.isBefore(c.endDate));
        logger.PlayerEventsAnnounces(` we have ${globalEx.length} global exceptions creating jobs for them`);
        await globalEx.forEach(c => this.setGlobalEx(c.ID, c.startDate, c.endDate, c.startTime, c.daynumber, 'globalEx'));
        await globalEx.forEach(c => this.setGlobalEx(c.ID, c.startDate, c.endDate, c.endTime, c.daynumber, 'globalEx'));


        if(delays.length>0) {
            logger.PlayerEventsAnnounces(`now setting delays for ${delays.length} delays`)
            //now set crons for the delays
            for (let i = 0; i < delays.length; i++) {
                await globalEx.forEach(c => this.setGlobalEx(c.ID, c.startDate, c.endDate, c.startTime.add(delays[i], 'minutes'), c.daynumber, 'globalEx'));
                await globalEx.forEach(c => this.setGlobalEx(c.ID, c.startDate, c.endDate, c.endTime.add(delays[i], 'minutes'), c.daynumber, 'globalEx'));
            }
        }
        let tmp = schedule.scheduledJobs;
        logger.PlayerEventsAnnounces('creating globalEx jobs completed');

    }

    setGlobalEx(ID, startDate, endDate, startTime, daysEffected, exception) {
        logger.PlayerEventsAnnounces(`setting cron for announce global exception:${startDate.toJSON()} - ${endDate.toJSON()}, time:${startTime.toJSON().substr(11,8)}, days:${daysEffected}`)

        let arr = [];
        // for(let i = 0; i < 7; i++){ if((daysEffected & Math.pow(2, i)) == Math.pow(2, i)) arr.push(i); }
        arr.push(daysEffected);
        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(exception + ID, { start: startDate, end: endDate, rule: rule }, async () => {
            console.log(`created globalex: ${j.name}`);
        });
    }

    async setTimeExceptions(delays) {
        //get all time exceptions and create recurrence jobs for them.
        let now = new moment().utc(true);
        logger.PlayingSchedules('creating recurrence jobs for time exceptions');
        // first clear old exceptions
        this.clearEx('timeEx');
        // get all time exceptions
        let timeExceptions = await this.db.getTimeExceptions();
        if(!timeExceptions) return;
        timeExceptions = await timeExceptions.where(c => now.isBefore(c.endDate));
        logger.PlayerEventsAnnounces(`we have ${timeExceptions.length} time exceptions, creating jobs for them`);
        await timeExceptions.forEach(c => this.setTimeEx(c.ID, c.startDate, c.endDate, c.startTime, 'timeEx'));
        await timeExceptions.forEach(c => this.setTimeEx(c.ID, c.startDate, c.endDate, c.endTime, 'timeEx'));

        if(delays.length>0) {
            logger.PlayerEventsAnnounces(`now setting delays for ${delays.length} delays`)
            //now set crons for the delays
            for (let i = 0; i < delays.length; i++) {
                await timeExceptions.forEach(c => this.setTimeEx(c.ID, c.startDate, c.endDate, c.startTime.add(delays[i], 'minutes'), 'timeEx'));
                await timeExceptions.forEach(c => this.setTimeEx(c.ID, c.startDate, c.endDate, c.endTime.add(delays[i], 'minutes'), 'timeEx'));
            }
        }
        logger.PlayerEventsAnnounces('creating time exception jobs completed.')
    }

    setTimeEx(ID, startDate, endDate, startTime, exception) {
        logger.PlayerEventsAnnounces(`setting cron for announce TimeExceptions:${startDate.toJSON()} - ${endDate.toJSON()}, time:${startTime.toJSON().substr(11,8)}`)

        let start = startTime.toObject();
        let rule = new schedule.RecurrenceRule();
        rule.hour = start.hours;
        rule.minute = start.minutes;
        rule.second = start.seconds;
        let j = schedule.scheduleJob(exception + ID, { start: startDate, end: endDate, rule: rule }, async () => {
            console.log(`created time ex: ${j.name}`)
        });
    }

    clearEx(exception) {
        logger.PlayerEventsAnnounces('Clearing old time exception jobs');
        let cnt = 0;
        for(let key in schedule.scheduledJobs) {
            if(key.startsWith(exception)) {
                schedule.scheduledJobs[key].cancel();
                cnt++
            }
        }
        logger.PlayerEventsAnnounces(`Cleared ${cnt} old time exception jobs`);
    }

    setSchedule(announceID,startDate,endDate,startTime,daysEffected){

        logger.PlayerEventsAnnounces(`setting cron for announce:${startDate.toJSON()} - ${endDate.toJSON()}, time:${startTime.toJSON().substr(11,8)}, days:${daysEffected}`)
        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('announce' + announceID,{start:startDate,end:endDate, rule:rule}, async ()=>{
            logger.PlayerEventsAnnounces(`running schedule ${announceID}`)
            await this.checkNextPlay()
        });
    }

    clearSchedules(){
        logger.PlayerEventsAnnounces('Clearing old jobs');
        let cnt=0;
        for(let key in schedule.scheduledJobs){
            if(key.startsWith('announce')) {
                schedule.scheduledJobs[key].cancel()
                cnt++;
            }
        }
        logger.PlayerEventsAnnounces(`Cleared ${cnt} old jobs`);
    }

}

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