/*
 * $Id: chart_main.js,v 1.8 2010/03/02 08:45:44 obo Exp $
 */  
dojo.declare("swx.inv.Chart", null, {
  
/*************************************************************************************************
 * Constructor                                                                                   *
 *************************************************************************************************/
  constructor: function(id, key, domain, type, chartLayout, customConfig,
      skipLoading, skipMovie, expirationDate, contractType, strikePrice) {
    // FQS PK (primary key) parameters, used to locate the security, which chart we would like to plot 
    // SecurityId/ProductSymbol is common parameter for ssecom/stoxx/eurex
    this.id = id;         // save the id
    // Eurex only, for the rest those below remains undefined
    this.expirationDate = expirationDate;
    this.contractType = contractType;
    this.strikePrice = strikePrice;
    // end of PK parameters

    this.key = key;       // save the key used to find HTML elements
    
    if (!domain)
      domain = "1d"; // intraday by default
    this.domain = domain; // save the domain (number of trading days to show)
        
    if (!type) type = this.statics.SIMPLE; // simple type by default
    this.type = type;     // save the type (simple, advanced)
    
    if (type != this.statics.SIMPLE)
      selectedTimeRange = domain;
    
    if (!chartLayout) chartLayout = siteChartConfig.getMINI_LAYOUT(); // mini layout by default
    this._chartLayout = chartLayout;     // object containing layout of the chart
    this._completeLayout(); // complete the layout
    
    this.skipMovie = skipMovie; // true if we do not want to update the chart
    
    this.useIntradayValues = true; // changed afterwards (for simple chart)
    
    this.values_days = {};   // containing day ticks (for advanced graphs)
    this.values_inter = {}; 
    this.values_inter_zoom = {};
    this.values_intra = {};  // containing intraday ticks (max 10 days, for advanced graphs)
    this.values_zoomed = {};  // containing intraday ticks like values_intra but each day 
                              // is an object member (only advanced graphs)
    
    this.values_domain     = {};  // values of the full domain being drawn
    this.values_displayed  = {};  // values of the drawn points (for advanced graphs)
    this.graph_values      = {};  // Graph values being displayed
    
    
    this.showedLines = {}; // save which line is shown (for advanced graphs)
    this.showedLines.id = this.id; // chart line is always displayed 
    
    this.showedEvents = {}; // For advanced charts (news, dividends, ...)
    this.events = {}; // Save all the downloaded events (capital events/news, for advanced graphs)
    
    // Save the start and stop percents for advanced graphs
    this.displayedMinPercent = 0;
    this.displayedMaxPercent = 1;
    
    // The data updater (uses ajax to load data at the start)
    this._chartDataUpdater = new swx.inv.ChartDataUpdater(this);
    // The drawer
    this.chartDrawer = new swx.inv.ChartDrawer(type == this.statics.SIMPLE, domain, key, this._chartLayout);
    
    // The ValuesHelper
    if (type == this.statics.SIMPLE) {
      this.valuesHelper = new swx.inv.ChartValuesHelper(this._chartLayout, type == this.statics.SIMPLE);
    } else {
      this.valuesHelper = new swx.inv.ChartValuesAdvancedHelper(this._chartLayout);
    }
    
    // boolean to know if the functions are active or not. (For advanced charts)
    this._functionsActive = false;
    

    // resize the holding DIV according to chart configuration
    var chartNode = dojo.query('#chart'+this.key);
    if(!isNaN(this._chartLayout.chartWidth))
      chartNode.style('width', this._chartLayout.chartWidth);
    if(!isNaN(this._chartLayout.chartHeight) )
      chartNode.style('height', this._chartLayout.chartHeight);    

    // adjust the position of loading message
    // 
    // Save layout ptr first
    var currentLayout = this._chartLayout; 
    dojo.query('#gfxLoading'+this.key).forEach(function(node){
      // do not move the element around if its is hidden (dojo.coords returns empty w/h values)
      if(node.style.display == 'none') 
        return; 
      var loadingMsgCoords = dojo.coords(node);
      var leftOffset = Math.round(Math.max(currentLayout.graphWidth  - loadingMsgCoords.w,0)/2+currentLayout.marginLeft);
      var topOffset  = Math.round(Math.max(currentLayout.graphHeight - loadingMsgCoords.h,0)/2+currentLayout.marginTop);
       
      if(!isNaN(topOffset))
        dojo.style(node,'top', topOffset);
      if(!isNaN(leftOffset) )
        dojo.style(node,'left', leftOffset);    
    });

    // Set up the timer if needed (used to get the new data)
    // we only use a static one to save number of timers on a page!
    if (this.statics._timer == null)
      this.statics._startTimer();
    
    // Custom Configuration (for advanced charts)
    if (customConfig)
      chartChangeGraphFromCustomConfig(this, customConfig, true);
    
    // get the initial values and run a function with the ajax response
    //  TODO: probably we need to change following line
    if (!skipLoading) this.getAjaxValues("id", this.id);

    this.statics.allCharts.push(this);
  },
  
/*************************************************************************************************
 * Static members ( You have to use this.statics.XXXX )                                          *
 *************************************************************************************************/
  statics: {
    // different types (SIMPLE: only show static data, ADVANCED: have some interactivity)
    SIMPLE   : 1,
    ADVANCED : 2,
    
    // different grains for data
    INTRADAY : "intraday",
    INTERMEDIATE : "intermediate",
    HISTORICAL : "historic",
    
    _timer: null, // a time to get the new data from (unique for all graphs to save browser's memory
    
    _startTimer: function() { // this is only called once at the first instance created of this class
      this._timer = setInterval(function() {
        for (var cNb=0; cNb<swx.inv.Chart.prototype.statics.allCharts.length; cNb++) {
          if (swx.inv.Chart.prototype.statics.allCharts[cNb].quotePlayer) {
            swx.inv.Chart.prototype.statics.allCharts[cNb].quotePlayer.timerTick();
          }
        }
      }, 1000);
    },
    allCharts : []
  },
  
/*************************************************************************************************
 * Private init function. Used to init the chart                                                 *
 *************************************************************************************************/
  _completeLayout: function() {
    this._chartLayout.surfaceWidth  = this._chartLayout.marginLeft + this._chartLayout.paddingLeft 
                                    + this._chartLayout.graphWidth  + this._chartLayout.paddingRight
                                    + this._chartLayout.marginRight;
    this._chartLayout.surfaceHeight = this._chartLayout.marginTop + this._chartLayout.paddingTop
                                    + this._chartLayout.graphHeight + this._chartLayout.paddingBottom 
                                    + this._chartLayout.marginBottom;
    
    // IE and FF cheats!
    if (dojo.isIE) {
      this._chartLayout.marginLeft -= 2;
    } else {
      this._chartLayout.marginTop += 0.5;  // sometimes the top border disapears in FF?
    }
  },
/*************************************************************************************************
 * Get ajax values (also used on the Market overview page                                        *
 *************************************************************************************************/
  
  getAjaxValues: function(lineType, id, newDate) {
    if (this.type == this.statics.ADVANCED) {
      var domain = this.domain;
      this._chartDataUpdater.calculateAndDownloadDataIfNeeded(
        domain, function () {changePeriod(domain);}
      );
    } else {
      this._chartDataUpdater.calculateAndDownloadDataIfNeeded(
        this.domain, null, newDate
      );
    }
  },
  
/*************************************************************************************************
 * Public draw function. Used to draw the chart                                                  *
 *************************************************************************************************/
  draw: function(onlyUpdateIfPossible) {
    if (this.type == this.statics.SIMPLE) {
      // draw simple graph
      
      if (this.values_domain.id && this.values_domain.id.vals) {
        var local_graph_values = this.valuesHelper.getSimpleGraphValuesAndUpdateRange(this.values_domain.id, 
                                                  onlyUpdateIfPossible,this._chartLayout.nonDiagonalShape);
        // to save time, only redraw line (If updating and limits are the same)
        // this is detected by the getSimpleGraphValuesAndUpdateRange
      
        this.chartDrawer.drawSimple(local_graph_values, this.values_domain, local_graph_values.onlyDrawLine);
      }
      
    } else { // ADVANCED
            
      // set the values_displayed depending on the displayedMin and displayedMax percents
      this.values_displayed = this.valuesHelper.cutValuesByPercent(
        this.values_domain, this.showedLines, this.displayedMinPercent, this.displayedMaxPercent
      );
           
      if (this.values_displayed.id.valuesType != swx.inv.Chart.prototype.statics.INTRADAY 
         && this.values_displayed.id.vals.length <= 10) {
         
        // We get here because we are showing historical data of 10 days or less. 
        // We would like to show intraday values
        var newValues = zoomIntoIntradayFromHistorical(onlyUpdateIfPossible);
        if (newValues == null) { 
          return;  // we have to download some, we will come back here
        }
        // Replace the old ones by the new ones
        this.values_displayed = newValues;
      
      } else if (this.values_displayed.id.vals.length >10 
                && this.values_displayed.id.vals.length<=25) {
      
        var newValues = zoomIntoIntermediate(onlyUpdateIfPossible);
        if (newValues == null) { 
          return;  // we have to download some, we will come back here
        }
        // Replace the old ones by the new ones
        this.values_displayed = newValues;
      }
      
      if (this.values_displayed.id.valuesType == swx.inv.Chart.prototype.statics.INTRADAY
          || this.values_displayed.id.valuesType == swx.inv.Chart.prototype.statics.INTERMEDIATE) {
        this.chartDrawer.sliderHelper.setDailyTrackLine();
      } else {
        this.chartDrawer.sliderHelper.setHistoricalTrackLine();
      }
    
      // mix the values_displayed together if we have more than one lin to show
      // this method modifies the values_displayed passed as parameter
      this.valuesHelper.mixValues(this.values_displayed, this.values_domain.id.oldClose);
    
      this.graph_values.id = this.valuesHelper.getAdvancedGraphValues(this.values_displayed.id, false, !onlyUpdateIfPossible, this.values_domain.id.oldClose);
      if (this.showedLines.idx1) { 
        this.graph_values.idx1 = this.valuesHelper.getAdvancedGraphValues(this.values_displayed.idx1, true, false);
      } else {
        this.graph_values.idx1 = null;
      }
      if (this.showedLines.idx2) { 
        this.graph_values.idx2 = this.valuesHelper.getAdvancedGraphValues(this.values_displayed.idx2, true, false);
      } else {
        this.graph_values.idx2 = null;
      }
      for (var i=1; i<=10; i++) {
        if (this.showedLines["ti"+i] && this.values_displayed["ti"+i]) { 
          if (this.values_displayed["ti"+i].isAddon) {
            this.graph_values["ti"+i] = this.valuesHelper.getAdvancedAddonGraphValues(this.showedLines["ti"+i], this.values_displayed["ti"+i]);
          } else {
            this.graph_values["ti"+i] = this.valuesHelper.getAdvancedGraphValues(this.values_displayed["ti"+i], true, false);
          }
        } else {
          this.graph_values["ti"+i] = null;
        }
      }
      
      this.chartDrawer.drawAdvanced(this.graph_values, this.values_displayed);
      if (!onlyUpdateIfPossible) {
        // For the small graph in the slider, we show the whole domain (not only displayMin and displayMax percents)
        var full_domain_graph_values = {};
        full_domain_graph_values.id = this.valuesHelper.getAdvancedGraphValues(this.values_domain.id, false, true, this.values_domain.id.oldClose);
        
        this.chartDrawer.drawSmallChart(full_domain_graph_values);
      }
      this.chartDrawer.drawVolume(this.graph_values.id.points, this.values_displayed.id);
      
      this.chartDrawer.drawEvents(this.values_displayed.id, this.events, this.showedEvents);

    }
    
  },
  
  drawPreview: function() {
    this.values_displayed = this.valuesHelper.cutValuesByPercent(
      this.values_domain, this.showedLines, this.displayedMinPercent, this.displayedMaxPercent, true
    );

    var preview_graph_values = this.valuesHelper.getPreviewGraphValues(this.values_displayed.id);
    
    this.chartDrawer.drawPreview(preview_graph_values);
  },
  
  // Only for simple charts
  changeDomain: function(domain) {
    this.domain = domain;
    this.chartDrawer.changeDomain(domain);
  },
  
  changeId: function(id, newDate) {
    this.id = id;
    this.quotePlayer.clearUpdateEvents(); // stop updates for this chart.
    this.quotePlayer = null; // stop the player for this id. A new one will be made
    this.chartDrawer.clean();
    this._chartDataUpdater.downloadedData["id"] = null;
    this.values_intra["id"] = null;
    this.values_days["id"] = null;
    this.getAjaxValues("id", id, newDate);
  },
  
  /** 
   * Does not really destroy the object, but stops all timers and updates, we can then
   * delete anything related to this chart. 
   */
  destroy: function() {
    if (this.quotePlayer) {
      this.quotePlayer.clearUpdateEvents(); // stop updates for this chart.
      this.quotePlayer = null; // stop the player for this id.
    }
  }
  
});    // end of Chart main class

// Some functions used for small and large charts

  function chartGetNiceDateFromEventDate(eventDate) {
    return chartGetNiceDate(eventDate) + " (" + chartGetNiceHourMinutes(eventDate) + ")";
  };
  
  function chartGetNiceHourMinutes(eventDate) {
    var date = tSd(eventDate);
    var hours = eventDate.getHours();
    if (hours < 10) hours = "0" + hours;
    var minutes = eventDate.getMinutes();
    if (minutes < 10) minutes = "0" + minutes;
    
    return hours +":"+ minutes;
  };
  
  function tSd(date) { 
    x=new Date(date.getUTCFullYear(),date.getUTCMonth(),date.getUTCDate(),date.getUTCHours(),date.getUTCMinutes(),date.getUTCSeconds());
    x.setTime(x.getTime()+dSd(x)+3600000); 
    return x; 
  } 
  
  function is_dsd(date){ return ((date.getTime()>fD(0,2,2,-1).getTime())&&(date.getTime()<fD(0,9,2,-1).getTime()))?true:false; } 
  function dSd(date){  return is_dsd(date)?3600000:0; }   
    
  function chartGetNiceDate(eventDate) {
    var day = eventDate.getDate();
    if (day < 10) day = "0" + day;
    var month = eventDate.getMonth()+1;
    if (month < 10) month = "0" + month;
    
    return day +"."+ month +"."+ eventDate.getFullYear();
  };
  
  function chartGetNiceNumber(number, fixed) {
      var numberFormatted = null;
      var index = null;
      if (fixed!=null && fixed >= 0) {
        numberFormatted = number.toFixed(fixed);
      } else {
        numberFormatted = number + "";
      }
      index = numberFormatted.indexOf(".");
      if (index == -1) index = numberFormatted.length;
      
      // format this number to ###'###'##0.00 (dojo uses the locale to set the grouping seperator!)
      
      while (index > 3) {
        index -= 3;
        numberFormatted = numberFormatted.substring(0, index) + "'" + 
                            numberFormatted.substring(index);
      }
      
      return numberFormatted;
  }
