/*
 * Utility functions for Javascript Applications
 *   + Global Inits                  (securityCountdown, expandCharts, ...)
 *   + Init Charts                   (initQuickSearch, initExpandableCharts, initTableSorter, ...)
 *   ++ Init QuickSearch             (initQuickSearch)
 *   ++ Init TableSorter             (loadTableSorter, sortExtractData, sortExtractAttr, ...)
 *   ++ Basic Javascript Extensions  (indexOf, trim, startsWith, ucFirst, insertCommas, minToStr, ...)
 *   + AJAX Callbacks                (saveUserSetting, ...)
 *   + Highchart Callbacks           (disableClick, formatValueDate, formatValueComma, ...)
 *   + Highchart Series Utilities    (sumDatasetValues, totalsByCategory, getCategories, lastValue, ...)
 *   + Highchart Utilities           (setProperty, colorDefaults, ...)
 *   + Highchart Defaults            (lineChartDefaults, areaChartDefaults, pieChartDefaults, ...)
 *   + Dashboard Chart Object        (Chart)
 *   + Dashboard Chart Actions       (renderChart, clickSwitch, clickExpand, ...)
 */

var SIZES = [
    ['byte',     'B',  1],
    ['kilobyte', 'KB', 1000],
    ['megabyte', 'MB', 1000000],
    ['gigabyte', 'GB', 1000000000],
    ['terabyte', 'TB', 1000000000000],
    ['petabyte', 'PB', 1000000000000000]
];

var LOGCOUNT = { 1:'10',   2:'100',
    3:'1K', 4:'10K',  5:'100K',
    6:'1M', 7:'10M',  8:'100M',
    9:'1B', 10:'10B', 11:'100B'
};


//------------------------------------------------------------------
//  Global Inits (!!! Runs on Dashboard and Nasuni.com !!!)
//------------------------------------------------------------------
$(function() {
    initQuickSearch($('#dashboard #quicksearch'));          /* Dashboard Quicksearch */
    initExpandableCharts($('#dashboard div.expandable'));   /* Dashboard Expandable Charts */                    
    initTableSorter($("#dashboard table.sorted"));          /* Dashboard Tablesorter */
});


//------------------------------------------------------------------
//  Init Charts 
//------------------------------------------------------------------
function initQuickSearch(target) {
    if (target.length) {
        var options = {}
        options['parse']        = function(data) { return eval(data); };
        options['formatItem']   = function(item) { return item.value; };
        options['highlight']    = function(value) { return value; };
        options['width']        = target.width() + 24;
        options['matchSubset']  = false;
        options['scrollHeight'] = 330;
        options['delay']        = 250;
        options['minChars']     = 3;
        target.autocomplete("/dashboard/json/quickSearch/", options)
        target.result(function(event, item) { location.href = item.url; });
    }
}

//------------------------------------------------------------------
//  Init Charts 
//------------------------------------------------------------------
function initExpandableCharts(target) {
    if (target.length) {
        target.mouseenter(function() { $(this).find('.expand').fadeIn('fast'); });
        target.mouseleave(function() { $(this).find('.expand').fadeOut('fast'); });
        $('#bigchart div.close').click(function() {
            $("#bigchart").fadeOut('fast', function() {
                $("#bigchart_background").hide();
            });
        });
    }
}

//------------------------------------------------------------------
//  Init TableSorter
//------------------------------------------------------------------
function initTableSorter(target) {
    if (target.length) {
        // Create Table Sorter Objects
        $.tablesorter.defaults.sortList = [[0,0]];
        $.tablesorter.defaults.widgets = ['zebra'];
        $.tablesorter.defaults.widthFixed = true;
        // Loop through all sorted tables
        target.each(function() {
            var numRows = $(this).find('tbody tr').size();
            if (numRows > 0) {
                // Load the Table Sorter
                $(this).tablesorter({textExtraction: sortExtractAttr});
            } else {
                // No data in the table
                var parent = $(this).parent();
                var message = $("<div class='empty'>No data to display.</div>");
                var line_height = parent.height() - $(this).find('thead').height() - 10;
                message.css('line-height', line_height +"px");
                message.css('text-align', "center");
                message.css('font-style', "italic");
                message.css('font-size',  "11px");
                parent.append(message);
            }
        });
    }
}

function sortExtractData(node) {
    var jnode = $(node);
    if (jnode.data('sort') != null)
        return jnode.data('sort').toString();
    return jnode.text();
}

function sortExtractAttr(node) {
    var jnode = $(node);
    if (jnode.attr('sort') != null)
        return jnode.attr('sort');
    return jnode.text();
}


//------------------------------------------------------------------
//  Basic Javascript Extensions
//------------------------------------------------------------------
Array.prototype.indexOf     = function(i) { for (j in this) { if (this[j] == i) { return j; }} return -1; }
String.prototype.trim       = function()  { return (this.replace(/^[\s\xA0]+/, "").replace(/[\s\xA0]+$/, "")); }
String.prototype.startsWith = function(s) { return (this.match("^"+s)==s); }
String.prototype.endsWith   = function(s) { return (this.match(s+"$")==s); }

function ucFirst(inStr, eachWord) {
    newStr = '';
    if (eachWord == null) { eachWord = false; }
    if (!eachWord) {
        newStr += inStr.substring(0,1).toUpperCase() +
            inStr.substring(1,inStr.length);
    } else {
        inStr = inStr.split(' ');
        for(var i=0; i<inStr.length; i++)
            newStr += inStr[i].substring(0,1).toUpperCase() +
                inStr[i].substring(1,val[i].length) + ' ';
    }
    return newStr;
}

function updateUrl(url, variable, value) {
    if (url.indexOf("?") >= 0)
        return url +"&"+ variable +"="+ value;
    return url +"?"+ variable +"="+ value;
}

/*--- Numeric Functions ---*/
function round(num, places) {
    var power = Math.pow(10, places);
    return Math.round(num * power) / power;
}

function insertCommas(nStr) {
    nStr += '';
    x = nStr.split('.');
    x1 = x[0];
    x2 = x.length > 1 ? '.' + x[1] : '';
    var rgx = /(\d+)(\d{3})/;
    while (rgx.test(x1))
        x1 = x1.replace(rgx, '$1' + ',' + '$2');
    return x1 + x2;
}

/*--- Conversion Functions ---*/
function numToStr(num, places) {
    if (places == undefined) { places = 0; }
    if (num >= 1000000000) { return round(num/1000000000, places) + "B";}
    if (num >= 1000000)    { return round(num/1000000, places) + "M";}
    if (num >= 1000)       { return round(num/1000, places) + "K";}
    return num;
}

function minToStr(min) {
    var parts = 0;
    var rtnval = "";
    if (min > 1440) {  // Days
        parts += 1;
        days = parseInt(min / 1440);
        min -= days * 1440;
        rtnval += days+"d";
    }
    if (min > 60) {  // Hours
        parts += 1;
        hours = parseInt(min / 60);
        min -= hours * 60;
        if (parts >= 1) { rtnval += " "; }
        rtnval += hours+"h";
    }
    if (parts >= 2) { return rtnval; }
    if (parts >= 1) { rtnval += " "; }
    rtnval += min+"m";
    return rtnval;
}

function bytesToStr(bytes, places) {
    if (places == null) { places = 1; }
    if (bytes == 0) { return "0.0 B";}
    var rtnstr = "";
    for (var i=SIZES.length-1; i>=0; i--) {
        unit = SIZES[i][1];
        size = SIZES[i][2];
        if (size <= bytes) {
            rtnstr = round(bytes / size, places) +" "+ unit;
            break;
        }
    }
    return rtnstr;
}

function kilobytesToStr(kb, places, bytes1024) {
    byteSize = 1000;
    if (bytes1024 == true) { byteSize = 1024; }
    return bytesToStr(kb * byteSize, places);
}

/*--- Misc Utilities ---*/
function website() {
    if (window.location.pathname.indexOf('dashboard') >= 0)
        return "dashboard";
    return "nasuni";
}


//------------------------------------------------------------------
//  AJAX Callbacks
//------------------------------------------------------------------
function saveUserSetting(setting, value) {
    var settingUrl = "/usersettings/update?setting="+setting+"&value="+value;
    $.getJSON(settingUrl);
}


//------------------------------------------------------------------
//  Highchart Callbacks
//------------------------------------------------------------------
function disableClick()       { return false; }
function formatValueDate()    { return Highcharts.dateFormat('%b %e', this.value); }
function formatValueBytes()   { return bytesToStr(this.value); }
function formatValueComma()   { return insertCommas(this.value); }
function formatValueNum()     { return numToStr(this.value); }
function formatValuePercent() { return this.value+"%"; }

function formatBasicTooltip() {
    var html = "<strong>"+ insertCommas(this.y) +" "+ this.series.name +"</strong>";
    html += "<div class='subText'>"+ Highcharts.dateFormat('%B %e, %Y', this.x) +"</div>";
    return html;
}

//------------------------------------------------------------------
//  Highchart Series Utilities
//------------------------------------------------------------------
function sumDatasetValues(dataSet) {
    var total = 0;
    for (i in dataSet)
        if (i < dataSet.length)
            total += dataSet[i][1];
        else if (!dataSet.length)
            total += dataSet[i];
    return total;
}

function totalsByCategory(series) {
    var totals = {};
    for (i=0; i<series[0].data.length; i++) {
        var category = series[0].data[i][0];
        var total = 0;
        for (j=0; j<series.length; j++)
            total += series[j].data[i][1];
        setProperty(totals, category, total);
    }
    return totals;
}

function getCategories(series) {
    var categories = [];
    for (var i in series[0].data)
        categories.push(series[0].data[i][0]);
    return categories;
}

function lastValue(series, name) {
    for (var i=0; i<series.length; i++) {
        var dataset = series[i];
        if (dataset.name == name)
            return dataset.data[dataset.data.length-1][1];
    }
    return "NA";
}

function maxy(series) {
    var maxVal = -999999;
    for (i=0; i<series.length; i++) {
        for (j=0; j<series[i].data.length; j++) {
            if (series[i].data[j].length)
                maxVal = Math.max(maxVal, series[i].data[j][1]);
            else if (!series[i].data[j].length)
                maxVal = Math.max(maxVal, series[i].data[j]['y']);
        }
    }
    return maxVal;
}

function columnTickInterval(series, renderTo, options) {
    // Return a better tick interval column charts allowing
    // room to display all the data Label values.
    // Pull afew options
    var margin = options.chart.margin;
    var font = options.plotOptions.column.dataLabels.style.font;
    // Find the New MaxY
    var max = maxy(series);
    var height = $('#'+renderTo).height() - margin[0] - margin[2];
    var fontHeight = parseInt(font.split(" ")[0].replace("px", "")) + 8;
    var amtPerPixel = max / (height * 1.0);
    var amtPadding = amtPerPixel * fontHeight;
    var newMax = parseInt(max + amtPadding);
    return betterTickInterval(newMax);
}


function betterTickInterval(max) {
    // Multiply to avoid max value landing on last tick.
    var tmp = max * 1.05;
    var zeros = 0;
    while (tmp >= 100) {
            tmp = tmp / 10;
            zeros += 1;
    }
    tickInterval = Math.ceil(tmp / 2.0);
    tickInterval = tickInterval * Math.pow(10, zeros);
    return tickInterval == 0 ? 1 : tickInterval;
}


//------------------------------------------------------------------
//  Highchart Utilities
//------------------------------------------------------------------
function setProperty(object, property, value) {
    var parts = property.split('.');
    var current = parts.shift();
    var pointer = object;
    // Recursive Call
    while (parts.length > 0) {
        if (pointer[current] === undefined)
            pointer[current] = {}
        pointer = pointer[current];
        current = parts.shift();
    }
    // Hack in Specific Margin
    switch(current) {
        case 'margin-top':    pointer.margin[0] = value; break;
        case 'margin-right':  pointer.margin[1] = value; break;
        case 'margin-bottom': pointer.margin[2] = value; break;
        case 'margin-left':   pointer.margin[3] = value; break;
        default:              pointer[current] = value; break;
    }
}

function getProperty(object, property) {
    var parts = property.split('.');
    var current = parts.shift();
    if (object[current] != undefined) {
        if (parts.length >= 1)
            return getProperty(object[current], parts.join('.'));
        return object[current];
    }
    return undefined;
}

function mergeObjects(object1, object2) {
    for (var i in object2) {
        try {
            if (object2[i].constructor == Object) {
                object1[i] = mergeObjects(object1[i], object2[i]);
            } else {
                object1[i] = object2[i];
            }
        } catch(e) {
            object1[i] = object2[i];
        }
    }
    return object1;
}

function colorDefaults(rotate, reverse) {
    if (rotate == undefined)  { rotate = 0; }
    if (reverse == undefined) { reverse = false; }
    var colors = [
        '#4581D9',  // Blue
        '#B51F26',  // Red
        '#749523',  // Green
        '#E27418',  // Orange
        '#80699B',  // Purple
        '#5F3527'   // Brown
    ];
    for (var i=0; i<rotate; i++)
        colors.push(colors.shift());
    if (reverse)
        colors.reverse();
    return colors;
}

function switchValue(renderTo) {
    var selector = "#"+renderTo+" div.switcher div.switch.on";
    return $(selector).attr('value');
}


//------------------------------------------------------------------
//  Highchart Defaults
//------------------------------------------------------------------
function lineChartDefaults(chartID) {
    var options = {};
    setProperty(options, 'title.text',                      '');
    setProperty(options, 'chart.renderTo',                  chartID);
    setProperty(options, 'chart.defaultSeriesType',         'line');
    setProperty(options, 'chart.margin',                    [30, 15, 20, 30]);
    setProperty(options, 'colors',                          colorDefaults());
    setProperty(options, 'credits.enabled',                 false);
    setProperty(options, 'legend.layout',                   'horizontal');
    setProperty(options, 'legend.style.top',                '5px');
    setProperty(options, 'legend.style.right',              'auto');
    setProperty(options, 'legend.style.bottom',             'auto');
    setProperty(options, 'legend.style.left',               '25px');
    setProperty(options, 'legend.itemStyle.font',           '10px Arial');
    setProperty(options, 'xAxis.type',                      'datetime');
    setProperty(options, 'xAxis.dateTimeLabelFormats.day',  '%B %e');
    setProperty(options, 'xAxis.alternateGridColor',        '#eee');
    setProperty(options, 'xAxis.tickInterval',              'auto');
    setProperty(options, 'xAxis.labels.style.font',         '10px Arial');
    setProperty(options, 'xAxis.labels.align',              'left');
    setProperty(options, 'xAxis.labels.x',                  4);
    setProperty(options, 'xAxis.labels.y',                  11);
    setProperty(options, 'xAxis.labels.formatter',          formatValueDate);
    setProperty(options, 'xAxis.title.enabled',             false);
    setProperty(options, 'yAxis.title.enabled',             false);
    setProperty(options, 'yAxis.labels.style.font',         '8px Arial');
    setProperty(options, 'yAxis.labels.formatter',          formatValueComma);
    setProperty(options, 'yAxis.title.enabled',             false);
    setProperty(options, 'yAxis.showFirstLabel',            true);
    setProperty(options, 'yAxis.min',                       0);
    // Plot Options
    setProperty(options, 'plotOptions.line.marker.radius',        2);
    setProperty(options, 'plotOptions.area.marker.radius',        1);
    setProperty(options, 'plotOptions.spline.marker.radius',      2);
    setProperty(options, 'plotOptions.areaspline.marker.radius',  2);
    setProperty(options, 'plotOptions.line.lineWidth',            2);
    setProperty(options, 'plotOptions.area.lineWidth',            1);
    setProperty(options, 'plotOptions.spline.lineWidth',          1);
    setProperty(options, 'plotOptions.areaspline.lineWidth',      1);
    setProperty(options, 'plotOptions.area.fillOpacity',          0.3);
    setProperty(options, 'plotOptions.spline.fillOpacity',        0.3);
    setProperty(options, 'plotOptions.areaspline.fillOpacity',    0.3);
    setProperty(options, 'plotOptions.line.marker.symbol',        'circle');
    setProperty(options, 'plotOptions.area.marker.symbol',        'circle');
    setProperty(options, 'plotOptions.spline.marker.symbol',      'circle');
    setProperty(options, 'plotOptions.areaspline.marker.symbol',  'circle');
    return options;
}

function areaChartDefaults(chartID) {
    var options = lineChartDefaults(chartID);
    setProperty(options, 'chart.defaultSeriesType', 'area');
    return options;
}

function pieChartDefaults(chartID) {
    var options = {};
    setProperty(options, 'title.text',                             '');
    setProperty(options, 'chart.renderTo',                         chartID);
    setProperty(options, 'chart.defaultSeriesType',                'pie');
    setProperty(options, 'chart.margin',                           [0, 0, 0, 0]);
    setProperty(options, 'colors',                                 colorDefaults());
    setProperty(options, 'credits.enabled',                        false);
    setProperty(options, 'plotOptions.pie.center',                 ['27%', '50%']);
    setProperty(options, 'plotOptions.pie.states.hover.enabled',   false);
    setProperty(options, 'plotOptions.pie.dataLabels.enabled',     true);
    setProperty(options, 'plotOptions.pie.dataLabels.color',       '#333');
    setProperty(options, 'plotOptions.pie.dataLabels.style.font',  '10px Arial');
    setProperty(options, 'plotOptions.pie.dataLabels.x',           0);
    setProperty(options, 'plotOptions.pie.dataLabels.y',           0);
    setProperty(options, 'legend.layout',                          'veritcal');
    setProperty(options, 'legend.style.left',                      '185px');
    setProperty(options, 'legend.style.top',                       '5px');
    return options
}

function columnChartDefaults(chartID) {
    var options = {};
    setProperty(options, 'title.text',               '');
    setProperty(options, 'chart.renderTo',           chartID);
    setProperty(options, 'chart.defaultSeriesType',  'column');
    setProperty(options, 'chart.margin',             [30, 15, 20, 30]);
    setProperty(options, 'colors',                   colorDefaults());
    setProperty(options, 'credits.enabled',          false);
    setProperty(options, 'legend.layout',            'horizontal');
    setProperty(options, 'legend.style.top',         '5px');
    setProperty(options, 'legend.style.right',       'auto');
    setProperty(options, 'legend.style.bottom',      'auto');
    setProperty(options, 'legend.style.left',        '20px');
    setProperty(options, 'legend.itemStyle.font',    '11px Arial');
    setProperty(options, 'xAxis.labels.style.font',  '8px Arial');
    setProperty(options, 'xAxis.title.enabled',      false);
    setProperty(options, 'yAxis.labels.style.font',  '8px Arial');
    setProperty(options, 'yAxis.title.enabled',      false);
    setProperty(options, 'yAxis.showFirstLabel',     true);
    setProperty(options, 'yAxis.min',                0);
    setProperty(options, 'plotOptions.column.dataLabels.color',      'auto');
    setProperty(options, 'plotOptions.column.dataLabels.style.font', '10px Arial');
    return options
}

function bigChartExtraOptions(chart) {
    var extraOptions = {}
    setProperty(extraOptions, 'chart.margin',                    [50, 30, 20, 50]);
    setProperty(extraOptions, 'legend.style.left',               '45px');
    setProperty(extraOptions, 'legend.style.width',              '640px');
    setProperty(extraOptions, 'legend.itemStyle.font',           '13px Arial');
    setProperty(extraOptions, 'xAxis.labels.style.font',         '10px Arial');
    setProperty(extraOptions, 'yAxis.labels.style.font',         '10px Arial');
    setProperty(extraOptions, 'plotOptions.area.marker.radius',  2);
    setProperty(extraOptions, 'plotOptions.line.marker.radius',  2);
    if (chart.type == 'pie') {
        setProperty(extraOptions, 'chart.margin',                [0,0,0,0]);
        setProperty(extraOptions, 'legend.style.top',            '50px');
        setProperty(extraOptions, 'legend.style.left',           '550px');
    }
    return extraOptions;
}


//------------------------------------------------------------------
//  Dashboard Chart Object
//------------------------------------------------------------------
function Chart(baseUrl, options, swKeys, type) {
    if (swKeys == undefined) { swKeys = []; }
    if (type == undefined)   { type = 'area'; }
    this.baseUrl = baseUrl;    // URL to retrieve Series data
    this.options = options;    // Options Callback
    this.swKeys  = swKeys;     // Array of SwitchKeys
    this.type    = type;       // Chart Type
}

//------------------------------------------------------------------
//  Dashboard Chart Actions
//------------------------------------------------------------------
function renderChart(chart, renderTo, swSave, exOptions) {
    if (swSave == undefined)    { swSave == false; }
    if (exOptions == undefined) { exOptions == {}; }
    var chartDiv = $("#"+renderTo+"_chart");
    showLoadingIndicator(chartDiv);
    // Update the Series Callback URL
    var dataUrl = chart.baseUrl;
    if (swSave) { dataUrl = updateUrl(dataUrl, 'swSave',  'true'); }
    for (var i=0; i<chart.swKeys.length; i++) {
        var swKey = chart.swKeys[i];
        var swValue = switchValue(renderTo);
        dataUrl = updateUrl(dataUrl, swKey, swValue);
    }
    // Fetch the chart Data & Options
    $.getJSON(dataUrl, {}, function(response) {
        if (response.redirect != undefined) { return checkResponseRedirect(response, chartDiv); }
        if (response.error != undefined) { return checkResponseError(response, chartDiv); }
        // Update the Chart Options
        var options = chart.options(response, renderTo);
        options = mergeObjects(options, exOptions);
        if (options.series == undefined) { setProperty(options, 'series', response.series); }
        setProperty(options, 'chart.renderTo', renderTo+"_chart");
        // Render the Chart (HighCharts)
        hideLoadingIndicator(chartDiv);
        new Highcharts.Chart(options);
        // Check we want to add some post render CSS
        if (getProperty(options, 'plotOptions.pie.events.click') != undefined)
            chartDiv.find('.highcharts-image-map area[shape=poly]').css('cursor', 'pointer');
        return true;
    });
}

function clickSwitch(jqSwitch, callback) {
    var enabled = jqSwitch.hasClass('on');
    if (!enabled) {
        var switcher = jqSwitch.parents('.switcher');
        switcher.find('.switch').removeClass('on');
        jqSwitch.addClass('on');
        callback(jqSwitch);
    }
}

function clickChartSwitch(jqSwitch, chart, renderTo, exOptions) {
    var callback = function() { renderChart(chart, renderTo, true) }
    clickSwitch(jqSwitch, callback);
}

function clickChartExpand(jqExpand, chart) {
    var container = jqExpand.parents('.chartWrap');
    var exOptions = bigChartExtraOptions(chart);
    var renderTo = "bigchart";
    // Copy the Title and Switches into the Dialog
    $('#bigchart .title').text(container.find('.title').text());
    $('#bigchart .switcher').html(container.find('.switcher').html());
    $('#bigchart .switch').click(function() {
        clickChartSwitch($(this), chart, renderTo, exOptions);
    });
    // Render the Big Chart
    $('#bigchart_chart').html("");
    $('#bigchart_chart').addClass("loading");
    $("#bigchart_background").show();
    $("#bigchart").fadeIn('slow', function() {
        renderChart(chart, renderTo, false, exOptions);
    });
}

/*--- Show and hide the loading indicator ---*/
function showLoadingIndicator(chartDiv) {
    chartDiv.find('.highcharts-container').fadeOut('fast');
    chartDiv.addClass('loading');
}

function hideLoadingIndicator(chartDiv) {
    chartDiv.stop().removeClass('loading');
}

/*--- Check response for Redirect---*/
function checkResponseRedirect(response, chartDiv) {
    if (response.redirect != undefined) {
        window.top.location = response.redirect;
        return false;
    }
    return true;
}

/*--- Check response for Error ---*/
function checkResponseError(response, chartDiv) {
    if (response.error != undefined) {
        hideLoadingIndicator(chartDiv);
        var errorDiv = $("<div class='error'>"+ response['error'] +"</div>");
        chartDiv.append(errorDiv);
        return false;
    }
    return true;
}

