//Kevin Lynagh January 2010
//Elastic lists for analyzing residues
//idea: let users save a "query" (the 'selected' object) in a list, and compare two of them -- in this comparison mode the charts take on red/green or light/dark based on whether or not certain elements change a lot between A & B. I'll probably also want normalization factors on each widget (maybe express things as a percentage by default? The numbers are pretty meaningless unless users input a list of PDBs)


//prefetch images
var amino_acids = ["ala", "arg", "asn", "asp", "cys", "glu", "gln", "gly", "his", "ile", "leu", "lys", "met", "phe", "pro", "ser", "thr", "trp", "tyr", "val"];
var imgs = new Array;
jQuery(amino_acids).each(function(i,aa_name){
                           imgs[i] = new Image(); //new img obj
                           imgs[i].src = '/aa_tooltips/' + aa_name + '.png';
                         });

var selected_color = 'darkRed';
var selected = new Object; //object that holds the currently selected bins
selected.residues = new Array;
selected.depths = new Array;
selected.volumes = new Array;

var sample_seqnos;
var charts = ['residue', 'depth', 'rama', 'volume'];

//individual charts
var residue_hist, depth_hist, volume_hist;

$(document).ready(function(){
                    layout_page();
                    make_residue_hist();
                    make_depth_hist();
                    make_volume_hist();
                    make_rama_hist();

                    selected.depths.push(4); //lets have the initial examples be bounded so they look nice
                    get_seqno_samples();
                    selected.depths.pop();

                  });




//weird things happen to my fancy-pants elements on resize, so lock the page width, make everything square, &c.
function layout_page(){
  //set the div heights to be a third of the width of the container (making the divs square)
  var width = $('#hist2').width();
  $('.small_hist').each(function(){
                          $(this).css('height', width);
                          $(this).css('width', width);
                        });
}



function set_loading_overlay(element){
  element = $('#' + element)[0];
  var overlay = $('#' + element.id + '_overlay');
  if(overlay.length > 0){
    $(overlay).remove();
  }else{
    overlay = document.createElement("div");
    overlay.id = element.id + '_overlay';
    var o = $(element).offset();
    $(overlay).css('position','absolute')
      .css('top', o.top)
      .css('left', o.left)
      .css('height', $(element).height() + 3)
      .css('width', $(element).width() + 3)
      .css('background-color', 'rgba(30,30,30,0.7');
    $(overlay).css('z-index','2000');
    document.body.appendChild(overlay);
    $(overlay).show();


  }
}





//use the current selected query to get some seqnos for o3d display
function get_seqno_samples(){
  //turn off the miniviews
  clear_o3d_samples();
  jQuery.ajax({
                type: 'POST',
                url: '/el/json/seqno_samples',
                data: {query:JSON.stringify(selected)},
                dataType: 'json',
                success: function(s){
                  sample_seqnos = s;
                  draw_o3d_samples(s);
                }
              });
}



function draw_random_seqnos(){
  var start = Math.floor(Math.random()*(sample_seqnos.length-4));
  for(var i=0; i<3; i++)
    make_miniview('miniview' + i, sample_seqnos[start + i]);
}

function draw_o3d_samples(sample_seqnos){
  for(var n=0; n<3; n++)
    make_miniview('miniview' + n, sample_seqnos[n]);
}

function clear_o3d_samples(){
  for(var n=0; n<3; n++)
    make_miniview('miniview' + n, false);
}



function draw_charts(charts){
  jQuery.each(charts, function(i, dimension){
                eval('make_' + dimension + '_hist();');
              });
}




function make_residue_hist(){
  //we want to query all of the OTHER parameters--if we include our selected as a param, then this chart will be just be the selected bars (i.e. useless)
  var my_query = jQuery.extend(true, new Object, selected); my_query.residues = new Array;

  jQuery.ajax({
                type: 'POST',
                url:  '/el/json/residue_counts',
                data: {query:JSON.stringify(my_query)},
                dataType: 'json',
                beforeSend: function(){
                  set_loading_overlay('residue_hist');
                },
                success: function(residue_counts){
                  draw_residue_hist(residue_counts);
                  set_loading_overlay('residue_hist');
                }
              });
}

function draw_residue_hist(residue_count_hash){
  var residue_types = new Array;
  var residue_counts = new Array;
  for(type in residue_count_hash){
    residue_types.push(type);
    residue_counts.push({name: type, y: residue_count_hash[type], selected: jQuery.inArray(type, selected.residues) != -1});
  }
  residue_hist = new Highcharts.Chart(jQuery.extend(true, new Object, hist_template,
                                                    {
                                                      chart:{
                                                        renderTo:'residue_hist'
                                                      },

                                                      series: [{name:'residue', data: residue_counts}],

                                                      xAxis:{
                                                        categories: residue_types,
                                                        title:{text:'residue types'}
                                                      }
                                                    }));


  jQuery.each(residue_hist.series[0].data, function(i, d){
                //color the selected elements
                if(jQuery.inArray(d.category, selected.residues) != -1){
                  d.selected = true;
                  d.unselected_color = d.color;
                  d.color = selected_color;
                }

                //set tooltip images
                d.tooltipText = '<img src="/aa_tooltips/' + d.name + '.png"/>';
              });




}




function make_depth_hist(){
  var my_query = jQuery.extend(true, new Object, selected); my_query.depths = new Array;
  jQuery.ajax({
                type: 'POST',
                url:  '/el/json/depth_counts',
                data: {query:JSON.stringify(my_query)},
                dataType: 'json',
		beforeSend: function(){
		  set_loading_overlay('hist0');
                },
                success: function(depth_counts){
                  draw_depth_hist(depth_counts);
		  set_loading_overlay('hist0');
                }
              });
}




function draw_depth_hist(depth_array){

  var depth_counts = new Array;
  var n = depth_array.length;
  for(var i = 0; i < n; i++){//convert the depths from floats to full objects so we can use selection stuff and don't have to explicitly set the labels.
    //bin_size is set in the serverside reduce function, for depth it's unity
    depth_counts.push({name: i, x: i, y: depth_array[i], selected: jQuery.inArray('depth', selected.depths) != -1});
  }

  depth_hist = new Highcharts.Chart(jQuery.extend(true, new Object, hist_template,
                                                  {
                                                    chart:{
                                                      renderTo:'hist0'
                                                    },

                                                    series: [{name:'depth', data: depth_counts}],

                                                    xAxis:{
                                                      title:{text:'residue depths (&Aring;)'},
                                                      labels:{align:'left'},
                                                      min:0,
                                                      max:13
                                                    },

                                                    tooltip:{
                                                      formatter: function() {
                                                        return this.y + ' residues at ' + this.x + ' &Aring;';
                                                      }
                                                    }


                                                  }));

  //color the selected elements
  jQuery.each(depth_hist.series[0].data, function(i, d){
                if(jQuery.inArray(d.category, selected.depths) != -1){
                  d.selected = true;
                  d.unselected_color = d.color;
                  d.color = selected_color;
                }
              });

}





function make_volume_hist(){
  var my_query = jQuery.extend(true, new Object, selected); my_query.volumes = new Array;
  jQuery.ajax({
                type: 'POST',
                url:  '/el/json/volume_counts',
                data: {query:JSON.stringify(my_query)},
                dataType: 'json',
                beforeSend: function(){
                  set_loading_overlay('hist2');
                },
                success: function(volume_counts){
                  draw_volume_hist(volume_counts);
                  set_loading_overlay('hist2');
                }
              });
}




function draw_volume_hist(volume_count_hash){
  var volume_counts = new Array;
  var n = volume_count_hash.length;
  for(var c in volume_count_hash){//convert the volumes from floats to full objects so we can use selection stuff and don't have to explicitly set the labels.
    var vol = parseFloat(c);
    if(vol < 400)
      volume_counts.push({name: vol, x: vol, y: volume_count_hash[c], selected: jQuery.inArray('volume', selected.volumes) != -1});
  }
  volume_hist = new Highcharts.Chart(jQuery.extend(true, new Object, hist_template,
                                                   {
                                                     chart:{
                                                       renderTo:'hist2'
                                                     },

                                                     series: [{name:'volume', data: volume_counts}],

                                                     xAxis:{
                                                       title:{text:'residue volumes (&Aring;<sup>3</sup>)'},
                                                       labels:{align:'left',
                                                               formatter: function() {
                                                                 return this.value%50 == 0 ? this.value : '';
                                                               }
                                                              },
						       tickInterval: "auto",
						       tickWidth: 0 // I can't figure out how to get highcharts to only draw some ticks and labels
                                                     },

                                                     tooltip:{
                                                       formatter: function() {
                                                         return this.y + ' residues with ' + this.x + ' &Aring;<sup>3</sup>';
                                                       }
                                                     }


                                                   }));

  //color the selected elements
  jQuery.each(volume_hist.series[0].data, function(i, d){
                if(jQuery.inArray(d.category, selected.volumes) != -1){
                  d.selected = true;
                  d.unselected_color = d.color;
                  d.color = selected_color;
                }
              });

}











function make_rama_hist(){
  //we want to query all of the OTHER parameters--if we include our selected as a param, then this chart will be just be the selected bars (i.e. useless)
  var my_query = jQuery.extend(true, new Object, selected); my_query.rama = new Array;
  jQuery.ajax({
                type: 'POST',
                url:  '/el/json/rama_counts',
                data: {query:JSON.stringify(my_query)},
                dataType: 'json',
                beforeSend: function(){
                  set_loading_overlay('hist1');
                },
                success: function(rama_counts){
                  draw_rama_hist(rama_counts);
                  set_loading_overlay('hist1');
                }
              });

}










function draw_rama_hist(rama_hist){
  var rama_div = $('#hist1');
  var n = rama_hist.n;
  var px = Math.ceil(rama_div.width() / n);
  var height = rama_div.height();
  var width = rama_div.width();

  //find the normalization factor (1 / the largest element)
  var max = 0;
  jQuery.each(rama_hist, function(k,v){
                if(parseInt(k) != NaN)
                  jQuery.each(v, function(i,e){
                                if(e > max)
                                  max = e;
                              }
                             );});


  //set the contents of the div to be a canvas element
  $(rama_div).html("<canvas id='rama_canvas' height=" + height + " width=" + width + "></canvas>");
  var rama_canvas = $('#rama_canvas')[0];
  if (rama_canvas && rama_canvas.getContext) {
    var context = rama_canvas.getContext('2d');
    if (context) { // okay, lets start drawing.
      //flip the coordinate system so positive y values go upwards
      context.scale(1, -1);
      context.translate(0, -n*px);
      for(var i=0; i<n ; i++){
        //go through all of the values on the row
        $(rama_hist[i]).each(function(j, val){
                               //val = val / max; //linear
                               //val = Math.atan(val) / (Math.PI/2); //arctangent
                               var a = 0.3;
                               val = 1/(1+Math.exp(-a*val)); //sigmoid
                               context.fillStyle = color_ramp(val, 'grayscale');
                               context.fillRect(i*px, j*px, px, px);
                             });
      };
    }
  }
}






//call this from the click event of a highcharts column->point, give it an array to add/remove the selected object from
function update_selections(obj){
  //change the selected? status flag
  var sel = !obj.selected;
  obj.selected = sel;
  //what is the id for this data class / histogram bin?
  var id = obj.name;

  //what is the actual series data (residue_type, depth, &c.)
  var series_name = obj.series.name;


  if(sel){
    //this might not be the best way to save the unselected color if I start coloring the bars based on other dynamic options (the unselected color could become stale)
    obj.unselected_color = obj.color;
    obj.color = selected_color;

    //add object to selected list
    selected[series_name + 's'].push(id);
  }else{
    obj.color = obj.unselected_color;

    //remove object from the selected list
    //selected_list.splice(jQuery.inArray(id, selected_list), 1);
    selected[series_name + 's'] = jQuery.grep(selected[series_name + 's'], function(e){return e != id;});

  }

  //redraw the chart with obj new color
  obj.series.chart.render();

  //update info
  //draw the other charts
  var c = jQuery.grep(charts, function(e){return e != series_name;});
  draw_charts(c);
  get_seqno_samples();

}































//template object for making elastic list histograms. use jQuery.extend(new Object, hist_template, overrides) to build an options hash for highcharts
var hist_template = {
  chart: {
    defaultSeriesType: 'column',
    backgroundColor: 'white'
  },

  //series: [{data: depth_counts}],

  plotOptions: {
    column: {
      pointPadding: 0.0001, //there is a bug where mouseover on the farleft column doesn't work if this is exactly zero
      groupPadding: 0.00,
      borderWidth: 2,
      borderColor: 'gray',
      shadow: true,

      point:{
        events:{
          click: function() {
            // sending 'this' is by pointer, basically, so we can just mess with it in another function. Careful...
            update_selections(this);
          }
        }
      }
    }
  },

  title: {
    text: ''
  },

  credits: {
    enabled: false,
    text: "Kevin Lynagh, dept. dirigible flightcraft",
    href: 'http://dirigibleFlightcraft.com'
  },

  colors:['white', 'lightgray'],

  xAxis: {
    //these are the x-labels
    //categories: x_labels,
    tickWidth: 1,
    tickInterval: 200,
    labels:{
      enabled: true,
      align: 'center'
    },
    title: {
      enabled: true,
      text: 'Residues',
      margin: 40,
      style: {
        color: 'black',
        font: "bold 20px sans-serif"
      }
    }
  },

  yAxis: {
    min: 0,
    title: {
      text: 'residue counts'
    }
  },

  legend: {
    enabled: false
  }

};



//given an x between 0 and 1, return a canvas color string
function color_ramp(x, kind){
  if(kind == undefined)
    kind = 'rainbow';

  switch(kind){
  case 'grayscale':
    return 'hsl(0,0%,' + (1-x)*100 + '%)';
  case 'rainbow':
    return 'hsl(' + x*255 + ',100%,50%)';
  default: //grayscale
    return 'hsl(0,0%,' + (1-x)*100 + '%)';
  };
}

