/**
 * 
 * @param {JSON} obj JSON object on which to replace a specific key. 
 * @param {String} oldKey is the old key in the JSON to be renamed to newKey
 * @param {String} newKey is the key that should replace the oldKey
 * usage: myjson.forEach((obj) => renameKey(obj, "oldkey", "newkey"));
 */

 function renameKey(obj, oldKey, newKey) {
    obj[newKey] = obj[oldKey];
    delete obj[oldKey];
}


/**
 * 
 * @param {Array} arr is the Array to be converted into a JSON
 * @returns {JSON} stringToJsonObject 
 */
function convertArray2JSON(arr) {
    var arrayToString = JSON.stringify(Object.assign({}, arr)); // convert array to string
    var stringToJsonObject = JSON.parse(arrayToString); // convert string to json object
    return stringToJsonObject;
}


/**
 * Format the response from SensorThings API to make it suitable for heatmap
 * @param {Array} obsArray Response from SensorThings API as array
 * @param {Int8} hours Number of hours to aggregate over. If hours=0, 
 * it will be aggregated by date (e.g. all values recorded on 1st of May etc...) 
 * if hours to any number (also hours=24) data will be aggregated over every 24hours, 
 * even if the date changes (e.g. data from 1st of May from 10pm up 9pm on 2nd of May etc.)
 * @param {String} method Specify how to aggregate date. Use: 'mean' = default, 'sum', 'min' or 'max'
 * @returns {Array} Aggregated Response
 */

export const aggregateResponse = function(obsArray, hours, method) {
    if (!obsArray) return;
    if (hours < 0) return;

    // check if we have a defined method or the method specified is accepted, rest is handeled in switch/case below
    if (method == undefined) method = 'mean';


    // convert obsArray to json

    let jsonFromArr = [];
    for (var i = 0; i < obsArray.length; i++) {
        jsonFromArr.push(convertArray2JSON(obsArray[i]));
    }

    // rename the keys in the jason
    jsonFromArr.forEach((obj) => renameKey(obj, "0", "datetime"));
    let jsonData = jsonFromArr;
    jsonData.forEach((obj) => renameKey(obj, "1", "temperature"));


    let newOutput = [];
    var aggDates = [];
    var aggregatedVals = [];
    var vals = []; // store values temporarily to use for processing

    if (hours == 0) { // i.e. aggregate over one Date / Day

        var currentDate;
        var oldDate;
        for (var d = 0; d < jsonData.length; d++) {
            let tmpDate = new Date(jsonData[d].datetime);
            currentDate = tmpDate.getDate(); // gets the day of the month 1...31
            if (d === 0) oldDate = currentDate;

            if (currentDate == oldDate) {
                vals.push(jsonData[d].temperature);
            } else {
                aggDates.push(new Date(tmpDate - 1));

                if (vals.length == 0) {
                    aggregatedVals.push(-1);
                    oldDate = currentDate;
                    continue;
                }

                switch (method) {
                    case 'mean':
                        aggregatedVals.push(vals.reduce(function(a, b) { return a + b / vals.length; }, -1));
                        break;
                    case 'sum':
                        aggregatedVals.push(vals.reduce(function(a, b) { return a + b; }, -1));
                        break;
                    case 'min':
                        aggregatedVals.push(vals.reduce(function(a, b) { return Math.min(a, b); }));
                        break;
                    case 'max':
                        aggregatedVals.push(vals.reduce(function(a, b) { return Math.max(a, b); }));
                        break;
                    default:
                        aggregatedVals.push(vals.reduce(function(a, b) { return a + b / vals.length; },-1));
                }
                vals = []; // clear the daily value vector 
                vals.push(jsonData[d].temperature); // now push first entry of new day into my temp value vector.
            }
            oldDate = currentDate;
        } // end of for loop

        // create output to be in the same List format as the original data from obsArray.

        for (let i = 0; i < aggregatedVals.length; i++) {
            newOutput.push([aggDates[i].toISOString(), aggregatedVals[i]]);
        }

    } else { // i.e. aggregate over X hours, irrespective of the day.
        let cnt = 0;
        let cumHours = 0;

        for (var d = 0; d < jsonData.length; d++) {
            if (cnt < hours) {
                vals.push(jsonData[d].temperature);
                cnt++;
            } else {
                cumHours += cnt;
                cnt = 0;
                aggDates.push(cumHours);

                if (vals.length == 0) {
                    aggregatedVals.push(-1);
                    continue;
                }

                switch (method) {
                    case 'mean':
                        aggregatedVals.push(vals.reduce(function(a, b) { return a + b / vals.length; }, -1));
                        break;
                    case 'sum':
                        aggregatedVals.push(vals.reduce(function(a, b) { return a + b; }, -1));
                        break;
                    case 'min':
                        aggregatedVals.push(vals.reduce(function(a, b) { return Math.min(a, b); }, ));
                        break;
                    case 'max':
                        aggregatedVals.push(vals.reduce(function(a, b) { return Math.max(a, b); }, ));
                        break;
                    default:
                        aggregatedVals.push(vals.reduce(function(a, b) { return a + b / vals.length; },-1));
                }
                vals = []; // clear the daily value vector 
                vals.push(jsonData[d].temperature); // now push first entry of new day into my temp value vector.
                cnt++;
            }
            oldDate = currentDate;
        } // end of for loop
        // create output to be in the same List format as the original data from obsArray.

        for (let i = 0; i < aggregatedVals.length; i++) {
            newOutput.push([aggDates[i], aggregatedVals[i]]);
        }
    } // end else





    return newOutput;
}