const scale = g.getWidth()/240;
var pal_by = new Uint16Array([g.getBgColor(),0xFFC0],0,1); // black, yellow
var pal_bw = new Uint16Array([g.getBgColor(),g.getColor()],0,1);  // black, white
var pal_bb = new Uint16Array([g.getBgColor(),0x07ff],0,1); // black, blue

// having 3 2 color pallette keeps the memory requirement lower
var buf1 = Graphics.createArrayBuffer(160*scale,160*scale,1, {msb:true});
var buf2 = Graphics.createArrayBuffer(g.getWidth()/3,40*scale,1, {msb:true});
var arrow_img = require("heatshrink").decompress(atob("lEowIPMjAEDngEDvwED/4DCgP/wAEBgf/4AEBg//8AEBh//+AEBj///AEBn///gEBv///wmCAAImCAAIoBFggE/AkaaEABo="));

var settings = Object.assign({
  // default values
  smoothDirection: true,
}, require('Storage').readJSON("waypointer.json", true) || {});

function flip1(x,y) {
  g.drawImage({width:160*scale,height:160*scale,bpp:1,buffer:buf1.buffer, palette:pal_by},x,y);
  buf1.clear();
}

function flip2_bw(x,y) {
  g.drawImage({width:g.getWidth()/3,height:40*scale,bpp:1,buffer:buf2.buffer, palette:pal_bw},x,y);
  buf2.clear();
}

function flip2_bb(x,y) {
  g.drawImage({width:g.getWidth()/3,height:40*scale,bpp:1,buffer:buf2.buffer, palette:pal_bb},x,y);
  buf2.clear();
}

var candraw = true;
var wp_bearing = 0;
var direction = 0;
var wpindex=0;
var loc = require("locale");
var selected = false;

var previous = {
  bs: '',
  dst: '',
  wp_name: '',
  course: 0,
  selected: false,
};

// clear the attributes that control the display refresh
function clear_previous() {
  previous.bs = '-';
  previous.dst = '-';
  previous.wp_name = '-';
  previous.course = -999;
}

function drawCompass(course) {
  if(!candraw) return;
  if (Math.abs(previous.course - course) < 9) return; // reduce number of draws due to compass jitter
  previous.course = course;

  buf1.setColor(1);
  buf1.fillCircle(buf1.getWidth()/2,buf1.getHeight()/2,79*scale);
  buf1.setColor(0);
  buf1.fillCircle(buf1.getWidth()/2,buf1.getHeight()/2,69*scale);
  buf1.setColor(1);
  buf1.drawImage(arrow_img, buf1.getWidth()/2, buf1.getHeight()/2, {scale:3*scale,  rotate:radians(course)} );
  flip1(40*scale, Bangle.appRect.y+6*scale);
}

/***** COMPASS CODE ***********/

var heading = 0;
function newHeading(m,h){
    var s = Math.abs(m - h);
    var delta = (m>h)?1:-1;
    if (s>=180){s=360-s; delta = -delta;}
    if (s<2) return h;
    var hd;
    if (settings.smoothDirection) {
        hd = h + delta*(1 + Math.round(s/5));
    } else {
        hd = h + delta*s;
    }
    if (hd<0) hd+=360;
    if (hd>360)hd-= 360;
    return hd;
}

var CALIBDATA = require("Storage").readJSON("magnav.json",1) || {};
let tiltfixread;
try {
  tiltfixread = require("magnav").tiltfixread;
} catch(e) {
  // magnav not installed
}

// Note actual mag is 360-m, error in firmware
function read_compass() {
  let d;
  if (tiltfixread === undefined || CALIBDATA.offset === undefined || CALIBDATA.scale === undefined) {
    // magnav not installed or no valid calibration, use built in
    d = Bangle.getCompass().heading;
  } else {
    d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale);
  }
  if (isNaN(d)) return; // built in compass heading can return NaN when uncalibrated
  heading = newHeading(d,heading);
  direction = wp_bearing - heading;
  if (direction < 0) direction += 360;
  if (direction > 360) direction -= 360;
  drawCompass(direction);
}


/***** END Compass ***********/

var speed = 0;
var satellites = 0;
var wp;
var dist=0;

function radians(a) {
  return a*Math.PI/180;
}

function degrees(a) {
  var d = a*180/Math.PI;
  return (d+360)%360;
}

function bearing(a,b){
  var delta = radians(b.lon-a.lon);
  var alat = radians(a.lat);
  var blat = radians(b.lat);
  var y = Math.sin(delta) * Math.cos(blat);
  var x = Math.cos(alat)*Math.sin(blat) -
        Math.sin(alat)*Math.cos(blat)*Math.cos(delta);
  return Math.round(degrees(Math.atan2(y, x)));
}

function distance(a,b){
  var x = radians(a.lon-b.lon) * Math.cos(radians((a.lat+b.lat)/2));
  var y = radians(b.lat-a.lat);
  return Math.round(Math.sqrt(x*x + y*y) * 6371000);
}


function drawN(){
  buf2.setFont("Vector",24*scale);
  var bs = wp_bearing.toString();
  bs = wp_bearing<10?"00"+bs : wp_bearing<100 ?"0"+bs : bs;
  var dst = loc.distance(dist);

  // -1=left (default), 0=center, 1=right

  // show distance on the left
  if (previous.dst !== dst) {
    previous.dst = dst;
    buf2.setColor(1);
    buf2.setFontAlign(-1,-1);
    buf2.setFont("Vector", 20*scale);
    buf2.drawString(dst,0,0);
    flip2_bw(0, g.getHeight()-40*scale);
  }

  // bearing, place in middle at bottom of compass
  if (previous.bs !== bs) {
    previous.bs = bs;
    buf2.setColor(1);
    buf2.setFontAlign(0, -1);
    buf2.setFont("Vector",38*scale);
    buf2.drawString(bs,40*scale,0);
    flip2_bw(g.getWidth()/3, g.getHeight()-40*scale);
  }

  // waypoint name on right
  if (previous.wp_name !== wp.name || previous.selected !== selected) {
    previous.selected = selected;
    buf2.setColor(1);
    buf2.setFontAlign(1,-1);     // right, bottom
    buf2.setFont("Vector", 20*scale);
    buf2.drawString(wp.name, 80*scale, 0);

    if (selected)
      flip2_bw(g.getWidth()/3*2, g.getHeight()-40*scale);
    else
      flip2_bb(g.getWidth()/3*2, g.getHeight()-40*scale);
  }
}

var savedfix;

function onGPS(fix) {
  savedfix = fix;
  if (fix!==undefined){
    satellites = fix.satellites;
  }

  if (candraw) {
    if (fix!==undefined && fix.fix==1){
      dist = distance(fix,wp);
      if (isNaN(dist)) dist = 0;
      wp_bearing = bearing(fix,wp);
      if (isNaN(wp_bearing)) wp_bearing = 0;
      drawN();
    }
  }
}

var intervalRef;

function stopdraw() {
  candraw=false;
  prev_course = -1;
  if(intervalRef) {clearInterval(intervalRef);}
}

function startTimers() {
  candraw=true;
  intervalRefSec = setInterval(function() {
    read_compass();
  }, 500);
}

function drawAll(){
  g.setColor(1,1,1);
  drawN();
  drawCompass(direction);
}

function startdraw(){
  g.clear();
  Bangle.drawWidgets();
  startTimers();
  candraw=true;
  drawAll();
}

function setButtons(){
  Bangle.setUI("updown", d=>{
    if (d<0) { nextwp(-1); }
    else if (d>0) { nextwp(1); }
    else { doselect(); }
  });
}

Bangle.on('lcdPower',function(on) {
  if (on) {
    clear_previous();
    startdraw();
  } else {
    stopdraw();
  }
});

var waypoints = require("waypoints").load();
wp=waypoints[0];

function nextwp(inc){
  if (!selected) return;
  wpindex+=inc;
  if (wpindex>=waypoints.length) wpindex=0;
  if (wpindex<0) wpindex = waypoints.length-1;
  wp = waypoints[wpindex];
  drawN();
}

function doselect(){
  if (selected && wpindex>=0 && waypoints[wpindex].lat===undefined && savedfix.fix) {
     waypoints[wpindex] ={name:"@"+wp.name, lat:savedfix.lat, lon:savedfix.lon};
     wp = waypoints[wpindex];
     require("waypoints").save(waypoints);
  }
  if (selected) {
    Bangle.resetCompass(); // reset built in compass when a waypoint is selected
  }
  selected=!selected;
  drawN();
}

Bangle.on('kill',()=>{
  Bangle.setCompassPower(0);
  Bangle.setGPSPower(0);
});

g.clear();
Bangle.setLCDBrightness(1);
Bangle.loadWidgets();
Bangle.drawWidgets();
// load widgets can turn off GPS
Bangle.setGPSPower(1);
Bangle.setCompassPower(1);
Bangle.resetCompass() // reset built in compass on start in case we are not using tilt compensation
drawAll();
startTimers();
Bangle.on('GPS', onGPS);
setButtons();
