/**
 * BangleJS ASTROCALC
 *
 * Inspired by: https://www.timeanddate.com
 *
 * Original Author: Paul Cockrell https://github.com/paulcockrell
 * Created: April 2020
 *
 * Calculate the Sun and Moon positions based on watch GPS and display graphically
 */

const SunCalc = require("suncalc"); // from modules folder

function drawMoon(phase, x, y) {
  const moonImgFiles = [
    "new",
    "waxing-crescent",
    "first-quarter",
    "waxing-gibbous",
    "full",
    "waning-gibbous",
    "last-quarter",
    "waning-crescent",
  ];

  const img = require("Storage").read(`${moonImgFiles[phase]}.img`);
  // image width & height = 92px
  g.drawImage(img, x - parseInt(92 / 2), y);
}

// linear interpolation between two values a and b
// u controls amount of a/b and is in range [0.0,1.0]
function lerp(a,b,u) {
  return (1-u) * a + u * b;
}

function titlizeKey(key) {
  return (key[0].toUpperCase() + key.slice(1)).match(/[A-Z][a-z]+/g).join(" ");
}

function dateToTimeString(date) {
  const hrs = ("0" + date.getHours()).substr(-2);
  const mins = ("0" + date.getMinutes()).substr(-2);
  const secs = ("0" + date.getMinutes()).substr(-2);

  return `${hrs}:${mins}:${secs}`;
}

function drawTitle(key) {
  const fontHeight = 16;
  const x = 0;
  const x2 = g.getWidth() - 1;
  const y = fontHeight + 26;
  //const y2 = g.getHeight() - 1;
  const title = titlizeKey(key);

  g.setFont("6x8", 2);
  g.setFontAlign(0,-1);
  g.drawString(title,(x+x2)/2,y-fontHeight-2);
  g.drawLine(x,y-2,x2,y-2);
}

/**
 * @params {Number} angle Angle of point around a radius
 * @params {Number} radius Radius of the point to be drawn, default 2
 * @params {Object} color Color of the point
 * @params {Number} color.r Red 0-1
 * @params {Number} color.g Green 0-1
 * @params {Number} color.b Blue 0-1
 */
function drawPoint(angle, radius, color) {
  const pRad = Math.PI / 180;
  const faceWidth = g.getWidth()/3; // watch face radius
  const centerPx = g.getWidth() / 2;

  const a = angle * pRad;
  const x = centerPx + Math.sin(a) * faceWidth;
  const y = centerPx - Math.cos(a) * faceWidth;

  if (!radius) radius = 2;

  g.setColor(color.r, color.g, color.b);
  g.fillCircle(x, y + 20, radius);
}

function drawPoints() {
  const startColor = {r: 140, g: 255, b: 255};  // light blue
  const endColor   = {r: 0, g: 0, b: 140};  // dark turquoise

  const steps = 60;
  const step_u = 1.0 / (steps / 2);
  let u = 0.0;

  for (let i = 0; i < steps; i++) {
    const colR = lerp(startColor.r, endColor.r, u) / 255;
    const colG = lerp(startColor.g, endColor.g, u) / 255;
    const colB = lerp(startColor.b, endColor.b, u) / 255;
    const col = {r: colR, g: colG, b: colB};

    if (i >= 0 && i <= 30) {
      u += step_u;
    } else {
      u -= step_u;
    }

    drawPoint((360 * i) / steps, 2, col);
  }
}

function drawData(title, obj, startX, startY) {
  g.clearRect(Bangle.appRect);
  drawTitle(title);

  let xPos, yPos;

  if (typeof(startX) === "undefined" || startX === null) {
    // Center text
    g.setFontAlign(0,-1);
    xPos = (0 + g.getWidth() - 2) / 2;
  } else {
    xPos = startX;
  }

  if (typeof(startY) === "undefined") {
    yPos = 5;
  } else {
    yPos = startY;
  }

  g.setFont("6x8", 1);

  Object.keys(obj).forEach((key) => {
    g.drawString(`${key}: ${obj[key]}`, xPos, yPos += 20);
  });

  g.flip();
}

function drawMoonPositionPage(gps, title) {
  const pos = SunCalc.getMoonPosition(new Date(), gps.lat, gps.lon);
  const moonColor = g.theme.dark ? {r: 1, g: 1, b: 1} : {r: 0, g: 0, b: 0};
  const azimuth = pos.azimuth + Math.PI; // 0 is south, we want 0 to be north

  const pageData = {
    Azimuth: parseInt(azimuth * 180 / Math.PI + 0.5) + '°',
    Altitude: parseInt(pos.altitude * 180 / Math.PI + 0.5) + '°',
    Distance: `${pos.distance.toFixed(0)} km`,
    "Parallactic Ang": parseInt(pos.parallacticAngle * 180 / Math.PI + 0.5) + '°',
  };
  const azimuthDegrees = parseInt(azimuth * 180 / Math.PI + 0.5);

  drawData(title, pageData, null, g.getHeight()/2 - Object.keys(pageData).length/2*20);
  drawPoints();
  drawPoint(azimuthDegrees, 8, moonColor);

  Bangle.setUI({mode: "custom", back: () => moonIndexPageMenu(gps)});
}

function drawMoonIlluminationPage(gps, title) {
  const phaseNames = [
    /*LANG*/"New Moon", /*LANG*/"Waxing Crescent", /*LANG*/"First Quarter", /*LANG*/"Waxing Gibbous",
    /*LANG*/"Full Moon", /*LANG*/"Waning Gibbous", /*LANG*/"Last Quater", /*LANG*/"Waning Crescent",
  ];

  const phase = SunCalc.getMoonIllumination(new Date());
  const phaseIdx = Math.round(phase.phase*8);
  const pageData = {
    Phase: phaseNames[phaseIdx],
  };

  drawData(title, pageData, null, 35);
  drawMoon(phaseIdx, g.getWidth() / 2, g.getHeight() / 2);

  Bangle.setUI({mode: "custom", back: () => moonIndexPageMenu(gps)});
}


function drawMoonTimesPage(gps, title) {
  const times = SunCalc.getMoonTimes(new Date(), gps.lat, gps.lon);
  const moonColor = g.theme.dark ? {r: 1, g: 1, b: 1} : {r: 0, g: 0, b: 0};

  const pageData = {
    Rise: times.rise ? dateToTimeString(times.rise) : "Not today",
    Set: times.set ? dateToTimeString(times.set) : "Not today",
  };

  drawData(title, pageData, null, g.getHeight()/2 - Object.keys(pageData).length/2*20 + 5);
  drawPoints();

  // Draw the moon rise position
  const risePos = SunCalc.getMoonPosition(times.rise, gps.lat, gps.lon);
  const riseAzimuth = risePos.azimuth + Math.PI; // 0 is south, we want 0 to be north
  const riseAzimuthDegrees = parseInt(riseAzimuth * 180 / Math.PI);
  drawPoint(riseAzimuthDegrees, 8, moonColor);

  // Draw the moon set position
  const setPos = SunCalc.getMoonPosition(times.set, gps.lat, gps.lon);
  const setAzimuth = setPos.azimuth + Math.PI; // 0 is south, we want 0 to be north
  const setAzimuthDegrees = parseInt(setAzimuth * 180 / Math.PI);
  drawPoint(setAzimuthDegrees, 8, moonColor);

  Bangle.setUI({mode: "custom", back: () => moonIndexPageMenu(gps)});
}

function drawSunShowPage(gps, key, date) {
  const pos = SunCalc.getPosition(date, gps.lat, gps.lon);

  const hrs = ("0" + date.getHours()).substr(-2);
  const mins = ("0" + date.getMinutes()).substr(-2);
  const secs = ("0" + date.getMinutes()).substr(-2);
  const time = `${hrs}:${mins}:${secs}`;
  const azimuth = pos.azimuth + Math.PI; // 0 is south, we want 0 to be north

  const azimuthDegrees = parseInt(azimuth * 180 / Math.PI + 0.5) + '°';
  const altitude = parseInt(pos.altitude * 180 / Math.PI + 0.5) + '°';

  const pageData = {
    Time: time,
    Altitude: altitude,
    Azimuth: azimuthDegrees,
  };

  drawData(key, pageData, null, g.getHeight()/2 - Object.keys(pageData).length/2*20 + 5);

  drawPoints();

  // Draw the suns position
  drawPoint(azimuthDegrees, 8, {r: 1, g: 1, b: 0});

  Bangle.setUI({mode: "custom", back: () => sunIndexPageMenu(gps)});

  return null;
}

function sunIndexPageMenu(gps) {
  const sunTimes = SunCalc.getTimes(new Date(), gps.lat, gps.lon);

  const sunMenu = {
    "": {
      "title": "-- Sun --",
    },
    "Current Pos": () => {
      E.showMenu();
      drawSunShowPage(gps, "Current Pos", new Date());
    },
  };

  Object.keys(sunTimes).sort().reduce((menu, key) => {
    const title = titlizeKey(key);
    menu[title] = () => {
      E.showMenu();
      drawSunShowPage(gps, key, sunTimes[key]);
    };
    return menu;
  }, sunMenu);

  sunMenu["< Back"] = () => indexPageMenu(gps);

  return E.showMenu(sunMenu);
}


function moonIndexPageMenu(gps) {
  const moonMenu = {
    "": {
      "title": "-- Moon --",
    },
    "Times": () => {
      E.showMenu();
      drawMoonTimesPage(gps, /*LANG*/"Times");
    },
    "Position": () => {
      E.showMenu();
      drawMoonPositionPage(gps, /*LANG*/"Position");
    },
    "Illumination": () => {
      E.showMenu();
      drawMoonIlluminationPage(gps, /*LANG*/"Illumination");
    },
    "< Back": () => indexPageMenu(gps),
  };

  return E.showMenu(moonMenu);
}

function indexPageMenu(gps) {
  const menu = {
    "": {
      "title": /*LANG*/"Select",
    },
    /*LANG*/"Sun": () => {
      sunIndexPageMenu(gps);
    },
    /*LANG*/"Moon": () => {
      moonIndexPageMenu(gps);
    },
    "< Back": () => { load(); }
  };

  return E.showMenu(menu);
}

//function getCenterStringX(str) {
//  return (g.getWidth() - g.stringWidth(str)) / 2;
//}

function init() {
  let location = require("Storage").readJSON("mylocation.json",1)||{"lat":51.5072,"lon":0.1276,"location":"London"};
  Bangle.loadWidgets();
  indexPageMenu(location);
  Bangle.drawWidgets();
}

init();
