var basal = require('../profile/basal');
var get_iob = require('../iob');
var find_insulin = require('../iob/history');
var isf = require('../profile/isf');

function detectCarbAbsorption(inputs) {

    var glucose_data = inputs.glucose_data.map(function prepGlucose (obj) {
        //Support the NS sgv field to avoid having to convert in a custom way
        obj.glucose = obj.glucose || obj.sgv;
        return obj;
    });
    var iob_inputs = inputs.iob_inputs;
    var basalprofile = inputs.basalprofile;
    /* TODO why does declaring profile break tests-command-behavior.tests.sh? */
    profile = inputs.iob_inputs.profile;
    var mealTime = new Date(inputs.mealTime);
    var ciTime = new Date(inputs.ciTime);

    //console.error(mealTime, ciTime);

    // get treatments from pumphistory once, not every time we get_iob()
    var treatments = find_insulin(inputs.iob_inputs);

    var avgDeltas = [];
    var bgis = [];
    var deviations = [];
    var deviationSum = 0;
    var carbsAbsorbed = 0;
    var bucketed_data = [];
    bucketed_data[0] = glucose_data[0];
    var j=0;
    var foundPreMealBG = false;
    var lastbgi = 0;

    if (! glucose_data[0].glucose || glucose_data[0].glucose < 39) {
      lastbgi = -1;
    }

    for (var i=1; i < glucose_data.length; ++i) {
        var bgTime;
        var lastbgTime;
        if (glucose_data[i].display_time) {
            bgTime = new Date(glucose_data[i].display_time.replace('T', ' '));
        } else if (glucose_data[i].dateString) {
            bgTime = new Date(glucose_data[i].dateString);
        } else { console.error("Could not determine BG time"); }
        if (! glucose_data[i].glucose || glucose_data[i].glucose < 39) {
//console.error("skipping:",glucose_data[i].glucose);
            continue;
        }
        // only consider BGs for 6h after a meal for calculating COB
        var hoursAfterMeal = (bgTime-mealTime)/(60*60*1000);
        if (hoursAfterMeal > profile.maxMealAbsorptionTime || foundPreMealBG) {
            continue;
        } else if (hoursAfterMeal < 0) {
//console.error("Found pre-meal BG:",glucose_data[i].glucose, bgTime, Math.round(hoursAfterMeal*100)/100);
            foundPreMealBG = true;
        }
//console.error(glucose_data[i].glucose, bgTime, Math.round(hoursAfterMeal*100)/100, bucketed_data[bucketed_data.length-1].display_time);
        // only consider last ~45m of data in CI mode
        // this allows us to calculate deviations for the last ~30m
        if (typeof ciTime !== 'undefined') {
            var hoursAgo = (ciTime-bgTime)/(45*60*1000);
            if (hoursAgo > 1 || hoursAgo < 0) {
                continue;
            }
        }
        if (bucketed_data[bucketed_data.length-1].display_time) {
            lastbgTime = new Date(bucketed_data[bucketed_data.length-1].display_time.replace('T', ' '));
        } else if ((lastbgi >= 0) && glucose_data[lastbgi].display_time) {
            lastbgTime = new Date(glucose_data[lastbgi].display_time.replace('T', ' '));
        } else if ((lastbgi >= 0) && glucose_data[lastbgi].dateString) {
            lastbgTime = new Date(glucose_data[lastbgi].dateString);
        } else { console.error("Could not determine last BG time"); }
        var elapsed_minutes = (bgTime - lastbgTime)/(60*1000);
    //console.error(bgTime, lastbgTime, elapsed_minutes);
        if(Math.abs(elapsed_minutes) > 8) {
            // interpolate missing data points
            var lastbg = glucose_data[lastbgi].glucose;
            // cap interpolation at a maximum of 4h
            elapsed_minutes = Math.min(240,Math.abs(elapsed_minutes));
            //console.error(elapsed_minutes);
            while(elapsed_minutes > 5) {
                var previousbgTime = new Date(lastbgTime.getTime() - 5 * 60*1000);
                j++;
                bucketed_data[j] = [];
                bucketed_data[j].date = previousbgTime.getTime();
                var gapDelta = glucose_data[i].glucose - lastbg;
                //console.error(gapDelta, lastbg, elapsed_minutes);
                var previousbg = lastbg + (5/elapsed_minutes * gapDelta);
                bucketed_data[j].glucose = Math.round(previousbg);
                //console.error("Interpolated", bucketed_data[j]);

                elapsed_minutes = elapsed_minutes - 5;
                lastbg = previousbg;
                lastbgTime = new Date(previousbgTime);
            }

        } else if(Math.abs(elapsed_minutes) > 2) {
            j++;
            bucketed_data[j]=glucose_data[i];
            bucketed_data[j].date = bgTime.getTime();
        } else {
            bucketed_data[j].glucose = (bucketed_data[j].glucose + glucose_data[i].glucose)/2;
        }

        lastbgi = i;
        //console.error(bucketed_data[j].date)
    }
    var currentDeviation;
    var slopeFromMaxDeviation = 0;
    var slopeFromMinDeviation = 999;
    var maxDeviation = 0;
    var minDeviation = 999;
    var allDeviations = [];
    //console.error(bucketed_data);
    for (i=0; i < bucketed_data.length-3; ++i) {
        bgTime = new Date(bucketed_data[i].date);

        var sens = isf.isfLookup(profile.isfProfile,bgTime);

        //console.error(bgTime , bucketed_data[i].glucose, bucketed_data[i].date);
        var bg;
        var avgDelta;
        var delta;
        if (typeof(bucketed_data[i].glucose) !== 'undefined') {
            bg = bucketed_data[i].glucose;
            if ( bg < 39 || bucketed_data[i+3].glucose < 39) {
                process.stderr.write("!");
                continue;
            }
            avgDelta = (bg - bucketed_data[i+3].glucose)/3;
            delta = (bg - bucketed_data[i+1].glucose);
        } else { console.error("Could not find glucose data"); }

        avgDelta = avgDelta.toFixed(2);
        iob_inputs.clock=bgTime;
        iob_inputs.profile.current_basal = basal.basalLookup(basalprofile, bgTime);
        //console.log(JSON.stringify(iob_inputs.profile));
        //console.error("Before: ", new Date().getTime());
        var iob = get_iob(iob_inputs, true, treatments)[0];
        //console.error("After: ", new Date().getTime());
        //console.error(JSON.stringify(iob));

        var bgi = Math.round(( -iob.activity * sens * 5 )*100)/100;
        bgi = bgi.toFixed(2);
        //console.error(delta);
        var deviation = delta-bgi;
        deviation = deviation.toFixed(2);
        //if (deviation < 0 && deviation > -2) { console.error("BG: "+bg+", avgDelta: "+avgDelta+", BGI: "+bgi+", deviation: "+deviation); }
        // calculate the deviation right now, for use in min_5m
        if (i===0) {
            currentDeviation = Math.round((avgDelta-bgi)*1000)/1000;
            if (ciTime > bgTime) {
                //console.error("currentDeviation:",currentDeviation,avgDelta,bgi);
                allDeviations.push(Math.round(currentDeviation));
            }
            if (currentDeviation/2 > profile.min_5m_carbimpact) {
                //console.error("currentDeviation",currentDeviation,"/2 > min_5m_carbimpact",profile.min_5m_carbimpact);
            }
        } else if (ciTime > bgTime) {
            var avgDeviation = Math.round((avgDelta-bgi)*1000)/1000;
            var deviationSlope = (avgDeviation-currentDeviation)/(bgTime-ciTime)*1000*60*5;
            //console.error(avgDeviation,currentDeviation,bgTime,ciTime)
            if (avgDeviation > maxDeviation) {
                slopeFromMaxDeviation = Math.min(0, deviationSlope);
                maxDeviation = avgDeviation;
            }
            if (avgDeviation < minDeviation) {
                slopeFromMinDeviation = Math.max(0, deviationSlope);
                minDeviation = avgDeviation;
            }

            //console.error("Deviations:",avgDeviation, avgDelta,bgi,bgTime);
            allDeviations.push(Math.round(avgDeviation));
            //console.error(allDeviations);
        }

        // if bgTime is more recent than mealTime
        if(bgTime > mealTime) {
            // figure out how many carbs that represents
            // if currentDeviation is > 2 * min_5m_carbimpact, assume currentDeviation/2 worth of carbs were absorbed
            // but always assume at least profile.min_5m_carbimpact (3mg/dL/5m by default) absorption
            var ci = Math.max(deviation, currentDeviation/2, profile.min_5m_carbimpact);
            var absorbed = ci * profile.carb_ratio / sens;
            // and add that to the running total carbsAbsorbed
            //console.error("carbsAbsorbed:",carbsAbsorbed,"absorbed:",absorbed,"bgTime:",bgTime,"BG:",bucketed_data[i].glucose)
            carbsAbsorbed += absorbed;
        }
    }
    if(maxDeviation>0) {
        //console.error("currentDeviation:",currentDeviation,"maxDeviation:",maxDeviation,"slopeFromMaxDeviation:",slopeFromMaxDeviation);
    }

    return {
        "carbsAbsorbed": carbsAbsorbed
    ,   "currentDeviation": currentDeviation
    ,   "maxDeviation": maxDeviation
    ,   "minDeviation": minDeviation
    ,   "slopeFromMaxDeviation": slopeFromMaxDeviation
    ,   "slopeFromMinDeviation": slopeFromMinDeviation
    ,   "allDeviations": allDeviations
    }
}
module.exports = detectCarbAbsorption;
