/*
To do:
  -flight log
  -statistics page
  -navigation
*/

getAltitude = (p,baseP) => (44330 * (1.0 - Math.pow(p/baseP, 0.1903)));
getFL = () => (44330 * (1.0 - Math.pow(pressure/1013.25, 0.1903))).toFixed(0);
getTimeString = () => (settings.localTime) ? (require("locale").time(Date(),1)):(Date().toUTCString().slice(Date().toUTCString().length-12,Date().toUTCString().length-7));

var fg=g.getColor();
var bg=g.getBgColor();
var red="#F00",green="#0F0";

const unitsRoc=[
  {name:"m/s", factor:1, precision:1, layoutCode:{type:"v", halign:1, c: [
            {type:"txt", font:"12%", halign:0, filly:0, label:"m"},
            {type:"", height:1,width:"20", bgCol:fg},
            {type:"txt", font:"12%", halign:0, filly:0, label:"s"}]}},
  {name:"ft/m", factor:196.85039370078738, precision:0, layoutCode:{type:"v", halign:1, c: [
            {type:"txt", font:"12%", halign:0, filly:0, label:"ft"},
            {type:"", height:1,width:"30", bgCol:fg},
            {type:"txt", font:"12%", halign:0, filly:0, label:"min"}]}},
  {name:"kt", factor:1.9438444924406, precision:1, layoutCode:
            {type:"txt", font:"12%", halign:0, filly:0, label:"kt"}}
  ];
const unitsGs=[
  {name:"km/h", factor:1, precision:1, layoutCode:{type:"v", halign:1, c: [
            {type:"txt", font:"12%", halign:0, filly:0, label:"km"},
            {type:"", height:1,width:"30", bgCol:fg},
            {type:"txt", font:"12%", halign:0, filly:0, label:"h"}]}},
  {name:"kt", factor:0.5399568, precision:0, layoutCode:{type:"txt", font:"12%", halign:0, filly:0, label:"kt"}},
  {name:"m/s", factor:0.2777777777777778, precision:1, layoutCode:{type:"v", halign:1, c: [
            {type:"txt", font:"12%", halign:0, filly:0, label:"m"},
            {type:"", height:1,width:"20", bgCol:fg},
            {type:"txt", font:"12%", halign:0, filly:0, label:"s"}]}}
  ];
const unitsAlt=[
  {name:"m", factor:1, precision:0, layoutCode:{type:"txt", font:"12%", halign:0, filly:0, label:"m"}},
  {name:"ft", factor:3.280839895013123, precision:0, layoutCode:{type:"txt", font:"12%", halign:0, filly:0, label:"ft"}}
  ];
const unitROC={type:"v", halign:1, c: [
            {type:"txt", font:"12%", halign:0, filly:0, label:"m"},
            {type:"", height:1,width:"20", bgCol:fg},
            {type:"txt", font:"12%", halign:0, filly:0, label:"s"}
          ]};

const ground=0, flying=1, landed=2, maybeFlying=3, maybeLanded=4;

var settings = Object.assign({
  rocU: 0,
  altU: 0,
  gsU:0,
  intTime:10,
  localTime:true,
  autoDetect:true,
  bargraph:false
}, require('Storage').readJSON("tinyVario.json", true) || {});

var qnh=Math.floor(Bangle.getOptions().seaLevelPressure) || 1013;
var pfdHandle;
var rawP=0, samples=0;
var altH = [];
var altRaw=-9999, altFast=0, altSlow=0;
var fastGain=0.5, slowGain=0.3;
var roc=0,rocAvg=0, gs;
var lastPressure = Date.now();
var pressure = 1000;
var state=ground;
var takeoffTime=0, landingTime=0, flyingTime;
var Layout = require("Layout");
var oldSettings;

//var delta=0;//TESTING


function updateText(t) {
  g.reset();
  g.clearRect(t.x,t.y,t.x+t.w-1,t.y+t.h-1);
  if (t.col) g.setColor(t.col);
  else g.setColor(fg);
  if (t.halign==1)
    g.setFont(t.font).setFontAlign(1,0,0).drawString(t.label, t.x+t.w, t.y+(t.h>>1));
  else if (t.halign==-1)
    g.setFont(t.font).setFontAlign(-1,0,0).drawString(t.label, t.x, t.y+(t.h>>1));
  else
    g.setFont(t.font).setFontAlign(0,0,0).drawString(t.label, t.x+(t.w>>1), t.y+(t.h>>1));
}

function initPFD() {
  Bangle.setUI();
  var pfd = new Layout(
    {type:"v",c: [
      /*{type:"h",c: [
        {type:"", fillx:1, height:"1"}
        ]},*/
      {type:"h",filly:1, c: [
        {type:"custom", width:"25", render:()=>{
          var p = pfd.vario;
          if (roc>0.1) g.setColor(0,1,0);
          if (roc<-1) g.setColor(1,0,0);
          var y=p.y+p.h/2-roc*(p.h/2)/5;
          if (settings.bargraph==false) {
            g.clearRect(p.x,p.y,p.x+p.w-1,p.y+p.h-1);
            g.fillRect(p.x,p.y+(p.h/2),p.x+p.w-1,Math.clip(y,p.y,p.y+p.h-1));
          } else {
            g.setClipRect(p.x,p.y,p.x+p.w-1,p.y+p.h-1);
            g.scroll(-1,0);
            g.drawLine(p.x+p.w-1,p.y+(p.h/2),p.x+p.w-1,Math.clip(y,p.y,p.y+p.h-1));
          }
          g.reset();
        }, id:"vario",filly:1, cb:()=>initVarioMenu()},
        {type:"", filly:1, width:1, bgCol:fg},
        {type:"v",fillx:1, c: [
          {type:"h", halign:1, c:[
            {type:"txt", font:"22%", halign:1, filly:1, fillx:1, label:"9999", id:"alt", cb:()=>initAltMenu()},
            unitsAlt[settings.altU].layoutCode
          ]},
          {type:"", fillx:1, height:"1", bgCol:fg},
          {type:"h", halign:1, c:[
            {type:"txt", font:"25%", halign:1, filly:1, fillx:1, label:"-9.9", id:"avg", cb:()=>initAvgMenu()},
            unitsRoc[settings.rocU].layoutCode
          ]},
          {type:"", fillx:1, height:"1", bgCol:fg},
          {type:"h", halign:1, c:[
            {type:"txt", font:"25%", halign:1, filly:1, fillx:1, label:"XXX", id:"gs", cb:()=>initGsMenu()},
            unitsGs[settings.gsU].layoutCode
          ]}
        ]}
      ]},
      {type:"", fillx:1, height:"1", bgCol:fg},
      {type:"h",c: [
        {type:"txt",pad:0, halign:0, font:"15%",fillx:1, label:"99:99", id:"time", cb:()=>initTimeMenu()},
        {type:"", width:1,height:g.getHeight()*0.15, bgCol:fg},
        {type:"txt",pad:0, halign:0, font:"15%", fillx:1, label:"--:--", id:"flyingtime", cb:()=>initFlyingTimeMenu() }
      ]}
    ]},{lazy:true}
  );
  g.clear();
  pfd.render();
  //-------testing------
  //rawP=1000;
  //samples=1;
  //--------------------
  pfdHandle = setInterval(function() {
    t1=Date().getTime();
    //process pressure readings
    if (samples) {
      pressure=rawP/samples;
      samples=0;
      rawP=0;
      if (altRaw==-9999) {//first measurement)
        altRaw=getAltitude(pressure,qnh);
        altFast=altRaw;
        altSlow=altRaw;
        for (let i = 0; i < settings.intTime*4+1; i++) altH.push(altRaw);
      }
    }
    //altRaw=altRaw+delta;getAltitude(pressure,qnh);//TESTING
    altRaw=getAltitude(pressure,qnh);
    altFast=altFast+(altRaw-altFast)*fastGain;
    altSlow=altSlow+(altRaw-altSlow)*slowGain;
    altH.push(altFast);
    while (altH.length>settings.intTime*4) {
      rocAvg=(altH[altH.length-1]-altH[0])/settings.intTime;
      altH.shift();
    }
    roc=(altFast-altSlow)/((0.25/slowGain)-(0.25/fastGain));

    if (settings.autoDetect==true) switch (state) {
      case ground:
        if ((gs>=5) || (roc>=1) || (roc<=-1)) {
          state=maybeFlying;
          takeoffTime=Date().getTime();
        }
        break;
      case maybeFlying:
        if (!(gs>=5) && (roc<1) && (roc>-1)) state=ground;
        else if (Date().getTime()-takeoffTime>60000) state=flying;
        break;
      case flying:
        if (!(gs>=5) && (roc<1) && (roc>-1)) {
          state=maybeLanded;
          landingTime=Date().getTime();
        }
        break;
      case maybeLanded:
        if ((gs>=5) || (roc>=1) || (roc<=-1)) state=flying;
        else if (Date().getTime()-landingTime>60000) state=landed;
        break;
    }
    if ((state==flying) || (state==maybeLanded)) {
      flyingTime=Date().getTime()-takeoffTime;
      pfd.flyingtime.label=(flyingTime / 3600000).toFixed(0)+":"+(flyingTime / 60000 % 60).toFixed(0).padStart(2,'0');
      pfd.flyingtime.col=fg;
    } else if (state==landed) {
      flyingTime=landingTime-takeoffTime;
      pfd.flyingtime.label=(flyingTime / 3600000).toFixed(0)+":"+(flyingTime / 60000 % 60).toFixed(0).padStart(2,'0');
      pfd.flyingtime.col=green;
    }

    pfd.alt.label=(altRaw*unitsAlt[settings.altU].factor).toFixed(unitsAlt[settings.altU].precision);
    pfd.avg.col=(rocAvg<-1) ? (red):((rocAvg>0.1) ? (green):(fg));
    pfd.avg.label=(rocAvg*unitsRoc[settings.rocU].factor).toFixed(unitsRoc[settings.rocU].precision);

    var gps = Bangle.getGPSFix();
    if (gps!=undefined) {
      pfd.gs.label=(gps.speed*unitsGs[settings.gsU].factor).toFixed(unitsGs[settings.gsU].precision);
      updateText(pfd.gs);
      gs=gps.speed;
    } //else gs=0;
    
    pfd.time.label=getTimeString();
    updateText(pfd.alt);
    updateText(pfd.avg);
    updateText(pfd.time);
    updateText(pfd.flyingtime);

    pfd.vario.render();
    //print(Date().getTime()-t1);
  }, 250);

}

function initAltMenu() {
  var oldQnh=qnh;
  function updateAltMenu() {
    altMenu.clear();
    altMenu.alt.label=
      (getAltitude(pressure,qnh)*unitsAlt[settings.altU].factor).toFixed(unitsAlt[settings.altU].precision)
      +unitsAlt[settings.altU].name;
    altMenu.qnh.label=qnh;
    altMenu.render();
  }
  oldSettings=Object.assign({},settings);
  clearInterval(pfdHandle);
  var altMenu = new Layout ({
    type:"v", c: [{
      type:"v", width:180, c: [
        {type:"h", c: [
          {type:"btn", font:"15%", pad:1, fillx:1, filly:1, label:"-", cb:l=>{qnh--; updateAltMenu();}},
          {type:"btn", font:"15%", pad:1, fillx:1, filly:1, label:"+", cb:l=>{qnh++; updateAltMenu();}}
        ]},
        {type:"v", c: [
          {type:"h", c: [
            {type:"txt", font:"13%", fillx:1, filly:1, label:"QNH: "},
            {type:"txt", font:"13%", fillx:1, filly:1, id:"qnh", label:"    "},
          ]},
          {type:"h", c: [
            {type:"txt", font:"13%", fillx:1, filly:1, label:"Alt: "},
            {type:"txt", font:"13%", fillx:1, filly:1, id:"alt", label: "      "},
          ]}
        ]},
        {type:"btn", font:"12%", id:"units", pad:2, fillx:1, filly:1, label:"Units: "+unitsAlt[settings.altU].name, cb:()=>{
          settings.altU=(settings.altU+1)%unitsAlt.length;
          altMenu.units.label="Units: "+unitsAlt[settings.altU].name;
          altMenu.render();
        }},
      ]},
      {type:"h", c: [
        {type:"btn", font:"16%", pad:1, fillx:1, label:"BACK", cb: ()=>{
          settings=Object.assign({},oldSettings);
          print("old settings restored");
          initPFD();
        }},
        {type:"btn", font:"16%", pad:1, fillx:1, label:"SAVE", cb: ()=>{
          require('Storage').writeJSON("tinyVario.json", settings);
          Bangle.setOptions({seaLevelPressure:qnh});
          initPFD();
        }}
      ]}
    ], lazy:true});
  g.clear();
  altMenu.render();
  updateAltMenu();
}

function initAvgMenu() {
  oldSettings=Object.assign({},settings);
  clearInterval(pfdHandle);
  var avgMenu = new Layout ({
    type:"v", c: [{
      type:"v", width:180, c: [
        {type:"h", c: [
          {type:"btn", font:"12%", pad:2, fillx:1, filly:1, label:"-", cb:l=>{
            settings.intTime=Math.clip(settings.intTime-1,1,60);
            avgMenu.clear();
            avgMenu.interval.label="Interval: "+settings.intTime+"s";
            avgMenu.render();
          }},
          {type:"btn", font:"12%", pad:1, fillx:1, filly:1, label:"+", cb:l=>{
            settings.intTime=Math.clip(settings.intTime+1,1,60);
            avgMenu.clear();
            avgMenu.interval.label="Interval: "+settings.intTime+"s";
            avgMenu.render();
          }}
        ]},
        {type:"txt", id:"interval", font:"10%", pad:1, fillx:1, filly:1, label:"Interval: "+settings.intTime+"s"},
        {type:"btn", font:"12%", id:"units", pad:1, fillx:1, filly:1, label:"Units: "+unitsRoc[settings.rocU].name, cb:()=>{
          settings.rocU=(settings.rocU+1)%unitsRoc.length;
          avgMenu.units.label="Units: "+unitsRoc[settings.rocU].name;
          avgMenu.render();
        }},
      ]},
      {type:"h", c: [
        {type:"btn", font:"16%", pad:1, fillx:1, label:"BACK", cb: ()=>{
          settings=Object.assign({},oldSettings);
          initPFD();
        }},
        {type:"btn", font:"16%", pad:1, fillx:1, label:"SAVE", cb: ()=>{
          require('Storage').writeJSON("tinyVario.json", settings);
          initPFD();
        }}
      ]}
    ], lazy:true});
  g.clear();
  avgMenu.render();
}

function initGsMenu() {
  oldSettings=Object.assign({},settings);
  clearInterval(pfdHandle);
  var gsMenu = new Layout ({
    type:"v", c: [
      {type:"btn", font:"20%", id:"units", pad:1, fillx:1, filly:1, label:"Units:\n"+unitsGs[settings.gsU].name, cb:()=>{
        settings.gsU=(settings.gsU+1)%unitsGs.length;
        gsMenu.units.label="Units:\n"+unitsGs[settings.gsU].name;
        gsMenu.render();
      }},
      {type:"h", c: [
        {type:"btn", font:"16%", pad:1, fillx:1, label:"BACK", cb: ()=>{
          settings=Object.assign({},oldSettings);
          print("old settings restored");
          initPFD();
        }},
        {type:"btn", font:"16%", pad:1, fillx:1, label:"SAVE", cb: ()=>{
          require('Storage').writeJSON("tinyVario.json", settings);
          initPFD();
        }}
      ]}
    ], lazy:true});
  g.clear();
  gsMenu.render();
}

function initTimeMenu() {
  oldSettings=Object.assign({},settings);
  clearInterval(pfdHandle);
  var timeMenu = new Layout ({
    type:"v", c: [
      {type:"btn", font:"20%", id:"format", pad:1, fillx:1, filly:1, label:"Time:\n"+((settings.localTime==true) ? ("LCL") : ("UTC")), cb:()=>{
        settings.localTime=!settings.localTime;
        timeMenu.format.label="Time:\n"+((settings.localTime==true) ? ("LCL") : ("UTC"));
        timeMenu.render();
      }},
      {type:"h", c: [
        {type:"btn", font:"16%", pad:1, fillx:1, label:"BACK", cb: ()=>{
          settings=Object.assign({},oldSettings);
          initPFD();
        }},
        {type:"btn", font:"16%", pad:1, fillx:1, label:"SAVE", cb: ()=>{
          require('Storage').writeJSON("tinyVario.json", settings);
          initPFD();
        }}
      ]}
    ], lazy:true});
  g.clear();
  timeMenu.render();
}

function initVarioMenu() {
  oldSettings=Object.assign({},settings);
  clearInterval(pfdHandle);
  var varioMenu = new Layout ({
    type:"v", c: [
      {type:"btn", font:"20%", id:"format", pad:1, fillx:1, filly:1, label:"Display:\n"+((settings.bargraph==true) ? ("graph") : ("simple")), cb:()=>{
        settings.bargraph=!settings.bargraph;
        varioMenu.format.label="Display:\n"+((settings.bargraph==true) ? ("graph") : ("simple"));
        varioMenu.render();
      }},
      {type:"h", c: [
        {type:"btn", font:"16%", pad:1, fillx:1, label:"BACK", cb: ()=>{
          settings=Object.assign({},oldSettings);
          initPFD();
        }},
        {type:"btn", font:"16%", pad:1, fillx:1, label:"SAVE", cb: ()=>{
          require('Storage').writeJSON("tinyVario.json", settings);
          initPFD();
        }}
      ]}
    ], lazy:true});
  g.clear();
  varioMenu.render();
}
function initFlyingTimeMenu() {
  oldSettings=Object.assign({},settings);
  clearInterval(pfdHandle);
  var ftMenu = new Layout (
    {type:"v", c: [
      {type:"v", c: [
        {type:"h", c: [
          {type:"btn", font:"12%", pad:1, fillx:1, filly:1, label:"Toggle\nAuto", cb:()=>{
            settings.autoDetect=!settings.autoDetect;
            ftMenu.manual.label= (settings.autoDetect==true)? 
              ("AUTO"):((state==flying) ? ("LAND") : ("TAKE\nOFF"));
            ftMenu.render();
          }},
          {type:"btn", font:"12%", id:"manual", pad:1, fillx:1, filly:1, label:(settings.autoDetect==true)? 
            ("AUTO"):((state==flying) ? ("LAND") : ("TAKE\nOFF")), cb:()=>{
              if (settings.autoDetect==false) {
                if (state!=flying) {
                  E.showPrompt("Take off now?").then((v)=> {
                    if (v) {
                      state=flying;
                      takeoffTime=Date().getTime();
                      initPFD();
                    }
                  });
                } else {
                  E.showPrompt("Land now?").then((v)=> {
                    if (v) {
                      state=landed;
                      landingTime=Date().getTime();
                      initPFD();
                    }
                  });
                }
              }
            }
          }
        ]},
        {type:"btn", font:"12%", pad:1, fillx:1, filly:1, label:"Reset", cb:()=>{
          E.showPrompt("Reset Flight?").then((v)=> {
            state=ground;
            initPFD();
          });
        }}
      ]},
      {type:"h", c: [
        {type:"btn", font:"16%", pad:1, fillx:1, label:"BACK", cb: ()=>{
          settings=Object.assign({},oldSettings);
          initPFD();
        }},
        {type:"btn", font:"16%", pad:1, fillx:1, label:"SAVE", cb: ()=>{
          require('Storage').writeJSON("tinyVario.json", settings);
          initPFD();
        }}
      ]}
    ], lazy:true});
  g.clear();
  ftMenu.render();
}

Bangle.setGPSPower(true, "tinyVario");
Bangle.setBarometerPower(true, "tinyVario");

Bangle.on('pressure', function(e) {
  if (samples<10) { //no need to gather more samples when stuck in a menu
    rawP+=e.pressure;
    samples++;
  }
});

initPFD();
