var low = require("lowdb");
var db = low("db.json");
var sid = require("short-id");
sid.configure({length:12});
db.defaults({}).write();

var http = require("http").createServer();

var ioClient = require("socket.io-client")("http://localhost:5000");
var io = require("socket.io")(http);

var activeFile = undefined;
var toTrigger = [];

class FrameTime{
   constructor(millis){
      this.lframe = 0;
      this.lseconds = 0;
      this.lminutes = 0;
      this.lhours = 0;
      this.lmilliseconds = 0;
      if(Number.isInteger(millis)){
         this.milliseconds = millis;
      } else if(typeof millis === "object"){
         if(Number.isInteger(millis.frame) && Number.isInteger(millis.seconds) && Number.isInteger(millis.minutes) && Number.isInteger(millis.hours) ){
            this.lframe = millis.frame; //use the local, because its mor performant
            this.lseconds = milllis.seconds;
            this.lminutes = millis.minutes;
            this.lhours = millis.hours;
            this.calcMillis();
         } else if(Number.isInteger(millis.milliseconds)){
            this.milliseconds = millis.milliseconds;
         }
      }
   }

   get milliseconds() {
      return this.lmilliseconds;
   }

   set milliseconds(val) {
      this.lmilliseconds = val;
      millisToFrame();
   }

   get frame(){
      return this.lframe;
   }

   set frame (val) {
      this.lframe = val;
      calcMillis();
   }

   get seconds() {
      return this.lseconds;
   }

   set seconds(val){
      this.lseconds = val;
      calcMillis();
   }

   get minutes() {
      return this.lminutes;
   }

   set minutes(val) {
      this.lminutes = val;
      calcMillis();
   }

   get hours(){
      return this.lhours;
   }

   set hours(val){
      this.lhours = val;
      calcMillis();
   }

   calcMillis(){
      this.lmilliseconds = (this.frame / 25 * 1000) + this.seconds * 1000 + this.minutes * 60 * 1000 + this.hours * 60 * 60 * 1000;
   }

   millisToFrame(millis) {
      this.frame   = Math.floor((millis % 1000)*25/1000);
      this.seconds = Math.floor((millis / 1000) % 60);
      this.minutes = Math.floor((millis / (1000*60)) % 60);
      this.hours   = Math.floor((millis / (1000*60*60)) % 24);
   }
}

ioClient.on("update", (data)=>{
   activeFile = data.file;
   var t = data.time;
   var ft = new FrameTime(t); 
   var answer = {};
   answer.time = ft;
   answer.timemillis = t;
   answer.file = activeFile;
   answer.playing = data.playing;
   io.emit("update", answer);

});

ioClient.on("playing", (data)=>{
   io.emit("playing", data);
});

ioClient.on("stopped", ()=>{
   io.emit("stopped")
});

io.on("connection", (socket)=>{
   //console.log("client connected:", socket.client.id);
   socket.on("set", (data)=>{ //ToDo preset ids etc.
      console.log(data);
      var entry = {};
      entry.id = sid.generate();
      entry.time = new FrameTime(data.time); //typof FrameTime
      entry.channel = data.channel;
      entry.note = data.note;
      entry.name = data.name;
      data.name = data.name.replace(".", "");
      entry.value = data.value;
      if(db.get(data.name).value() === undefined) {
         db.set(data.name, []).write();
      }
      db.get(data.name).push(entry).write();
   });

   socket.on("get", (data)=>{ //Requires filename
      var v = db.get(data.replace(".", "")).value();
      socket.emit("data", v);
   });

   socket.on("remove", (data)=>{ //requires id
      db.get(data.replace(".", "")).remove({id:data}).write();
   });

   socket.on("load_preset", (data)=>{
      toTrigger = db.get(data.replace(".", "")).value();
   });

   socket.on("play", (data)=>{
      ioClient.emit("play", data);
   });

   socket.on("stop", (data)=>{
      ioClient.emit("stop", data);
   });
});

setInterval(()=>{
   var start
   var update_infos = {};
   player.getOrigTime((time)=>{
      player.getTime((t)=>{
         player.getState((playing)=>{
            update_infos.file = activeFile;
            update_infos.time = t;
            update_infos.is_playing = playing;
            update_infos.triggeredMidi = [];
            if(playing){
               if(toTrigger){
                  toTrigger.forEach(e=>{
                     if(e.time <= t){
                        var midi = {channel:e.channel, note:e.note, value:e.value}
                        io.emit("midi", {channel:e.channel, note:e.note, value:e.value});
                        var i = toTrigger.indexOf(e);
                        if (i < 0) return;
                        toTrigger.splice(i, 1);
                        update_infos.triggeredMidi.push(midi);
                     } 
                  });
               }
            }
            io.emit("update", update_infos);
            //console.log(JSON.stringify(update_infos, undefined, 4));
         })
      });
   });
}, 1000/25)

http.listen(5000);