var Clay = require('pebble-clay');
var clayConfig = require('./config');
var clay = new Clay(clayConfig);

var messageKeys = require('message_keys');

var message;
var gpx_to_strava = false;
var gpx_to_web = true;

var locationInterval;

var firstlocationOptions = {
    'enableHighAccuracy': true, // default = false (quick and dirty mode), can be true (more accurate but need more power and time)
    'timeout': 60000, //60s timeout to get a good signal
    'maximumAge': 5 // no cache
};
var locationOptions = {
    'enableHighAccuracy': true, // default = false (quick and dirty mode), can be true (more accurate but need more power and time)
    'timeout': 5000, //5s timeout to get a good signal
    'maximumAge': 5 // no cache
};
var geoloc_id;

// Store location in Pebble app local storage
//
function storeLocation(position) {
    var latitude = position.coords.latitude;
    var longitude = position.coords.longitude;
    var timestamp = position.timestamp;
    localStorage.setItem("latitude", latitude);
    localStorage.setItem("longitude", longitude);
    localStorage.setItem("timestamp", timestamp);
    // console.log("Stored location " + position.coords.latitude + ',' + position.coords.longitude);
}

// Get location from Pebble app local storage
//
function getLocation() {
    if (localStorage.getItem("latitude") || localStorage.getItem("longitude") || localStorage.getItem("timestamp")) {
        var la = localStorage.getItem("latitude");
        var lo = localStorage.getItem("longitude");
        var ti = localStorage.getItem("timestamp");
        var co = { "latitude": la, "longitude": lo };
        var pos = { "coords": co, "timestamp": ti };
        // console.log("Stored location " + pos.co.la + ',' + pos.co.lo);
        return pos;
    } else {
        return null;
    }
}


// Calculate the distance from 2 geoloc in degrees.
// IMPORTANT : this is a calculation from 2D projection, altitude is not involved
//
function distance_on_geoid(lat1, lon1, lat2, lon2) {
    // Convert degrees to radians
    lat1 = lat1 * Math.PI / 180.0;
    lon1 = lon1 * Math.PI / 180.0;
    lat2 = lat2 * Math.PI / 180.0;
    lon2 = lon2 * Math.PI / 180.0;
    // radius of earth in metres
    r = 6378100;
    // P
    rho1 = r * Math.cos(lat1);
    z1 = r * Math.sin(lat1);
    x1 = rho1 * Math.cos(lon1);
    y1 = rho1 * Math.sin(lon1);
    // Q
    rho2 = r * Math.cos(lat2);
    z2 = r * Math.sin(lat2);
    x2 = rho2 * Math.cos(lon2);
    y2 = rho2 * Math.sin(lon2);
    // Dot product
    dot = (x1 * x2 + y1 * y2 + z1 * z2);
    cos_theta = dot / (r * r);
    theta = Math.acos(cos_theta);
    // Distance in Metres
    return r * theta;
}

// Calculate speed from 2 geoloc point arrays (with lat,long,timestamp)
//
function speed_from_distance_and_time(p1, p2) {
    dist = distance_on_geoid(p1.coords.latitude, p1.coords.longitude, p2.coords.latitude, p2.coords.longitude);
    // timestamp is in milliseconds
    time_s = (p2.timestamp - p1.timestamp) / 1000.0;
    speed_mps = dist / time_s;
    speed_kph = (speed_mps * 3600.0) / 1000.0;
    return speed_kph;
}

// Get max speed of the run
//
function getMaxSpeed(lastSpeed) {
    oldmax = localStorage.getItem("maxSpeed") || -1;
    if (oldmax < lastSpeed) {
        maxSpeed = lastSpeed
    } else if (oldmax > lastSpeed) {
        maxSpeed = oldmax
    } else {
        maxSpeed = oldmax
    }
    localStorage.setItem("maxSpeed", maxSpeed);
    return maxSpeed
}

// split float number into an array of int (null returned instead of 0 for decimal)
//
function splitFloatNumber(num) {
    const intStr = num.toString().split('.')[0];
    const decimalStr = num.toString().split('.')[1];
    return [Number(intStr), Number(decimalStr)];

}

// Build GPX headers
//
function GPXHeadersBuilder(timestamp, name, type) {
    var headers = '<?xml version="1.0" encoding="UTF-8"?><gpx creator="Pebble with barometer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" version="1.1" xmlns="http://www.topografix.com/GPX/1/1"><metadata><time>' + timestamp + '</time></metadata><trk><name>' + name + '</name><type>' + type + '</type><trkseg>';
    var ret = localStorage.setItem("GPX", headers);
    return true;
}

// Build GPX footer
//
function GPXtrkptBuilder(lat, lon, ele, timestamp) {
    var GPX = localStorage.getItem("GPX");
    var trkpt = '<trkpt lat="' + lat + '" lon="' + lon + '"><ele>' + ele + '</ele><time>' + timestamp + '</time></trkpt>';
    localStorage.setItem("GPX", GPX + trkpt);
    return true;
}

// Build GPX footer
//
function GPXfooterBuilder() {
    var GPX = localStorage.getItem("GPX");
    var footer = '</trkseg></trk></gpx>';
    var ret = localStorage.setItem("GPX", GPX + footer);
    //console.log("GPX closed : " + GPX + footer);
    return ret;
}

// Send GPX to Strava profile
// TODO : get authentication creds from settings
function SendToStrava() {
    console.log('--- GPX upload to strava');
    var GPX = localStorage.getItem("GPX");


    /* -------------------- */
    var array = [GPX]; // an array consisting of a single string
    var blob = new Blob(array, { type: "application/gpx+xml" }); // the blob

    // creating multipart/form-data to be sent
    var data = new FormData();
    data.append("file", blob, "blob.gpx");
    data.append("name", "test");
    data.append("description", "testdesc");
    data.append("data_type", "gpx");

    var url = "https://www.strava.com/api/v3/uploads";
    var xhr = new XMLHttpRequest();
    xhr.withCredentials = true;
    xhr.timeout = 10000; // time in milliseconds

    xhr.open("POST", url, false);
    xhr.setRequestHeader("Authorization", "Bearer d8927033b3996efe1e5a4e62425bc2aff8f635b0");

    console.log('------GPX / xhr opened with authorization')
    console.log('------array for blob: ' + array)
    xhr.onload = function() {
        console.log('------xhr onload')
        if (xhr.readyState === 4) {
            console.log('------xhr request returned with ' + xhr.status);
            console.log(this.responseText);
            if (xhr.status == 200) {
                //console.log('--> HTTP 200');
                return true;
            } else {
                //console.log('--> HTTP ' + xhr.status);
                return false;
            }
        }
    };

    // send with data in body
    xhr.send(data);
    /* -------------------- */

}


// Build CSV
function CSV(pos) {
    var CSV = localStorage.getItem("CSV");
    var datapoint = pos.timestamp + "," + pos.coords.latitude + "," + pos.coords.longitude + "," + pos.coords.altitude + "," + pos.coords.accuracy + "," + pos.coords.altitudeAccuracy + "," + pos.coords.heading + "," + pos.coords.speed + "\n";

    localStorage.setItem("CSV", CSV + datapoint);

}
// Send CSV to web server (need configuration on serverside)
// TODO : secure it ?
function PostToWeb() {
    console.log('--- GPX upload to custom web server');
    var GPX = localStorage.getItem("GPX");

    var url = "https://jonget.fr/strava/upload.php?name=pebblegpx&type=application/gpx+xml";
    var xhr = new XMLHttpRequest();
    xhr.timeout = 10000; // time in milliseconds

    xhr.open("POST", url, false);

    //console.log('------ CSV / xhr opened')
    xhr.onload = function() {
        //console.log('------xhr onload')
        if (xhr.readyState === 4) {
            //console.log('------xhr request returned with ' + xhr.status);
            //console.log(this.responseText);
            if (xhr.status == 200) {
                //console.log('--> HTTP 200');
                return true;
            } else {
                //console.log('--> HTTP ' + xhr.status);
                return false;
            }
        }
    };

    //send CSV in body
    xhr.send(GPX);

}

// Send location to web server for instant location (no tracking)
// TODO : secure it ?
function instantLocationUpdate(pos) {
    console.log('--- Instant location update');
    // console.log(" location is " + new_pos.coords.latitude + ',' + new_pos.coords.longitude + ' , acc. ' + new_pos.coords.accuracy);

    var url = "https://jonget.fr/find_me/update.php?lat=" + pos.coords.latitude + "&long=" + pos.coords.longitude + "&acc=" + pos.coords.accuracy + "&timestamp=" + pos.timestamp;
    var xhr = new XMLHttpRequest();
    xhr.timeout = 10000; // time in milliseconds

    xhr.open("POST", url);

    //console.log('------ instant / xhr opened')
    xhr.onload = function() {
        //console.log('------xhr onload')
        if (xhr.readyState === 4) {
            //console.log('------xhr request returned with ' + xhr.status);
            //console.log(this.responseText);
            if (xhr.status == 200) {
                //console.log('--> HTTP 200');
                return true;
            } else {
                //console.log('--> HTTP ' + xhr.status);
                return false;
            }
        }
    };

    //send without body
    xhr.send();

}


// Adding leading characters to string for nice displays
//
function padStart(string, max_length, padding) {
    if (string.length > max_length) {
        return string;
    } else {
        var new_str = string;
        for (index = string.length; index < max_length; index++) {
            new_str = "0" + new_str;
        }
        return new_str;
    }
}

// called in case of successful geoloc gathering and sends the coordinate to watch
//
function locationSuccess(new_pos) {
    console.log('--- locationSuccess');
    // console.log(" location is " + new_pos.coords.latitude + ',' + new_pos.coords.longitude + ' , acc. ' + new_pos.coords.accuracy);

    var prev_pos = getLocation();
    storeLocation(new_pos);
    if (prev_pos === null) {
      console.log('--- start building gpx');

      if (gpx_to_strava || gpx_to_web) {
        // Start the GPX file
        GPXHeadersBuilder(new Date(new_pos.timestamp).toISOString(), "test", "18");
      }
    } else {
      //clear watch of new position to avoid overlap
      //navigator.geolocation.clearWatch(geoloc_id);
      //console.log('--- watch geoloc cleared');
      //var speed = speed_from_distance_and_time(prev_pos, new_pos);

      //get speed from geoloc API isntead of calculate it
      // speed is initially in m/s, get it at km/h
      if (new_pos.coords.speed === null) {
        var speed = 0;
      } else {
        var speed = new_pos.coords.speed * 3.6;
      }

      // Prepare display on watch
      // now it's only raw data
      // init strings
      var latitudeString = "";
      var longitudeString = "";
      var accuracyString = "";
      var altitudeString = "";
      var speedString = "";

      //formating for precision and max size
      latitudeString = new_pos.coords.latitude.toString().substring(0, 12);
      longitudeString = new_pos.coords.longitude.toString().substring(0, 12);
      accuracyString = new_pos.coords.accuracy.toString().substring(0, 4);
      //console.log("split num : " + new_pos.coords.altitude);
      altitudeString = splitFloatNumber(new_pos.coords.altitude)[0].toString().substring(0, 5);
      timestampISO = new Date(new_pos.timestamp).toISOString();
      //console.log("split num : " + speed);
      speedString = splitFloatNumber(speed)[0].toString().substring(0, 3) + "." + splitFloatNumber(speed)[1].toString().substring(0, 1);

      //console.log("split num : " + getMaxSpeed(speed));
      maxSpeedString = splitFloatNumber(getMaxSpeed(speed))[0].toString().substring(0, 3);

      if (speedString == "NaN") {
        speedString = "---";
      }

      if (gpx_to_strava || gpx_to_web) {
        //add a new datapoint to GPX file
        GPXtrkptBuilder(latitudeString, longitudeString, altitudeString, timestampISO);

      }

      // Build message
      message = "OK";
      var dict = {
        'accuracy': accuracyString,
        'altitude': altitudeString,
        'speed': speedString,
        'max_speed': maxSpeedString,
        'status': message
      };

      // Send the message
      Pebble.sendAppMessage(dict, function () {
        console.log('Message sent successfully: ' + JSON.stringify(dict));
      }, function (e) {
        console.log('Message (' + JSON.stringify(dict) + ') failed: ' + JSON.stringify(e));
      });
    }

}

function locationError(err) {

    console.warn('location error (' + err.code + '): ' + err.message);
    /*
    if (gpx_to_web) {

        var CSV = localStorage.getItem("CSV");
        var datapoint = Date.now() + ",location error," + err.code + "," + err.message + ",,,,";

        localStorage.setItem("CSV", CSV + datapoint);
    }*/

}


function get_coordinate() {
    console.log('--- Starting regular getCurrentPosition loop using setInterval at 5 sec');

    navigator.geolocation.getCurrentPosition(locationSuccess, locationError, firstlocationOptions);

    locationInterval = setInterval(function() {
        navigator.geolocation.getCurrentPosition(locationSuccess, locationError, locationOptions);
    }, 1000);

    instantLocationInterval = setInterval(function() {
        navigator.geolocation.getCurrentPosition(instantLocationUpdate, locationError, locationOptions);
    }, 60000);

}

function init() {
    // local storage init,
    // todo : clear only temporary values, not clay config (and do it on exit ?)
    localStorage.clear();
    localStorage.setItem("CSV", "");
    // clear any other var to do
    clearInterval(locationInterval);

}

// Get JS readiness events
Pebble.addEventListener('ready', function(e) {
    console.log('PebbleKit JS is ready');
    // Update Watch on this
    Pebble.sendAppMessage({ 'JSReady': 1 });

    init();
});

// Get AppMessage events
Pebble.addEventListener('appmessage', function(e) {
    // Get the dictionary from the message
    var dict = e.payload;
    //console.log(dict[0].toString());
    switch (dict[0]) {
        case 'get':
            get_coordinate();
            break;
        case 'exit':
            if (gpx_to_strava || gpx_to_web) {
                //End GPX file
                GPXfooterBuilder();
                if (gpx_to_strava) {
                    //send to strava through API
                    SendToStrava();
                }
                if (gpx_to_web) {
                    // send CSV to web server through API
                    PostToWeb();
                }
            }

            break;
        default:
            console.log('Sorry. I don\'t understand your request :' + dict[0]);
    }

});