/************METEOGRAM FLOT************/


/**
* @author Javier Millan
*
* Draws the flot graph, including the point tags and the binding of events
*
* @param <meteo_data> Contains data_products[] + data_options + data_axis
* @param <flot_config>	GraphConfig single object, the type is flot.
*
*/
function drawFlot(meteo_data, flot_config){

        //data object
	var f_data = create_flot_data(meteo_data, flot_config);

        //options object
	var f_options = create_flot_options(flot_config, meteo_data.xaxis_data, meteo_data.options,f_data);
        //console.dir(f_options);

	//placeholder
	var placeholder = $("#"+flot_config.div);

	//flot drawing
	var flot = $.plot(placeholder,f_data,f_options);
	GLOBAL_PLOTS.push(flot);


	//draw point tags
	drawPointTags(placeholder, flot, meteo_data, flot_config);


        //draw units
        drawFlotUnits(placeholder,f_data,f_options);

	//events
	bindEvents(placeholder);
}



/**
* @author Javier Millan
* Creates the flot options object
*
* @param <fconfig> GraphConfig single object, the type is flot.
* @param <xaxis_ticks> Time ticks for x axis
* @param <time_options> Time options: start epoch, end epoch, cell time
*
* @return flot options object
*/
function create_flot_options(fconfig,xaxis_ticks,time_options,f_data){
	
	var options = new Object();
	
	//test the correction of axis
	if(fconfig.axis_options.xaxis == null || 
		(fconfig.axis_options.yaxis_left == null && fconfig.axis_options.yaxis_right == null)){
		//throw an exception
	}
		
	//xaxis	
	var xaxis = create_xaxis_options(fconfig.axis_options.xaxis,xaxis_ticks,time_options);
	options.xaxis = xaxis;


        var s = new Object();
        s.now = time_options.now;
        options.series = s;

        //stacked data
        if(typeof(fconfig.axis_options.xaxis.stack) != 'undefined'){

            if(fconfig.axis_options.xaxis.stack){
                options.series.stack = true;
            }
        }

	
	//yaxis
	var yaxis;
	if(fconfig.axis_options.yaxis_left != null){
	
		yaxis = create_yaxis_options(fconfig.axis_options.yaxis_left,f_data,1);
	}

       	//although there is no product on the y axis,
	//this should be created to make the right margin correctly
	else{			
		yaxis = new Object();
		yaxis.labelWidth = _AXIS_TICK_WIDTH;			
	}
	
	options.yaxis = yaxis;
		
	//y2axis
	var y2axis;
	if(fconfig.axis_options.yaxis_right != null){
	
		y2axis = create_yaxis_options(fconfig.axis_options.yaxis_right,f_data,2);
	}
	
	//although there is no product on the y2 axis,
	//this should be created to make the right margin correctly
	else{			
		y2axis = new Object();
		y2axis.labelWidth = _AXIS_TICK_WIDTH;			
	}
	
	options.y2axis = y2axis;
        		
	//grid
	var grid = create_grid_options(fconfig.grid_options);
	//time markings, including real/simulated line
	grid.markings = generate_time_markings(time_options.start_epoch,time_options.finish_epoch,time_options.cell_time,grid.color,fconfig.axis_options.xaxis.realsim);

	options.grid = grid;
		
	return options;
}


/**
* @author Javier Millan
*
* Creates an y-axis options object
*
* @param <yaxis_options> yaxis options object
* @param <place> ????
*
*/
function create_yaxis_options(yaxis_options,f_data,place){

	var yaxis = new Object();
		
	var tickDecimals = (typeof(yaxis_options.tickDecimals) != 'undefined') ? 
		yaxis_options.tickDecimals : _AXIS_TICKDECIMALS;

        if(typeof(yaxis_options.offset_unit) != 'undefined')
            yaxis.offset_unit = yaxis_options.offset_unit;
	
	var max = (typeof(yaxis_options.max) != 'undefined') ? 
		yaxis_options.max : _AXIS_MAX;
	
	
	var min = (typeof(yaxis_options.min) != 'undefined') ? 
		yaxis_options.min : _AXIS_MIN;

	yaxis.tickDecimals = tickDecimals;
	yaxis.max = max;
	yaxis.min = min;
	        
	yaxis.labelWidth =_AXIS_TICK_WIDTH;

         var mult = (yaxis_options.inverse) ? -1.0 : 1.0;
         yaxis.tickFormatter= function suffixFormatter(val, axis) {return (val *mult).toFixed(axis.tickDecimals);}


        if(yaxis_options.max_ticks && yaxis_options.nticks){
            minmax = getMinMax(f_data,mult,place);            

            yaxis.ticks = generateTicks(minmax,yaxis_options.max_ticks,yaxis_options.nticks,mult);
        }
	
	return yaxis;
}

/**
* @author Javier Millan
* Creates an x-axis options object
*
* @param <xaxis_options> 
* @param <xaxis_ticks>
* @param <time_options>
*
*/
function create_xaxis_options(xaxis_options,xaxis_ticks,time_options){

	var xaxis = new Object();
	xaxis.mode = "time";
	xaxis.timeFormat = xaxis_options.time_format;
	xaxis.min = time_options.start_epoch;
	xaxis.max = time_options.finish_epoch;
        var align_epoch;
	
	
	if(xaxis_ticks){
            //comprove if the ticks have to been showed
            if(xaxis_options.show_ticks){

                    if(time_options.period=='WEEKLY')
                        align_epoch = 43200000;

                    else
                        align_epoch = 0;


                    xaxis.ticks = xaxis_ticks.slice(0);
                    align_axis(xaxis.ticks,align_epoch);
            }

            else
                    xaxis.ticks = [];

        }

        xaxis.tickLength = 0;

	return xaxis;
}


/**
* @author Javier Millan
* Creates an grid options object
*
* @param <grid_options> 
*
*/
function create_grid_options(grid_options){

	var grid = new Object();
	
	var borderColor = (typeof(grid_options.borderColor) != 'undefined') ? 
		grid_options.borderColor : _GRID_BORDER_COLOR;

       	var tickColor = (typeof(grid_options.tickColor) != 'undefined') ?
		grid_options.tickColor : _GRID_TICK_COLOR;
            
        var borderWidth = (typeof(grid_options.borderWidth) != 'undefined') ? 
                grid_options.borderWidth : _GRID_BORDER_WIDTH;


	var color = (typeof(grid_options.color) != 'undefined') ? 
		grid_options.color : _GRID_COLOR;

	
	var interactive = (typeof(grid_options.interactive) != 'undefined') ? 
		grid_options.interactive : _GRID_INTERACTIVE;
		
	
	grid.borderColor = borderColor;
        grid.borderWidth = borderWidth;
        grid.tickColor = tickColor;
	grid.color = color;
	grid.hoverable = interactive;
	grid.autoHighlight = interactive;
	grid.clickable = interactive;
	
	return grid;
}


/**
* @author Javier Millan
* Creates the data options object
*
* @param <flot_data> Contains data_products[] + data_options + data_axis
* @param <fconfig>	GraphConfig single object
*
*/
function create_flot_data(flot_data, fconfig){

	var products = select_products(flot_data.product_data,fconfig.products);
	
	//make the offset
	var data = new Array();

	for (var i=0;i<products.length;i++){
            
		data.push(create_data_options(products[i],fconfig));
	}
	return data;
}


/**
* @author Javier Millan
* Select the series for the products of the graph
*
* @param <products> Array of products
* @param <config>
*
*/
function select_products(products,product_config){

	var selected_products = new Array();
	for(var i=0; i<product_config.length; i++){
	
		for(var j=0; j<products.length;j++){

			if((products[j].p_raw_id == product_config[i].product_ID) &&(products[j].p_hv == product_config[i].hv)){
				selected_products.push(products[j]);
			}
		}
	}
	return selected_products;
}


/**
* @author Javier Millan
* Creates the data object for the product
*
* @param <product> 
* @param <config>
*
*/
function create_data_options(product,config){
	
	//find the product config
	var index = find_product_config_index(product.p_raw_id,product.p_hv,config);
	
	if(index<0){	
		//throw an exception; product not found	
	}
		
	var p_config = config.products[index];
	
	//multiplicador
	var inv= (typeof(p_config.inverse) != 'undefined') ? 
		p_config.inverse : false;
			
	var mult = (inv) ? -1 : 1;
		
	//offset
	var offset = (typeof(p_config.epoch_align) != 'undefined') ? 
		p_config.epoch_align : _PRODUCT_EPOCH_ALIGN;

        var position_offset = (typeof(p_config.position_align) != 'undefined') ?
		p_config.position_align : 0;


        var accum = (typeof(p_config.accum) != 'undefined') ?
		p_config.accum : 0;
		

        //store data without alignment to manage events
        var raw_data = new Array();


        alignDataEpochApplyMultiplier(product.p_data,raw_data,offset,position_offset,mult,accum);
	
	
	//default values
		var highlightColor = (typeof(p_config.highlightColor) != 'undefined') ? 
		p_config.highlightColor : null;
				
		
	//time serie
	var data = new Object();
	data.data = product.p_data;
        data.raw_data = raw_data;
	data.name = product.p_name;
	data.hv = p_config.hv;
        data.unit= product.p_unit;

	//legend
        //comprove if is min or max to set it on legend
        var l_minmax;
        if(p_config.fgroup =='max' || p_config.fgroup =='min')
            l_minmax = p_config.fgroup;

        else
            l_minmax = '';

	if(p_config.legend)
		legends.push([product.p_name + " "+l_minmax, p_config.legend_color]);
		

	//id + tag mode for mousehover event
	data.ident = product.p_id;
        data.raw_ident = product.p_raw_id;
	
	if(p_config.pointTag !=null)
		data.tagmode = p_config.pointTag.type;
	
	else
		data.tagmode = "none";
	
	data.highlightColor = highlightColor;
        
	//axis
	switch(p_config.y_axis){
		
		case "yaxis_left":
			data.yaxis = 1;
			break;
			
		case "yaxis_right":
			data.yaxis = 2;
			break;		
	
		default:
			break;
	}
	
	data.hmode = p_config.hmode;
	
	var lineWidth,fill,fillColor;
	
	
        //bars
	if(p_config.bars !=null){

            if(p_config.bars.show){
	
		var bars = new Object();
		bars.show = true;

		//lineWidth
		lineWidth = (typeof(p_config.bars.lineWidth) != 'undefined') ? 
		p_config.bars.lineWidth : _PRODUCT_LINEWIDTH;
		
		bars.lineWidth = lineWidth;
	
		//fill
		fill = (typeof(p_config.bars.fill) != 'undefined') ? 
		p_config.bars.fill : _PRODUCT_FILL;
		
		bars.fill = fill;
		
		//fillcolor
		if(fill){
		
			var fillColor = new Object();
			var arr_fill = p_config.bars.fillColor;
			fillColor.colors = arr_fill;
			bars.fillColor = fillColor;
		}
			
		//bars width
		bars.barWidth = p_config.bars.barWidth;
		
		data.bars = bars;
            }
        }
		
	
	//lines
	if(p_config.lines !=null){
			
            if(p_config.lines.show){

                var lines = new Object();
		lines.show = true;

		//lineWidth
		lineWidth = (typeof(p_config.lines.lineWidth) != 'undefined') ? 
		p_config.lines.lineWidth : _PRODUCT_LINEWIDTH;
		
		lines.lineWidth = lineWidth;
	
		//fill
		fill = (typeof(p_config.lines.fill) != 'undefined') ? 
		p_config.lines.fill : _PRODUCT_FILL;
		
		lines.fill = fill;
                
		//fillcolor	
		if(fill)
                {
                    if(p_config.lines.fillColor.constructor.toString().indexOf("Array") != -1)
                        lines.fillColor = {colors:p_config.lines.fillColor};

                    else
                        lines.fillColor = p_config.lines.fillColor;
                }
			

		//line color
		var line_color = (typeof(p_config.color) != 'undefined') ? 
		p_config.color : null;
			
		data.color = line_color;
		
		data.lines = lines;
            }
        }
	
	
	//points
	if(p_config.points !=null){
	
		if(p_config.points.show){
		
			var points = new Object();
			points.show = true;
		
			//fill
			fill = (typeof(p_config.points.fill) != 'undefined') ? 
			p_config.points.fill : _PRODUCT_FILL;
			
			points.fill = fill;
			
			//fillcolor
			if(fill)
				points.fillColor = p_config.points.fillColor;
				
			data.points = points;
		}
	}	
	return data;	
}


/**
** @author Jordi Castells
*
* Align the epoch of a flot data array and use a multiplier.
* Array of pairs, where the 0 is a milliepoch and the 1 is a value
*
* Align and multiply is done in the same function to avoid multiple loops over the same data
*
* @param <data_array>  Flot data array
* @param <offset>      Offset to apply
* @param <multiplier>  A data multiplier. Usually used to inverse the data
*
*/
function alignDataEpochApplyMultiplier(data_array,raw_array,offset,position_offset,multiplier,accum){
	var ln = data_array.length;
	var i;
        var raw_data;

        //accumulations
	if(accum != 0){

            var d,h,acc_offset;

            for(i=0;i<ln;i++){
                
                //raw data
                raw_data = new Array();
                raw_data.push(data_array[i][0]*1000);
                raw_data.push(data_array[i][1] * multiplier);
                raw_array.push(raw_data);

                //offset data (visualization)
                d = new Date(data_array[i][0] * 1000);
                h = d.getHours();
                
                acc_offset = compute_accum_offset(accum,h);  
                data_array[i][0] = data_array[i][0]*1000 + offset+ acc_offset + position_offset;
                data_array[i][1] *= multiplier;
            }    
        }

        else{
            //rest of products
            for(i=0;i<ln;i++){

                //raw data
                raw_data = new Array();
                raw_data.push(data_array[i][0]*1000);
                raw_data.push(data_array[i][1] * multiplier);
                raw_array.push(raw_data);

                //offset data (visualization)
                data_array[i][0] = data_array[i][0] *1000 + offset + position_offset;
                data_array[i][1] *= multiplier;

            }
        }
}



/**
* @author Javier Millan
* TODO: Generate markings for values
*
* @param <fconfig> 
*
*/
function generate_markings(fconfig){

	return null;
}


/**
 * Generates the markings structure for flot to represent
 * the vertical lines
 * 
 * @param init_time		First vertical line to show
 * @param finish_time	Last vertical line to show
 * @param separation	Separation between each line
 * @param color			Line color
 * @param realsim       Has vertical line to separate real and simulatec data
 *
 **/
function generate_time_markings(init_time,finish_time,separation,color,realsim){
    var markings = new Array();
    var marking;
    var tmp_time;

    if(separation>0){
        for(tmp_time = init_time; tmp_time < finish_time; tmp_time += separation){
            marking = {color: color, lineWidth: 1, xaxis: {from: tmp_time, to: tmp_time}};
            markings.push(marking);
        }
    }

    if(realsim){
        if(realsim.show){
        var middle = init_time + (finish_time - init_time )/2;
        marking = {color: realsim.color, lineWidth: realsim.width, xaxis: {from: middle, to: middle}};
        markings.push(marking);
        }
    }

    return markings;
}


//get product index for config with id+hv
function find_product_config_index(product_id,p_hv,product_config){

	for (var i=0;i<product_config.products.length;i++){
		if((product_config.products[i].product_ID == product_id) &&(product_config.products[i].hv==p_hv))
			return i;
	}
	
	return -1;
}



/**
 * @author Jordi Castells
 * Align the axis data array to an specific align value
 *
 * @param axis_data     Axis data Array [[epoch,label],...]
 * @param align         Align value in Milliepoch
 */
function align_axis(axis_data,align){
    var ln,i;

    ln = axis_data.length;
    for(i=0;i<ln;i++){
        axis_data[i][0] = (axis_data[i][0] * 1000) + align; //milliepoch
    }
}


/**
 * @author Javier Millan
 * Compute the offset to align the accumulation bars. The objective
 * is that the bars are displayed focused
 *
 * @param interval      Number of hours of the accumulation
 * @param hour          Hour of the data {0,6,12,18}
 */
function compute_accum_offset(interval,hour){

   var offset = 0;
   var mh = 3600000;
  
 switch (interval){

        case 6:

            switch (hour){

                case 0:
                    offset = 0;
                    break;

                case 6:
                    offset = mh*3;
                    break;

                case 12:
                    offset = mh;
                    break;

                case 18:
                    offset = mh*2;
                    break;

                default:
                    offset = 0;
                    break;
            }
            break;


        case 12:

            switch (hour){

                case 0:
                    offset = mh;
                    break;


                case 12:
                    offset = mh*2;
                    break;

                default:
                    offset = 0;
                    break;
            }
            break;


        case 24:

            offset = mh;
            break;


        default:
            offset = 0;
            break;
    }

    return offset;
}



/**
 * @author Javier Millan
 * Defines the unit of each yaxis and sends it to draw
 *
 * @param placeholder   Div of the graph
 * @param f_data        Flot data object array
 */
function drawFlotUnits(placeholder,f_data,f_options){

    var unit1 = '';
    var unit2 = '';
    //select the correct unit for each y-axis
    for(var i=0; i<f_data.length; i++){

        if(f_data[i].yaxis == 1)
            unit1 = f_data[i].unit;

        else if (f_data[i].yaxis ==2)
            unit2 = f_data[i].unit;

    }

    //draw the units
    if(unit1 !=''){
        var xoffset = (typeof(f_options.yaxis.offset_unit) != 'undefined') ?
            f_options.yaxis.offset_unit : 0;

        drawYAxisUnit(placeholder,1,unit1,xoffset,0);
    }
    
     if(unit2 !='') {
        var xoffset2 = (typeof(f_options.y2axis.offset_unit) != 'undefined') ?
            f_options.y2axis.offset_unit : 0;
        
        drawYAxisUnit(placeholder,2,unit2,xoffset2,0);
     }
}


/**
 * Returns an array with MinMax [min,max]
 * from a data array with the format [[epoch,data],[epoch,data]....]
 *
 * @param array data array [[epoch,data],[epoch,data]....]
 *
 */
function getMinMax(p_data,mult,place){
    
    var minmax = [0,-1];
    var min = 0;
    var max = -1;
    var array = [];

    for(var j = 0; j< p_data.length;j++){

        if(p_data[j].yaxis == place){
        
           array = p_data[j].data;

           for(var i=0;i<array.length;i++){

                if(Math.round(mult*array[i][1])<min) min = mult*array[i][1];
                if(Math.round(mult*array[i][1])>max) max = mult*array[i][1];
            }
        }
    }

    minmax[0] = min;
    minmax[1] = max;
    return minmax;
}


function generateTicks(minmax,max_ticks,nticks,mult){

	var arr = [];
        arr.push(0);
        var top = 0;

        //define top ticks;
        for(var i=0; i < max_ticks.length;i++){

            if(minmax[1]< max_ticks[i]){
                top = max_ticks[i];
                break;
            }
        }

        if(top != 0){

            var prop = top/nticks;

            for(var j = 1; j<=nticks;j++){

                arr.push(j*prop*mult);
            }
        }
        
	return arr;
}

