"use strict";
/* global document */

/**
 * @file generic function of heremap
 * @author devbab
 */

const request = require("superagent");
const Throttle = require("superagent-throttle");

// by default, unless specified fby calling config
// environment usable with node
let APP_ID = process.env.APP_ID;
let APP_CODE = process.env.APP_CODE;
let API_KEY = process.env.API_KEY;
let CIT = ""; // production by default
let PROTOCOL = "https:"; // by default
let _useHTTPS = true; // by default
let _home = ".";

// RPS limits for throttling
let _throttleList = {
    regular: {
        rps: 1,
        concurrent: 1
    },
    gc: {
        rps: 10,
        concurrent: 20
    },
    rgc: {
        rps: 10,
        concurrent: 50
    },
    routing: {
        rps: 10,
        concurrent: 20
    },
    pde: {
        rps: 5,
        concurrent: 10
    }
};


// find out where we are and relative position for png/svg files
// pay attention if library is build in ./dist
if (process.browser) {
    let _script = document.getElementsByTagName("script");
    let _file = _script[_script.length - 1].src;
    let _path = _file.substring(0, _file.lastIndexOf("/"));
    let pos = _path.indexOf("heremap");
    _home = _path.substring(0, pos + "heremap".length) + "/";

}

/**
 * To configure app_id, app_code and optionally use CIT and http
 * @alias hm:config
 *
 * @param opt {Object}  - `opt` with parameters.
 * @param [opt.app_id] {string} - the app_id from developer.here.com (REST)
 * @param [opt.app_code] {string} - the app_code from developer.here.com (REST)
 * @param [opt.api_key] {string} - the api_key from developer.here.com (JS)
 * @param [opt.useCIT=false] {boolean} - true to use CIT environment. 
 * @param [opt.useHTTP=false] {string} - true to use HTTP. 
 * @param [opt.useHTTPS=true] {string} - true to use HTTPS. 
 * @param [opt.rps] {object} - set rps limit {routing:1,gc:2,rgc:10,pde:10} 
 *
 * @example
 * ```js
 *  hm.config({
 *      app_id: "YOUR APP_ID",
 *      app_code: "YOUR APP_CODE",
 *      api_key: "YOUR API_KEY",
 *   });
 *  ```
 */
function config(opt) {
    if (opt.app_id) APP_ID = opt.app_id;
    if (opt.app_code) APP_CODE = opt.app_code;
    if (opt.api_key) API_KEY = opt.api_key;
    if (opt.useCIT) CIT = ".cit";
    if (opt.useHTTP) {
        PROTOCOL = "http:";
        _useHTTPS = false;
    }
    if (opt.useHTTPS) {
        PROTOCOL = "https:";
        _useHTTPS = true;
    }


    if (opt.rps) {
        const modules = Object.keys(opt.rps);
        modules.forEach(module => {
            _throttleList[module].rps = opt.rps[module];
            _throttleList[module].concurrent = opt.rps[module]; // concurrent session to be same
        });
    }
}

/**
 * return URL of module home directory. 
 * svg icons are under getHome()+"/svg/"
 * images are under getHome()+"/img/"
 * @ignore
 *  @alias hm:getHome
 * @return {string} url of home directory including http or https. 
 */
function getHome() {
    return _home;
}

/**
 * returns app_id
 * @ignore
 *  @alias hm:getAppId
 * @return {string} app_id
 */
function getAppId() {
    return APP_ID;
}

/**
 * return app_code
 *  @ignore
 *  @alias hm:getHome
 *  @alias hm:getAppCode
 * @return {string} app_code
 */
function getAppCode() {
    return APP_CODE;
}

/**
 * return api_key
 *  @ignore
 *  @alias hm:getHome
 *  @alias hm:getApiKey
 * @return {string} api_key
 */
function getApiKey() {
    return API_KEY;
}
/**
 * return true if using CIT
 * @ignore
 * @alias hm:getCIT
 * @return {booolean} true if using CIT
 */
function getCIT() {
    return CIT;
}

/**
 * return protocol used, http:// or https://
 * @ignore
 * @alias hm:getProtocol
 * @return {string} - protocol
 */
function getProtocol() {
    return PROTOCOL;
}

/**
 * return true is https is used
 * @ignore
 *  @alias hm:useHTTPS
 * @return {boolean} - true if https is used
 */
function useHTTPS() {
    return _useHTTPS;
}

/**
 * add credentials to object provided
 * @ignore
 *  @alias hm:addCredentials
 * @param  {...objects} list of `objects` 
 * @return {object} object with all input objectconcatenated, and app_id/app_code inserted
 */
function addCredentials(...obj) {
    return Object.assign({
        app_id: APP_ID,
        app_code: APP_CODE
    }, ...obj);
}

/**
 * build HERE REST full url, taking in account protocol and cit. for instance  cm.buildUrl("geocoder", "api.here.com/6.2/geocode.json"
 * @ignore
 * @alias hm:buildUrl
 * @param {string} base - base name
 * @param {string} endpoint - end point
 * @return {string} full url
 */
function buildUrl(base, endpoint) {
    return PROTOCOL + "//" + base + CIT + "." + endpoint;
}

/**
 * does get/post request to HERE REST backend and manage main errors
 * @ignore
 * @alias hm:hereRest
 * @param {string} url  - url to call , for instance from buildUrl
 * @param {object} settings - settings to add in request
 * @param  {string} mode=get - mode "get" or "post"
 * @return {promise} - promise to resolve/reject
 */
async function hereRest(url, settings, mode = "get", needresp = "true") {

    let p;
    if (mode == "get")
        p = request.get(url).query(settings);
    else
        p = request.post(url).send(settings);

    return p
        .then(res => {
            // res.body, res.headers, res.status
            if (res.status != "200") {
                let e = new Error("Error " + res.status + ":" + res.body); // e.message
                throw (e);
            }

            if (needresp && !res.body.Response && !res.body.response) { // one of the two should be in the answer
                console.error(res.body);
                let e = new Error("Query error:" + res.body); // e.message
                throw (e);
            }

            if (res.body.response && res.body.response.type == "ApplicationError") {
                console.error(res.body);
                let e = new Error("Error" + res.body.response.details); // e.message
                throw (e);
            }

            return res;
        });
}

async function hereRest2(url, getpara, postpara = null, needresp = "true") {
    let p;

    if (postpara == null) {
        p = request.get(url).query(getpara);
        //    console.log("mode GET");
    } else {
        p = request.post(url).query(getpara).type("form").send(postpara);
        //      console.log("mode POST");
    }
    //  console.log("p",p);

    return p
        .then(res => {
            // res.body, res.headers, res.status
            if (res.status != "200") {
                let e = new Error("Error " + res.status + ":" + res.body); // e.message
                throw (e);
            }

            if (needresp && !res.body.Response && !res.body.response) { // one of the two should be in the answer
                console.error(res.body);
                let e = new Error("Query error:" + res.body); // e.message
                throw (e);
            }

            if (res.body.response && res.body.response.type == "ApplicationError") {
                console.error(res.body);
                let e = new Error("Error" + res.body.response.details); // e.message
                throw (e);
            }

            return res;
        });
}

/**
 * get Throttle limts for various modules
 * @alias hm:getThrottleLimits
 * @return {string} object with various limitsl
 * 
 */
function getThrottleLimits() {
    return _throttleList;
}

/*
 * crate a throttle for a specified mode
 */
function getThrottle(mode, rps, concurrent) {

    if (!mode) mode = "regular";

    if (_throttleList[mode]._throttle)
        return _throttleList[mode]._throttle;

    let settings = {
        active: true, // set false to pause queue
        ratePer: 1000, // number of ms in which `rate` requests may be sent
    };

    if (rps)
        settings.rate = rps;
    else
        settings.rate = _throttleList[mode].rps;
    if (concurrent)
        settings.concurrent = concurrent;
    else
        settings.concurrent = _throttleList[mode].concurrent;

    let t = new Throttle(settings);
    _throttleList[mode]._throttle = t;
    return t;
}



/*
getpara, postpara=null, needresp = "true"
 */
async function hereRestThrottle(url, getpara, postpara, opt) {
    let p;

    let settings = {
        rpsMode: "regular",
        needBodyResponse: false //require res.body.response or res.body.Response
    };

    Object.assign(settings, opt);
    let throttle = getThrottle(settings.rpsMode, settings.rps, settings.concurrent);

    if (!postpara) 
        p = request.get(url).query(getpara).use(throttle.plugin());
    else 
        p = request.post(url).query(getpara).type("form").use(throttle.plugin()).send(postpara);

    return p
        .then(res => {
            // res.body, res.headers, res.status
            if (res.status != "200") {
                let e = new Error("Error " + res.status + ":" + res.body); // e.message
                throw (e);
            }

            if (settings.needBodyResponse && !res.body.Response && !res.body.response) { // one of the two should be in the answer
                console.error(res.body);
                let e = new Error("Query error:" + res.body); // e.message
                throw (e);
            }

            if (res.body.response && res.body.response.type == "ApplicationError") {
                console.error(res.body);
                let e = new Error("Error" + res.body.response.details); // e.message
                throw (e);
            }

            return res;
        });
}


module.exports = {
    config: config,
    buildUrl: buildUrl,
    getAppId: getAppId,
    getAppCode: getAppCode,
    getApiKey: getApiKey,
    getCIT: getCIT,
    getProtocol: getProtocol,
    getHome: getHome,
    useHTTPS: useHTTPS,
    addCredentials: addCredentials,
    hereRest: hereRest,
    hereRest2: hereRest2,
    hereRestThrottle: hereRestThrottle,
    getThrottle: getThrottle,
    getThrottleLimits: getThrottleLimits,
    superagent : request                    // to do superagent request
};