Bangle.loadWidgets();
Bangle.drawWidgets();

var settings;

var osm;
try { // if it's installed, use the OpenStreetMap module
  osm = require("openstmap");
} catch (e) {}

function loadSettings() {
  settings = require("Storage").readJSON("recorder.json",1)||{};
  var changed = false;
  if (!settings.file) {
    changed = true;
    settings.file = "recorder.log0.csv";
  }
  if (!Array.isArray(settings.record)) {
    settings.record = ["gps"];
    changed = true;
  }
  if (changed)
    require("Storage").writeJSON("recorder.json", settings);
}
loadSettings();

function updateSettings() {
  require("Storage").writeJSON("recorder.json", settings);
  if (WIDGETS["recorder"])
    WIDGETS["recorder"].reload();
}

function getTrackNumber(filename) {
  var trackNum = 0;
  var matches = filename.match(/^recorder\.log(.*)\.csv$/);
  if (matches) {
    trackNum = parseInt(matches[1]||0);
  }
  return trackNum;
}

function showMainMenu() {
  function menuRecord(id) {
    return {
      value: settings.record.includes(id),
      onchange: v => {
        settings.recording = false; // stop recording if we change anything
        settings.record = settings.record.filter(r=>r!=id);
        if (v) settings.record.push(id);
        updateSettings();
      }
    };
  }
  const mainmenu = {
    '': { 'title': /*LANG*/'Recorder' },
    '< Back': ()=>{load();},
    /*LANG*/'RECORD': {
      value: !!settings.recording,
      onchange: v => {
        setTimeout(function() {
          E.showMenu();
          WIDGETS["recorder"].setRecording(v).then(function() {
            print(/*LANG*/"Complete");
            loadSettings();
            print(settings.recording);
            showMainMenu();
          });
        }, 1);
      }
    },
    /*LANG*/'File #': {
      value: getTrackNumber(settings.file),
      min: 0,
      max: 99,
      step: 1,
      onchange: v => {
        settings.recording = false; // stop recording if we change anything
        settings.file = "recorder.log"+v+".csv";
        updateSettings();
      }
    },
    /*LANG*/'View Tracks': ()=>{viewTracks();},
    /*LANG*/'Time Period': {
      value: settings.period||10,
      min: 1,
      max: 120,
      step: 1,
      format: v=>v+"s",
      onchange: v => {
        settings.recording = false; // stop recording if we change anything
        settings.period = v;
        updateSettings();
      }
    }
  };
  var recorders = WIDGETS["recorder"].getRecorders();
  Object.keys(recorders).forEach(id=>{
    mainmenu[/*LANG*/"Log "+recorders[id]().name] = menuRecord(id);
  });
  delete recorders;
  return E.showMenu(mainmenu);
}



function viewTracks() {
  const menu = {
    '': { 'title': /*LANG*/'Tracks' }
  };
  var found = false;
  require("Storage").list(/^recorder\.log.*\.csv$/,{sf:true}).forEach(filename=>{
    found = true;
    menu[/*LANG*/"Track "+getTrackNumber(filename)] = ()=>viewTrack(filename,false);
  });
  if (!found)
    menu[/*LANG*/"No Tracks found"] = function(){};
  menu['< Back'] = () => { showMainMenu(); };
  return E.showMenu(menu);
}

function getTrackInfo(filename) {
  "ram"
  var minLat = 90;
  var maxLat = -90;
  var minLong = 180;
  var maxLong = -180;
  var starttime, duration=0;
  var f = require("Storage").open(filename,"r");
  if (f===undefined) return;
  var l = f.readLine(f);
  var fields, timeIdx, latIdx, lonIdx;
  var nl = 0, c, n;
  if (l!==undefined) {
    fields = l.trim().split(",");
    timeIdx = fields.indexOf("Time");
    latIdx = fields.indexOf("Latitude");
    lonIdx = fields.indexOf("Longitude");
    l = f.readLine(f);
  }
  if (l!==undefined) {
    c = l.split(",");
    starttime = parseInt(c[timeIdx]);
  }
  // pushed this loop together to try and bump loading speed a little
  while(l!==undefined) {
    ++nl;c=l.split(",");l = f.readLine(f);
    if (c[latIdx]=="")continue;
    n = +c[latIdx];if(n>maxLat)maxLat=n;if(n<minLat)minLat=n;
    n = +c[lonIdx];if(n>maxLong)maxLong=n;if(n<minLong)minLong=n;
  }
  if (c) duration = parseInt(c[timeIdx]) - starttime;
  var lfactor = Math.cos(minLat*Math.PI/180);
  var ylen = (maxLat-minLat);
  var xlen = (maxLong-minLong)* lfactor;
  var screenSize = g.getHeight()-48; // 24 for widgets, plus a border
  var scale = xlen>ylen ? screenSize/xlen : screenSize/ylen;
  return {
    fn : getTrackNumber(filename),
    fields : fields,
    filename : filename,
    time : new Date(starttime*1000),
    records : nl,
    minLat : minLat, maxLat : maxLat,
    minLong : minLong, maxLong : maxLong,
    lat : (minLat+maxLat)/2, lon : (minLong+maxLong)/2,
    lfactor : lfactor,
    scale : scale,
    duration : Math.round(duration)
  };
}

function asTime(v){
  var mins = Math.floor(v/60);
  var secs = v-mins*60;
  return ""+mins.toString()+"m "+secs.toString()+"s";
}

function viewTrack(filename, info) {
  if (!info) {
    E.showMessage(/*LANG*/"Loading...",/*LANG*/"Track "+getTrackNumber(filename));
    info = getTrackInfo(filename);
  }
  //console.log(info);
  const menu = {
    '': { 'title': /*LANG*/'Track '+info.fn }
  };
  if (info.time)
    menu[info.time.toISOString().substr(0,16).replace("T"," ")] = function(){};
  menu["Duration"] = { value : asTime(info.duration)};
  menu["Records"] = { value : ""+info.records };
  if (info.fields.includes("Latitude"))
    menu[/*LANG*/'Plot Map'] = function() {
      info.qOSTM = false;
      plotTrack(info);
    };
  if (osm && info.fields.includes("Latitude"))
    menu[/*LANG*/'Plot OpenStMap'] = function() {
      info.qOSTM = true;
      plotTrack(info);
    }
  if (info.fields.includes("Altitude"))
    menu[/*LANG*/'Plot Alt.'] = function() {
      plotGraph(info, "Altitude");
    };
  if (info.fields.includes("Latitude"))
    menu[/*LANG*/'Plot Speed'] = function() {
      plotGraph(info, "Speed");
    };
  // TODO: steps, heart rate?
  menu[/*LANG*/'Erase'] = function() {
    E.showPrompt(/*LANG*/"Delete Track?").then(function(v) {
      if (v) {
        settings.recording = false;
        updateSettings();
        var f = require("Storage").open(filename,"r");
        f.erase();
        viewTracks();
      } else
        viewTrack(filename, info);
    });
  };
  menu['< Back'] = () => { viewTracks(); };

  function plotTrack(info) { "ram"
    function distance(lat1,long1,lat2,long2) { "ram"
      var x = (long1-long2) * Math.cos((lat1+lat2)*Math.PI/360);
      var y = lat2 - lat1;
      return Math.sqrt(x*x + y*y) * 6371000 * Math.PI / 180;
    }

    // Function to convert lat/lon to XY
    var getMapXY;
    if (info.qOSTM) {
      getMapXY = osm.latLonToXY.bind(osm);
    } else {
      getMapXY = function(lat, lon) { "ram"
        return {x:cx + Math.round((long - info.lon)*info.lfactor*info.scale),
                y:cy + Math.round((info.lat - lat)*info.scale)};
      };
    }

    E.showMenu(); // remove menu
    E.showMessage(/*LANG*/"Drawing...",/*LANG*/"Track "+info.fn);
    g.flip(); // on buffered screens, draw a not saying we're busy
    g.clear(1);
    var s = require("Storage");
    var W = g.getWidth();
    var H = g.getHeight();
    var cx = W/2;
    var cy = 24 + (H-24)/2;
    if (!info.qOSTM) {
      g.setColor("#f00").fillRect(9,80,11,120).fillPoly([9,60,19,80,0,80]);
      g.setColor(g.theme.fg).setFont("6x8").setFontAlign(0,0).drawString("N",10,50);
    } else {
      osm.lat = info.lat;
      osm.lon = info.lon;
      osm.draw();
      g.setColor("#000");
    }
    var latIdx = info.fields.indexOf("Latitude");
    var lonIdx = info.fields.indexOf("Longitude");
    g.drawString(asTime(info.duration),10,220);
    var f = require("Storage").open(info.filename,"r");
    if (f===undefined) return;
    var l = f.readLine(f);
    l = f.readLine(f); // skip headers
    var ox=0;
    var oy=0;
    var olat,olong,dist=0;
    var i=0, c = l.split(",");
    // skip until we find our first data
    while(l!==undefined && c[latIdx]=="") {
      c = l.split(",");
      l = f.readLine(f);
    }
    // now start plotting
    var lat = +c[latIdx];
    var long = +c[lonIdx];
    var mp = getMapXY(lat, long);
    g.moveTo(mp.x,mp.y);
    g.setColor("#0f0");
    g.fillCircle(mp.x,mp.y,5);
    if (info.qOSTM) g.setColor("#f09");
    else g.setColor(g.theme.fg);
    l = f.readLine(f);
    g.flip(); // force update
    while(l!==undefined) {
      c = l.split(",");l = f.readLine(f);
      if (c[latIdx]=="")continue;
      lat = +c[latIdx];
      long = +c[lonIdx];
      mp = getMapXY(lat, long);
      g.lineTo(mp.x,mp.y);
      if (info.qOSTM) g.fillCircle(mp.x,mp.y,2); // make the track more visible
      var d = distance(olat,olong,lat,long);
      if (!isNaN(d)) dist+=d;
      olat = lat;
      olong = long;
      ox = mp.x;
      oy = mp.y;
      if (++i > 100) { g.flip();i=0; }
    }
    g.setColor("#f00");
    g.fillCircle(ox,oy,5);
    if (info.qOSTM) g.setColor("#000");
    else g.setColor(g.theme.fg);
    g.drawString(require("locale").distance(dist,2),g.getWidth() / 2, g.getHeight() - 20);
    g.setFont("6x8",2);
    g.setFontAlign(0,0,3);
    var isBTN3 = "BTN3" in global;
    g.drawString(/*LANG*/"Back",g.getWidth() - 10, isBTN3 ? (g.getHeight() - 40) : (g.getHeight()/2));
    setWatch(function() {
      viewTrack(info.fn, info);
    }, isBTN3?BTN3:BTN1);
    Bangle.drawWidgets();
    g.flip();
  }

  function plotGraph(info, style) { "ram"
    E.showMenu(); // remove menu
    E.showMessage(/*LANG*/"Calculating...",/*LANG*/"Track "+info.fn);
    var filename = info.filename;
    var infn = new Float32Array(80);
    var infc = new Uint16Array(80);
    var title;
    var lt = 0; // last time
    var tn = 0; // count for each time period
    var strt, dur = info.duration;
    var f = require("Storage").open(filename,"r");
    if (f===undefined) return;
    var l = f.readLine(f);
    l = f.readLine(f); // skip headers
    var nl = 0, c, i;
    var timeIdx = info.fields.indexOf("Time");
    if (l!==undefined) {
      c = l.split(",");
      strt = c[timeIdx];
    }
    if (style=="Altitude") {
      title = /*LANG*/"Altitude (m)";
      var altIdx = info.fields.indexOf("Altitude");
      while(l!==undefined) {
        ++nl;c=l.split(",");l = f.readLine(f);
        if (c[altIdx]=="") continue;
        i = Math.round(80*(c[timeIdx] - strt)/dur);
        infn[i]+=+c[altIdx];
        infc[i]++;
      }
    } else if (style=="Speed") {
      // use locate to work out units
      var localeStr = require("locale").speed(1,5); // get what 1kph equates to
      let units = localeStr.replace(/[0-9.]*/,"");
      var factor = parseFloat(localeStr)*3.6; // m/sec to whatever out units are
      // title
      title = /*LANG*/"Speed"+` (${units})`;
      var latIdx = info.fields.indexOf("Latitude");
      var lonIdx = info.fields.indexOf("Longitude");
      // skip until we find our first data
      while(l!==undefined && c[latIdx]=="") {
        c = l.split(",");
        l = f.readLine(f);
      }
      // now iterate
      var p,lp = Bangle.project({lat:c[1],lon:c[2]});
      var t,dx,dy,d,lt = c[timeIdx];
      while(l!==undefined) {
        ++nl;c=l.split(",");
        l = f.readLine(f);
        if (c[latIdx] == "") {
          continue;
        }
        t = c[timeIdx];
        i = Math.round(80*(t - strt)/dur);
        p = Bangle.project({lat:c[latIdx],lon:c[lonIdx]});
        dx = p.x-lp.x;
        dy = p.y-lp.y;
        d = Math.sqrt(dx*dx+dy*dy);
        if (t!=lt) {
          infn[i]+=d / (t-lt); // speed
          infc[i]++;
        }
        lp = p;
        lt = t;
      }
    } else throw new Error("Unknown type "+style);
    var min=100000,max=-100000;
    for (var i=0;i<infn.length;i++) {
      if (infc[i]>0) infn[i]=factor*infn[i]/infc[i];
      var n = infn[i];
      if (n>max) max=n;
      if (n<min) min=n;
    }
    // work out a nice grid value
    var heightDiff = max-min;
    var grid = 1;
    while (heightDiff/grid > 8) {
      grid*=2;
    }
    // draw
    g.clear(1).setFont("6x8",1);
    var r = require("graph").drawLine(g, infn, {
      x:4,y:24,
      width: g.getWidth()-24,
      height: g.getHeight()-(24+8),
      axes : true,
      gridy : grid,
      gridx : infn.length / 3,
      title: title,
      miny: min,
      maxy: max,
      xlabel : x=>Math.round(x*dur/(60*infn.length))+/*LANG*/" min" // minutes
    });
    g.setFont("6x8",2);
    g.setFontAlign(0,0,3);
    var isBTN3 = "BTN3" in global;
    g.drawString(/*LANG*/"Back",g.getWidth() - 10, isBTN3 ? (g.getHeight() - 40) : (g.getHeight()/2));
    setWatch(function() {
      viewTrack(info.filename, info);
    }, isBTN3?BTN3:BTN1);
    g.flip();
  }

  return E.showMenu(menu);
}


showMainMenu();
