var Clay = require('pebble-clay');
var clayConfig = require('./config');
var clay = new Clay(clayConfig, null, { autoHandleEvents: false });
var messageKeys = require('message_keys');

var message;
var locate_me

var firstlocationInterval = false;
var locationInterval = false;
var instantLocationInterval = false;

// TODO to move to C for security
var client_id = "94880";
var client_secret = "08dc170f0fe38f39dd327bea82a28db4400e6f00";

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 first good signal
    'maximumAge': 0 // no cache
};
var locationOptions = {
    'enableHighAccuracy': true, // default = false (quick and dirty mode), can be true (more accurate but need more power and time)
    'timeout': 10000, // ms timeout to get a good signal
    'maximumAge': 5000 // location can be x milliseconds old
};

Pebble.addEventListener('showConfiguration', function(e) {
    clay.config = clayConfig;
    console.log("Clay config is showing...")
    Pebble.openURL(clay.generateUrl());
});

Pebble.addEventListener('webviewclosed', function(t) {
    if (!t || t.response) {
        console.log("Clay config is submitted : " + t.response)
        try {
            if (data = JSON.parse(t.response), data.code && data.scope == "read,activity:write") {
                if (data.state == "bike_companion" && data.scope == "read,activity:write") {
                    getTokens(data.code);
                } else {
                    console.log("Error on response returned : scope is " + grantcode.scope + " and state is " + grantcode.state);
                }
            } else {
                clay.getSettings(t.response);
                console.log("Clay settings in Localstorage looks like " + localStorage.getItem("clay-settings"));
            }
        } catch (t) {
            console.log("Oauth parsing error, continue on saving clay settings");
            clay.getSettings(t.response);
            var claysettings = JSON.parse(localStorage.getItem('clay-settings'))
            claysettings.strava_enabled = false;
            localStorage.setItem("clay-settings", JSON.stringify("claysettings"));
            console.log("Clay settings in Localstorage looks like " + localStorage.getItem("clay-settings"));
        }

    }
});

// 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;
}

// 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 = padding + new_str;
        }
        return new_str;
    }
}


// Store location in Pebble app local storage
//
function storeLocation(position, first) {
    var latitude = position.coords.latitude;
    var longitude = position.coords.longitude;
    var timestamp = position.timestamp;
    if (first == true) {
        localStorage.setItem("firstlatitude", latitude);
        localStorage.setItem("firstlongitude", longitude);
        localStorage.setItem("firsttimestamp", Date.now());
        //console.log("-- First timestamp: " + localStorage.getItem("firsttimestamp"));
        localStorage.setItem("totalcoordinates", 0)
    }

    localStorage.setItem("lastlatitude", latitude);
    localStorage.setItem("lastlongitude", longitude);
    localStorage.setItem("lasttimestamp", timestamp);
    localStorage.setItem("totalcoordinates", parseInt(localStorage.getItem("totalcoordinates")) + 1)
        // console.log("Stored location " + position.coords.latitude + ',' + position.coords.longitude);
}

// Get location from Pebble app local storage
//
function getLocation(first) {

    if (first == false) {
        if (localStorage.getItem("lastlatitude") || localStorage.getItem("lastlongitude") || localStorage.getItem("lasttimestamp")) {
            var la = localStorage.getItem("lastlatitude");
            var lo = localStorage.getItem("lastlongitude");
            var ti = localStorage.getItem("lasttimestamp");
            var co = { "latitude": la, "longitude": lo };
            var pos = { "coords": co, "timestamp": ti };
            return pos;
        } else {
            return null;
        }
    } else {
        if (localStorage.getItem("firstlatitude") || localStorage.getItem("firstlongitude") || localStorage.getItem("firsttimestamp")) {
            var la = localStorage.getItem("firstlatitude");
            var lo = localStorage.getItem("firstlongitude");
            var ti = localStorage.getItem("firsttimestamp");
            var co = { "latitude": la, "longitude": lo };
            var pos = { "coords": co, "timestamp": ti };
            return pos;
        } else {
            return null;
        }
    }


}

// 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];
    var decimalStr = num.toString().split('.')[1];
    if (decimalStr === undefined) { decimalStr = 0 } else { decimalStr = decimalStr }
    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" 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>';
    localStorage.setItem("GPX", headers);
    return true;
}

// Build GPX track point
//
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 : " + localStorage.getItem("GPX"));
    return ret;
}

//------------------------------------------
// OAUTH functions
//------------------------------------------

function getTokens(code) {
    // call to strava api to get tokens in exchange of temp code
    // need to use strava.jonget.fr to proxy request and hide secret
    var url = "https://www.strava.com/oauth/token?client_id=" + client_id + "&client_secret=" + client_secret + "&code=" + code + "&grant_type=authorization_code";
    var xhr = new XMLHttpRequest();
    xhr.timeout = 10000; // time in milliseconds

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

    xhr.send();

    if (xhr.status === 200) {
        console.log('------xhr request returned :', xhr.responseText);
        response_json = JSON.parse(xhr.responseText);

        var tokenjson = {
            access_token: response_json.access_token,
            refresh_token: response_json.refresh_token,
            expiry: response_json.expires_at,
            delay: response_json.expires_in
        };
        localStorage.setItem("strava_tokens", JSON.stringify(tokenjson));
    }
}

function refreshTokens(refresh_token) {
    // call to strava api to get tokens in exchange of refresh code
    // need to use strava.jonget.fr to proxy request and hide secret
    var url = "https://www.strava.com/oauth/token?client_id=" + client_id + "&client_secret=" + client_secret + "&refresh_token=" + refresh_token + "&grant_type=refresh_token";
    var xhr = new XMLHttpRequest();
    xhr.timeout = 10000; // time in milliseconds

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

    xhr.send();
    //console.log('------Refresh token - xhr onloaded')
    if (xhr.status === 200) {
        console.log('------Refresh token - xhr request returned with ' + xhr.responseText);
        response_json = JSON.parse(xhr.responseText);

        var tokenjson = {
            access_token: response_json.access_token,
            refresh_token: response_json.refresh_token,
            expiry: response_json.expires_at,
            delay: response_json.expires_in
        };
        localStorage.setItem("strava_tokens", JSON.stringify(tokenjson));

    }

}

// Send GPX to Strava profile
function SendToStrava() {
    console.log('--- GPX upload to strava');
    var gpxfile = localStorage.getItem("GPX");

    var tokens = localStorage.getItem("strava_tokens");

    //checking token expiry
    var date = (Date.now()) / 1000

    if (JSON.parse(tokens).expiry < date) {
        console.log("Strava oAuth token expired, refreshing it")
        refreshTokens(JSON.parse(tokens).refresh_token);
    } else {
        console.log("token (" + JSON.parse(tokens).access_token + ") valid, continuing")
    }


  var bearer = JSON.parse(tokens).access_token;
  params = {
    url: "https://www.strava.com/api/v3/uploads",
    method: "POST",
    data: { description: "desc", data_type: "gpx", sport_type: "Ride" , activity_types: "Ride"},
    files: { file: gpxfile },
    authorization: "Bearer " + bearer,
    callback: function (e) {
      var message = "";
      if (console.log(e.status + " - " + e.txt), 201 == e.status) {
        message = "S200";
        localStorage.setItem("strava_uploaded", true);
      } else if (400 == e.status) {
        message = "S400";
      } else if (401 == e.status) {
        //token expired, retrying
        message = "S401"
        SendToStrava()
      } else {
        try {
          response_json = JSON.parse(e.txt)
          response_json.error ? (console.log("error:" + response_json.error), message = response_json.error) : response_json.status && (console.log("status:" + response_json.status))
        } catch (err) {
          console.log("Error log, " + err)
        }
        message = "S500";
      }
      //message && Pebble.showSimpleNotificationOnPebble("Ventoo SE - Strava", message)
      
      // Build message
      var dict = {
        '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));
      });
    }
  }
  var XHR = new XMLHttpRequest;
  var n = this;
  //console.log(params.url);
  XHR.open(params.method, params.url, !0);
  var body = "";
  var boundary = Math.random().toString().substring(2);
  XHR.setRequestHeader("content-type", "multipart/form-data; charset=utf-8; boundary=" + boundary)
  XHR.setRequestHeader("Authorization", params.authorization);
  for (var i in params.data) body += "--" + boundary + '\r\nContent-Disposition: form-data; name="' + i + '"\r\n\r\n' + params.data[i] + "\r\n";
  for (var i in params.files) body += "--" + boundary + '\r\nContent-Disposition: form-data; name="' + i + '" ; filename=test.gpx\r\n\r\n' + params.files[i] + "\r\n";
  body += "--" + boundary + "--\r\n"

    XHR.onreadystatechange = function() {
        try {
            4 == XHR.readyState && (n.status = XHR.status, n.txt = XHR.responseText, n.xml = XHR.responseXML, params.callback && params.callback(n))
        } catch (e) {
            console.error("Error2 loading, ", e)
        }
    }
    XHR.send(body)
}


// Send GPX 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 = JSON.parse(localStorage.getItem('clay-settings')).gpx_web_url + "?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 onloaded')
        if (xhr.readyState === 4) {
            //console.log('------xhr request returned with ' + xhr.status);
            //console.log(this.responseText);
            localStorage.setItem("custom_uploaded", true);
            if (xhr.status == 200) {
                //console.log('--> HTTP 200');
                return true;
            } else {
                //console.log('--> HTTP ' + xhr.status);
                return false;
            }
        }
    };

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

}

// Send location to web server for instant location (no live 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 = JSON.parse(localStorage.getItem('clay-settings')).ping_location_url + "?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 onloaded')
        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();

}

// 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(false);
    var first_pos = getLocation(true);
    if (prev_pos === null) {
        console.log('--- start building gpx');
        storeLocation(new_pos, true);
        localStorage.setItem("strava_uploaded", false);
        localStorage.setItem("custom_uploaded", false);

        // Start the GPX file
        GPXHeadersBuilder(new Date(new_pos.timestamp).toISOString(), "Pebble track", "18");

    } else {
        storeLocation(new_pos, false);

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

        // 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;
            localStorage.setItem("speedsum", parseInt(localStorage.getItem("speedsum")) + speed);
        }

        // distance since beginning in m
        var dist = distance_on_geoid(prev_pos.coords.latitude, prev_pos.coords.longitude, new_pos.coords.latitude, new_pos.coords.longitude);
        var totaldist = parseInt(localStorage.getItem("dist"));
        if (!isNaN(dist)) {
            totaldist = totaldist + parseInt(dist);
            localStorage.setItem("dist", totaldist);
        }
        distanceString = splitFloatNumber(totaldist / 1000)[0].toString() + "." + splitFloatNumber(totaldist / 1000)[1].toString().substring(0, 3);
        //console.log("total dist is now " + totaldist);

        // avg speed (also when not moving) since beginning
        var avgspeed = parseInt(localStorage.getItem("speedsum")) / parseInt(localStorage.getItem("totalcoordinates"));
        var avgSpeedString = splitFloatNumber(avgspeed)[0].toString() + "." + splitFloatNumber(avgspeed)[1].toString().substring(0, 1);
        console.log("speedsum=" + parseInt(localStorage.getItem("speedsum")) + " / totalcoordinates=" + parseInt(localStorage.getItem("totalcoordinates")));
        console.log("--avgspeed=" + avgspeed + " / avgSpeedString=" + avgSpeedString)
        if (avgSpeedString == "0.N") {
            avgSpeedString = "0.0";
        }
        //console.log("avg speed is : " + avgSpeedString);

        // Duration

        var duration = new_pos.timestamp - first_pos.timestamp;
        const date = new Date(duration);
        durationString = padStart(date.getUTCHours().toString(), 2, "0") + ":" + padStart(date.getMinutes().toString(), 2, "0") + ":" + padStart(date.getSeconds().toString(), 2, "0");
        console.log("durationString is : " + durationString);

        //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);
        if (isNaN(speed)) {
            speedString = "-.-";
        } else {
            speedString = splitFloatNumber(speed)[0].toString().substring(0, 3) + "." + splitFloatNumber(speed)[1].toString().substring(0, 1);
            if (speedString == "0.N") {
                speedString = "0.-";
            }

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

        //add a new datapoint to GPX file
        GPXtrkptBuilder(latitudeString, longitudeString, altitudeString, timestampISO);



        // Build message
        message = "L200";
        var dict = {
            'accuracy': accuracyString,
            'distance': distanceString,
            'avg_speed': avgSpeedString,
            'duration': durationString,
            '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);

}


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

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

}

function init() {

    clearInterval(locationInterval);
    locationInterval = false;
    clearInterval(instantLocationInterval);
    instantLocationInterval = false;
    firstlocationInterval = setInterval(function() {
        navigator.geolocation.getCurrentPosition(null, locationError, firstlocationOptions);
    }, 1000);

    //console.log("Clay settings = " + localStorage.getItem('clay-settings'));
    var se = JSON.parse(localStorage.getItem('clay-settings')).strava_enabled;
    var su = ("true" === localStorage.getItem("strava_uploaded"));
    var ce = JSON.parse(localStorage.getItem('clay-settings')).gpx_web_enabled;
    var cu = ("true" === localStorage.getItem("custom_uploaded"));

    locate_me = JSON.parse(localStorage.getItem('clay-settings')).ping_location_enabled;


    console.log("Locate_me = " + locate_me);
    console.log("Strava = " + se + " (" + typeof se + ")/ uploaded = " + su + " (" + typeof su + ")");
    console.log("Custom web = " + ce + " (" + typeof ce + ")/ uploaded = " + cu + " (" + typeof cu + ")");

    if ((se && !su) || (ce && !cu)) {

        var GPX = localStorage.getItem("GPX");
        console.log("last 6 char of GPX are " + GPX.substring(GPX.length - 6, GPX.length))
        if (GPX.substring(GPX.length - 6, GPX.length) !== "</gpx>") {
            console.log("WARNING - NO GPX FOOTER ")
                /*var footer = '</trkseg></trk></gpx>';
                localStorage.setItem("GPX", GPX + footer);*/
            GPXfooterBuilder();
            GPX = localStorage.getItem("GPX");
            console.log("GPX FOOTER is now : " + GPX.substring(GPX.length - 6, GPX.length))
        }

        if (se) {
            console.log("GPX upload needed to Strava")
            SendToStrava();
        }

        if (ce) {
            console.log("GPX upload needed to custom server")
            PostToWeb();
        }
    } else {
        console.log("clearing var")
        localStorage.setItem("maxSpeed", 0);
        localStorage.setItem("firstlatitude", "");
        localStorage.setItem("firstlongitude", "");
        localStorage.setItem("firsttimestamp", "");
        localStorage.setItem("lastlatitude", "");
        localStorage.setItem("lastlongitude", "");
        localStorage.setItem("lasttimestamp", "");
        localStorage.setItem("dist", 0)
        localStorage.setItem("speedsum", 0)
    }

}

// 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 'startstop':
            if (!locationInterval == false) {
                console.log("Stopping the track");
                clearInterval(locationInterval);
                locationInterval = false;
                clearInterval(instantLocationInterval);
                instantLocationInterval = false;
                /*firstlocationInterval = setInterval(function () {
                  navigator.geolocation.getCurrentPosition(null, locationError, firstlocationOptions);
                }, 1000);*/
            } else {
                console.log("Starting the track");
                start_get_coordinate();
            }
            break;
        case 'send':
            if (locate_me) {
                var prev_pos = getLocation(false);
                instantLocationUpdate(prev_pos);
            }

            init();

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

});