/*
 * $Id: chart_valuesHelper.js,v 1.1 2011/04/11 15:05:57 obo Exp $
 */
dojo.declare("swx.inv.ChartValuesHelper", null, {

/*************************************************************************************************
 * Constructor                                                                                   *
 *************************************************************************************************/
  constructor: function(chart, isSimple) {  // all arrays and complex objects should be declared here

    this._chart = chart;       // save the chart

    this._isSimple = isSimple; // Simple chart

    this._init();
  },

/*************************************************************************************************
 * Static members ( You have to use this.statics.XXXX )                                          *
 *************************************************************************************************/
  statics: {
    PAID_INTERVAL_MINS : 3,            // 3 minutes
    PAID_INTERVAL_MS   : 180 * 1000,    // 3 minutes
    PAID_INTERVAL_INTER_MS : 3600 * 1000
  },

/*************************************************************************************************
 * Private init function. Used to initialise everything                                          *
 *************************************************************************************************/
  _init: function() {
  },

  /**
   * Transform intraday values
   */
  transformIntraValues: function(values, getDetailedValues, lineType) {

    if (!values) return null;  // error in getting the information!

    var startOfDayDate = null;
    var endOfDayDate = null;

    var timezoneChanged = values.timezoneChanged;

    var compareMode = (!this._isSimple && lineType != "id");

    if (compareMode) {
      // take the id's first time
      var retObj = this._getIntraValuesForCompareMode(values, getDetailedValues, lineType);
      this._appendNewIntraValuesToZoomedValues(lineType, retObj.vals);
      return retObj;
    }

    if (values.vals && values.vals.length > 0) {
      startOfDayDate = new Date(values.vals[0].d); // should be the start of the day (8h30) of the first day
    } else {
      startOfDayDate = new Date(); // cheat just to have times on the chart
    }
    endOfDayDate = new Date(startOfDayDate);

    /**
     * Define the times for start and end of trading hours (hence the x-axis for an intraday chart).
     *
     * The first choice is the runtime values, defined by the tradingStartTime and tradingEndTime
     * variables that are passed to the chart constructor.
     * If these are null, we use the website-specific values (tradingStart, tradingEnd) defined
     * in the siteChartConfig (chartConfig.js).
     * In the absence of any runtime or config data, the default values are as defined for
     * ssecom (8:30-17:30)
     */

    var tradingDateSet = false ;
    var timeRe = /(\d{1,2}):(\d{2})/;
    var tStart = null;
    var tEnd = null;

    // First choice: runtime defined hours
    if (this._chart.runtimeConfig.tradingStartTime && this._chart.runtimeConfig.tradingEndTime) {
      var tStart = timeRe.exec( this._chart.runtimeConfig.tradingStartTime);
      var tEnd = timeRe.exec( this._chart.runtimeConfig.tradingEndTime);
      if (tStart && tEnd) {
        startOfDayDate.setHours(tStart[1]);
        startOfDayDate.setMinutes(tStart[2]);
        endOfDayDate.setHours(tEnd[1]);
        endOfDayDate.setMinutes(tEnd[2]);
        tradingDateSet = true;
      }
    }

    // Second choice: website-config defined hours
    if (!tradingDateSet) {
      if (this._chart._chartLayout) {
        tStart = timeRe.exec( this._chart._chartLayout.tradingStart);
        tEnd   = timeRe.exec( this._chart._chartLayout.tradingEnd);
        if (tStart && tEnd) {
          startOfDayDate.setHours(tStart[1]);
          startOfDayDate.setMinutes(tStart[2]);
          endOfDayDate.setHours(tEnd[1]);
          endOfDayDate.setMinutes(tEnd[2]);
          tradingDateSet = true;
        }
      }
    }

    // Final choice: default hours
    if (!tradingDateSet) {
      if (values.usIndex) {  // Eurex default US times
        startOfDayDate.setHours(14);
        startOfDayDate.setMinutes(30);
        endOfDayDate.setHours(23);
        endOfDayDate.setMinutes(30);
      } else {  // Default SSE trading hours
        startOfDayDate.setHours(8);
        startOfDayDate.setMinutes(30);
        endOfDayDate.setHours(17);
        endOfDayDate.setMinutes(30);
      }
    }

    var tmpTime = startOfDayDate.getTime();

    var newVals = [];
    var oldDates = {};

    // No intraday value yet, only use oldClose
    if (!values.vals || values.vals.length == 0) {
      // fill the whole day blank
      while (tmpTime < endOfDayDate) {
        newVals.push(this._createEmptyVal(tmpTime, oldDates, getDetailedValues));
        tmpTime += swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_MS;
      }
      var retObj = values;
      retObj.valuesType = swx.inv.Chart.prototype.statics.INTRADAY;
      retObj.vals = newVals;
      retObj.gMin   = 98/100 * values.oldClose;
      if (retObj.gMin < 0) retObj.gMin = 0;
      retObj.gMax   = 102/100 * values.oldClose;
      retObj.gRange = retObj.gMax - retObj.gMin;
      return retObj;
    }

    // Used to get the max and min. Start with first point
    var minY = values.vals[0].c;
    var maxY = minY;
    if (values.oldClose < minY && values.oldClose > 0) minY = values.oldClose;
    if (values.oldClose > maxY) maxY = values.oldClose;

    values.vals[0].d = values.vals[0].d - swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_MS; // CHEAT TO bring first point right on time
    var firstTradeTime = values.vals[0].d;

    if (!timezoneChanged) {
      firstTradeTime = tSd(new Date(values.vals[0].d)).getTime();
    }

    // fill the first blank
    while (tmpTime < firstTradeTime) {
      newVals.push(this._createEmptyVal(tmpTime, oldDates, getDetailedValues));
      tmpTime += swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_MS;
    }

    newVals.push(this._createFullVal(tmpTime, oldDates, values.vals[0], getDetailedValues));
    tmpTime += swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_MS;

    for (var i = 1; i<values.vals.length; i++) {  // i = 0 is already done above
      var val = values.vals[i];
      if (!timezoneChanged) {
        val.d = tSd(new Date(val.d)).getTime();
      }

      if (val.d > endOfDayDate) { // another day
        while (tmpTime <= endOfDayDate) {  // fill the end of the day with empty vals
          newVals.push(this._createEmptyVal(tmpTime, oldDates, getDetailedValues));
          tmpTime += swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_MS;
        }
        if (new Date(val.d).getDate() == endOfDayDate.getDate()) {
          continue;  // points after closing time... skip them!
        }
        // TODO fix if points are before 8h30 ?
        val.d = val.d - swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_MS; // CHEAT TO bring first point right on time

        var newDate = new Date(val.d);
        endOfDayDate.setDate(newDate.getDate()); // next trading day
        endOfDayDate.setMonth(newDate.getMonth()); // next trading month
        endOfDayDate.setFullYear(newDate.getFullYear()); // next trading year

        startOfDayDate.setDate(endOfDayDate.getDate());   // next day
        startOfDayDate.setMonth(endOfDayDate.getMonth());   // next month
        startOfDayDate.setFullYear(endOfDayDate.getFullYear());   // next year
        tmpTime = startOfDayDate.getTime();
        while (tmpTime < val.d) {  // fill the start of day with empty vals
          newVals.push(this._createEmptyVal(tmpTime, oldDates, getDetailedValues));
          tmpTime += swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_MS;
        }
      }
      while (tmpTime < val.d) {  // fill with fake vals until next real val
        newVals.push(this._createFakeVal(tmpTime, oldDates, values.vals[i-1], val, getDetailedValues));
        tmpTime += swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_MS;
      }
      newVals.push(this._createFullVal(tmpTime, oldDates, val, getDetailedValues));
      tmpTime += swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_MS;

      // refresh max and min
      if (val.c < minY) minY = val.c;
      if (val.c > maxY) maxY = val.c;
    }
    while (tmpTime <= endOfDayDate) {  // fill the end of the day with empty vals
      newVals.push(this._createEmptyVal(tmpTime, oldDates, getDetailedValues));
      tmpTime += swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_MS;
    }

    var retObj = values;
    retObj.valuesType = swx.inv.Chart.prototype.statics.INTRADAY;
    retObj.vals = newVals;
    retObj.gMin   = minY;
    retObj.gMax   = maxY;
    if (minY == maxY) {
      retObj.gMin -= 2;
      if (retObj.gMin < 0) retObj.gMin = 0;
      retObj.gMax += 2;
    }
    retObj.gRange = retObj.gMax - retObj.gMin;

    if (!this._isSimple) {
      this._appendNewIntraValuesToZoomedValues(lineType, newVals);
    }
    return retObj;
  },

  /* This function take the newly created newVals array and splits it into days into the
   * advancedChart values_zoomed object (This is used when the user zooms in a lot, we show
   * him intraday ticks). It is duplicated with values_intra but this is getting too complex!
   */
  _appendNewIntraValuesToZoomedValues: function(lineType, newVals) {

    if (!advancedChart.values_zoomed[lineType]) {
      advancedChart.values_zoomed[lineType] = {};
    }
    var dayVals = [];
    var dayDate = "";
    for (var i=0; i<newVals.length; i++) {
      var val = newVals[i];
      if (val.newDay) {
        // Save old day if there is one
        if (dayVals.length > 0) {
          advancedChart.values_zoomed[lineType][dayDate] = true;
          dayVals = [];
        }

        var day = val.dayDate;
        if (day < 10) day = "0" + day;
        var month = val.month + 1;
        if (month < 10) month = "0" + month;
        dayDate = val.fullyear + "" + month + "" + day;
      }

      dayVals.push(val);
    }

    // last one
    if (dayVals.length > 0) {
      advancedChart.values_zoomed[lineType][dayDate] = true;
    }

  },

  _getIntraValuesForCompareMode: function(values, getDetailedValues, lineType) {

    var newVals = [];
    var oldDates = {};
    var startOfDayDate = null;
    var endOfDayDate = null;
    var timezoneChanged = values.timezoneChanged;

    // Used to get the max and min. Start with first point
    var minY = null;
    var maxY = null;

    var idIndex = 0;
    var idVal = null;
    var newValuesIndex = 0;
    var newVal = null;
    var newValDate = null;
    var existingIndex = 0;
    var existingVal = null;

    var fillFake = false;
    var valueChanged = true;

    if (values.vals && values.vals.length > 0) {
      startOfDayDate = new Date(values.vals[0].d); // should be the start of the day (8h30) of the first day
    } else {
      startOfDayDate = new Date(); // cheat just to have times on the chart
    }
    endOfDayDate = new Date(startOfDayDate);

    if (values.usIndex) {
      startOfDayDate.setHours(14);
      startOfDayDate.setMinutes(30);
      endOfDayDate.setHours(23);
      endOfDayDate.setMinutes(30);
    } else {
      startOfDayDate.setHours(8);
      startOfDayDate.setMinutes(30);
      endOfDayDate.setHours(17);
      endOfDayDate.setMinutes(30);
    }

    var tmpTimeNewValuesIndex = startOfDayDate.getTime();
    var tmpTimeExistingIndex = startOfDayDate.getTime();

    while (idIndex<advancedChart.values_intra.id.vals.length) {

      idVal = advancedChart.values_intra.id.vals[idIndex];
      if (newValuesIndex >= values.vals.length) {
        var newValToAdd = null;
        while (advancedChart.values_intra[lineType] && existingIndex < advancedChart.values_intra[lineType].vals.length) {
          existingVal = advancedChart.values_intra[lineType].vals[existingIndex];
          tmpTimeExistingIndex = this._getIntraFittedTmpTime(existingVal.d, tmpTimeExistingIndex);
          if (tmpTimeExistingIndex == idVal.d) {
            newValToAdd = existingVal;
            existingIndex++;
            break;
          } else if (tmpTimeExistingIndex > idVal.d) {
            break; // No existing point already;
          }
          existingIndex++;
        }
        if (!newValToAdd) {
          newValToAdd = this._createEmptyVal(idVal.d, oldDates, getDetailedValues);
        }
        newVals.push(newValToAdd);
        idIndex++;
        continue;
      }

      newVal = values.vals[newValuesIndex];
      if (!timezoneChanged && valueChanged) {
        newVal.d = tSd(new Date(newVal.d)).getTime();
        valueChanged = false;
      }

      if (newValDate == null || new Date(newVal.d).getDate() != newValDate) {
        // new date
        fillFake = false;
        newVal.d = newVal.d - swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_MS; // CHEAT TO bring first point right on time
        newValDate = new Date(newVal.d).getDate();
      }


      tmpTimeNewValuesIndex = this._getIntraFittedTmpTime(newVal.d, tmpTimeNewValuesIndex);

      if (tmpTimeNewValuesIndex > idVal.d) { // compared point is after this id point
        var newValToAdd = null;
        while (advancedChart.values_intra[lineType] && existingIndex < advancedChart.values_intra[lineType].vals.length) {
          existingVal = advancedChart.values_intra[lineType].vals[existingIndex];
          tmpTimeExistingIndex = this._getIntraFittedTmpTime(existingVal.d, tmpTimeExistingIndex);
          if (tmpTimeExistingIndex == idVal.d) {
            newValToAdd = existingVal;
            existingIndex++;
            break;
          } else if (tmpTimeExistingIndex > idVal.d) {
            break; // No existing point already;
          }
          existingIndex++;
        }
        if (!newValToAdd) {
          if (fillFake) {
            newValToAdd = this._createFakeVal(idVal.d, oldDates, values.vals[newValuesIndex-1], newVal, getDetailedValues);
          } else {
            newValToAdd = this._createEmptyVal(idVal.d, oldDates, getDetailedValues);
          }
        }
        newVals.push(newValToAdd);

        idIndex++; // go to the next id point
        continue;
      }

      if (tmpTimeNewValuesIndex < idVal.d) { // skip compared trades until the time of the id
        newValuesIndex++;
        valueChanged = true;
        continue;
      }

      newVals.push(this._createFullVal(idVal.d, oldDates, newVal, getDetailedValues));
      // refresh max and min
      if (minY == null || newVal.c < minY) minY = newVal.c;
      if (maxY == null || newVal.c > maxY) maxY = newVal.c;

      fillFake = true;
      idIndex++;
      newValuesIndex++;
      valueChanged = true;
    }

    var retObj = values;
    retObj.valuesType = swx.inv.Chart.prototype.statics.INTRADAY;
    retObj.vals = newVals;
    retObj.gMin   = minY;
    retObj.gMax   = maxY;
    if (minY == maxY) {
      retObj.gMin -= 2;
      if (retObj.gMin < 0) retObj.gMin = 0;
      retObj.gMax += 2;
    }
    retObj.gRange = retObj.gMax - retObj.gMin;
    return retObj;

  },

  _getIntraFittedTmpTime: function(currentDate, oldTmpTime){
     while (oldTmpTime < currentDate) {
       oldTmpTime += swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_MS;
     }
     return oldTmpTime;
  },

  _createEmptyVal: function(tmpTime, oldDates, getDetailedValues) {
    var newval = {};

    if (getDetailedValues) {
      newval.v = 0; // default volume = 0;
    }

    newval.d = tmpTime;
    newval.date = new Date(tmpTime);
    newval.hour = newval.date.getHours();
    newval.dayDate = newval.date.getDate();
    newval.day = newval.date.getDay();
    newval.month = newval.date.getMonth();
    newval.fullyear = newval.date.getFullYear();

    if (oldDates.hour != null && oldDates.hour != newval.hour) {
      newval.newHour = true;
    }
    if (oldDates.dayDate == null || newval.dayDate != oldDates.dayDate) {  // a line every new day
      newval.newDay = true;
    }
    if (oldDates.day != null && newval.day < oldDates.day) {  // a line every new week
      newval.newWeek = true;
    }
    if (oldDates.month != null && oldDates.month != newval.month) {
      newval.newMonth = true;
    }
    if (oldDates.fullyear != null && oldDates.fullyear != newval.fullyear) {
      newval.newYear = true;
    }

    oldDates.hour = newval.date.getHours();
    oldDates.dayDate = newval.dayDate;
    oldDates.day = newval.day;
    oldDates.month = newval.month;
    oldDates.fullyear = newval.fullyear;

    return newval;
  },

  _createFullVal: function(tmpTime, oldDates, val, getDetailedValues) {
    var newval = this._createEmptyVal(tmpTime, oldDates, getDetailedValues);
    newval.y = val.c;
    if (val.tr)
      newval.tr = val.tr;
    if (getDetailedValues) {
      newval.v = val.v;
      newval.open = val.o;
      newval.min = val.m;
      newval.max = val.M;
    }

    return newval;
  },

  _createFakeVal: function(tmpTime, oldDates, oldVal, newVal, getDetailedValues) {
    var fakeval = this._createEmptyVal(tmpTime, oldDates, getDetailedValues);
    var ratio = (tmpTime - oldVal.d) / (newVal.d - oldVal.d);
    fakeval.y = oldVal.c + ratio * (newVal.c - oldVal.c);
    return fakeval;
  },

/*************************************************************************************************
 * Things for daily values                                                                       *
 *************************************************************************************************/
  transformIntermediateValues: function(values, getDetailedValues, lineType, expectedStart, expectedStop) {
    if (!values || ! values.vals) return null;  // error in getting the information!

    var paidInterval = swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_INTER_MS;
    var startOfDayDate = null;
    var endOfDayDate = null;
    var timezoneChanged = values.timezoneChanged;

    if (values.vals && values.vals.length > 0) {
      startOfDayDate = new Date(values.vals[0].d * 100000); // should be the start of the day (8h30) of the first day
    } else {
      startOfDayDate = new Date(); // cheat just to have times on the chart
    }
    endOfDayDate = new Date(startOfDayDate);

    // Set the trading hours
    if (values.usIndex) {
      startOfDayDate.setHours(15);
      startOfDayDate.setMinutes(00);
      endOfDayDate.setHours(23);
      endOfDayDate.setMinutes(30);
      if (expectedStart) {expectedStart.setHours(15);expectedStart.setMinutes(00);}
      if (expectedStop) {expectedStop.setHours(23);expectedStop.setMinutes(30);}
    } else {
      startOfDayDate.setHours(9);
      startOfDayDate.setMinutes(00);
      endOfDayDate.setHours(17);
      endOfDayDate.setMinutes(30);
      if (expectedStart) {expectedStart.setHours(9);expectedStart.setMinutes(00);}
      if (expectedStop) {expectedStop.setHours(17);expectedStop.setMinutes(30);}
    }

    var tmpTime = startOfDayDate.getTime();

    var oldDates = {};
    var newVals = [];

    // when comparing ...
    if (!this._isSimple && lineType != "id") {
      var start = advancedChart.values_inter.id.vals[0].d;
      if (expectedStart) {
        start = expectedStart.getTime();
      } else {
        var timeRange = selectedTimeRange;
        if (timeRange == "ytd")
          timeRange = "1y"; // don't fool around with ytd, go for 1y
        if (domainDatesMap == null || !domainDatesMap["_"+timeRange])
          fillDomainDatesMap();
        start = domainDatesMap["_"+timeRange];
        if (values.usIndex) {
          start.setHours(15);
          start.setMinutes(00);
        }
        else {
          start.setHours(9);
          start.setMinutes(00);
        }
      }

      var idStartIndex = 0;
      for (var i=0; ; i++) {
        if (advancedChart.values_inter.id.vals[i].d < start)
          continue; // do not take this value (before limit)
        idStartIndex = i;
        break;
      }

      while (idStartIndex<advancedChart.values_inter.id.vals.length && advancedChart.values_inter.id.vals[idStartIndex].d<tmpTime) {
        newVals.push(this._createEmptyVal(advancedChart.values_inter.id.vals[idStartIndex].d, oldDates, false));
        idStartIndex++;
      }
    }

    // IPO-416 Start with the first value (index 0)
    // fill the first values if necessary
    var start = 0;
    values.vals[start].d *= 100000; // bandwidth hack reversal
    if (timezoneChanged) {
        // already Swiss time
        values.vals[start].d = values.vals[start].d;
    } else {
        values.vals[start].d = tSd(new Date(values.vals[start].d)).getTime(); // get it as the swiss time if necessary
    }
    var firstTradeTime = values.vals[start].d;

    while (tmpTime < firstTradeTime && tmpTime < endOfDayDate.getTime()) {
      newVals.push(this._createEmptyVal(tmpTime, oldDates, false));
      tmpTime += swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_INTER_MS;
    }

    newVals.push(this._createFullVal(tmpTime, oldDates, values.vals[0], false));
    tmpTime += swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_INTER_MS;

    start ++; // first done above
    for (var j = start; j < values.vals.length; j++) {
      var val = values.vals[j];
      val.d *= 100000; // bandwidth hack reversal;
      if (!timezoneChanged) {
        val.d = tSd(new Date(val.d)).getTime(); // get it as swiss
      }

      // if current value is for a new day
      if (val.d > endOfDayDate.getTime()) {
        while (tmpTime <= endOfDayDate.getTime()) {
          newVals.push(this._createEmptyVal(tmpTime, oldDates, false));
          tmpTime += swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_INTER_MS;
        }
        // skip all the points for the same day
        if (new Date(val.d).getDate() == endOfDayDate.getDate()) continue;

        // initialize date for the next day
        var nextDay = new Date(val.d);
        startOfDayDate.setDate(nextDay.getDate());
        startOfDayDate.setMonth(nextDay.getMonth());
        startOfDayDate.setFullYear(nextDay.getFullYear());
        endOfDayDate.setDate(nextDay.getDate());
        endOfDayDate.setMonth(nextDay.getMonth());
        endOfDayDate.setFullYear(nextDay.getFullYear());

        // reset tmpTime
        tmpTime = startOfDayDate.getTime();
        if (timezoneChanged) {
          firstTradeTime = values.vals[j].d;
        } else {
          firstTradeTime = tSd(new Date(values.vals[j].d)).getTime();
        }

        // fill the first values of the day if necessary
        while (tmpTime < firstTradeTime && tmpTime < endOfDayDate.getTime()) {
          newVals.push(this._createEmptyVal(tmpTime, oldDates, false));
          tmpTime += swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_INTER_MS;
        }
      }

      // complete between values
      while (tmpTime < val.d) {
        newVals.push(this._createEmptyVal(tmpTime, oldDates, false));
        tmpTime += swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_INTER_MS;
      }

      newVals.push(this._createFullVal(val.d, oldDates, val, getDetailedValues));
      tmpTime += swx.inv.ChartValuesHelper.prototype.statics.PAID_INTERVAL_INTER_MS;
    }

    // when comparing ...
    if (!this._isSimple && lineType != "id") {
      var stop = advancedChart.values_inter.id.vals[advancedChart.values_inter.id.vals.length-1].d;
      if (expectedStop) {
        console.log("Stop " + expectedStop);
        stop = expectedStop.getTime();
      }
      var idStopIndex = advancedChart.values_inter.id.vals.length-1;
      var idStartIndex = advancedChart.values_inter.id.vals.length-1;
      for (var i=advancedChart.values_inter.id.vals.length-1; ; i--) {
        if (advancedChart.values_inter.id.vals[i].d > stop) continue; // do not take this value (before limit)
        idStopIndex = i;
        if (advancedChart.values_inter.id.vals[i].d > tmpTime) continue;
        idStartIndex = i;
        break;
      }
      for (var i=idStartIndex; i<idStopIndex; i++) {
        newVals.push(this._createEmptyVal(advancedChart.values_inter.id.vals[i].d, oldDates, false));
      }
    }

    var retObj = values;
    retObj.valuesType = swx.inv.Chart.prototype.statics.INTERMEDIATE;
    retObj.vals = newVals;
    return retObj;
  },


  transformDailyValues: function(values, getDetailedValues, lineType) {
    if (!values || ! values.vals) return null;  // error in getting the information!

    var oldDates = {};
    // Call createEmptyVal with the first point to fill the oldDates
    this._createEmptyVal(values.vals[0].d * 100000, oldDates, false);

    var newVals = [];

    // start cheat to correct some securities that do not trade on the same days as the id
    var idStartIndex = -1;
    if (!this._isSimple && lineType != "id") {
      var timeRange = selectedTimeRange;
      if (timeRange == "ytd") timeRange = "1y"; // don't fool around with ytd, go for 1y
      if (domainDatesMap == null || !domainDatesMap["_"+timeRange]) {
        fillDomainDatesMap();
      }
      var startTime = domainDatesMap["_"+timeRange];

      for (var i=0; ; i++) {
        if (advancedChart.values_days.id.vals[i].d < startTime) continue; // do not take this value (before limit)
        idStartIndex = i;
        break;
      }
    }
    // stop cheat to correct some securities that do not trade on the same days as the id

    // start = 0 to have the first point. IPO-416
    // Overlap with other range is controlled by data updater.
    var start = 0;

    for (var j = start; j<values.vals.length; j++) {
      var val = values.vals[j];
      val.d *= 100000; // add the 5 zeros we removed to gain bandwith;

      // start cheat to correct some securities that do not trade on the same days as the id
      if (!this._isSimple && idStartIndex >= 0) {
        var tmpVal = val.d;
        while (tmpVal > advancedChart.values_days.id.vals[idStartIndex].d) {
          // some points are missing to compare to the id one, add them as empty points!
          newVals.push(this._createEmptyVal(advancedChart.values_days.id.vals[idStartIndex].d, oldDates, false));
          idStartIndex++;
        }
        if (tmpVal < advancedChart.values_days.id.vals[idStartIndex].d) {
           // this point is not in the id, but store it anyway !
           // not storing it leads to comparison issues when increasing period
           newVals.push(this._createFullVal(val.d, oldDates, val, getDetailedValues));
           continue;
        }
        idStartIndex++;
      }
      // stop cheat to correct some securities that do not trade on the same days as the id
      newVals.push(this._createFullVal(val.d, oldDates, val, getDetailedValues));
    }

    // todo fill last points?

    var retObj = values;
    retObj.valuesType = swx.inv.Chart.prototype.statics.HISTORICAL;
    retObj.vals = newVals;
    return retObj;
  },

/*************************************************************************************************
 * Things for graph values                                                                       *
 *************************************************************************************************/

  // This function takes all the values and returns the points in pixels!
  // nonDiagonal - if true put intermediate points so the graph line will be drawn using horizontal/vartical
  // lines only. By default the graph is drawn by connecting the values using diagonal lines.
  //
  getSimpleGraphValuesAndUpdateRange: function(values, onlyUpdateIfPossible, nonDiagonal) {

    // Object we return
    var retObj = {};

    if (!values || !values.vals) return retObj; // No intraday values

    // Used to get the max and min. Start with first point
    var minY = (values.oldClose ? values.oldClose : null);
    var maxY = minY;
    for (var i=0; i<values.vals.length; i++) {
      if (values.vals[i].y) {  // draw this point only if a trade was made
        if (minY == null || values.vals[i].y < minY) minY = values.vals[i].y;
        if (maxY == null || values.vals[i].y > maxY) maxY = values.vals[i].y;
      }
    }

    if (onlyUpdateIfPossible && values.gMin <= minY && values.gMax >= maxY) {
      // New values are inside the old ones, keep these min and max
      retObj.onlyDrawLine = true;
    } else if (maxY != minY) {
      // new limits
      values.gRange = maxY - minY;
      // Make them a bit larger so the chart does not jump all the time!
      values.gMin   = minY - values.gRange * 5/100;
      values.gMax   = maxY + values.gRange * 5/100;;
      values.gRange += values.gRange * 10/100;;
    }
    var pointIndex = 0;
    // Array we will return;
    var pixelLine = [];

    var leftSpace = this._chart._chartLayout.marginLeft + this._chart._chartLayout.paddingLeft;
    var topSpace  = this._chart._chartLayout.marginTop + this._chart._chartLayout.paddingTop;

    var oldPx = -1; // use to know if this pixel is used or not
    var oldPy ;
    for (var i=0; i<values.vals.length; i++) {
      if (values.vals[i].y) {  // draw this point only if a trade was made
        var px = Math.floor(leftSpace + this._chart._chartLayout.graphWidth * i / (values.vals.length - 1));
        var py = Math.floor(topSpace + this._chart._chartLayout.graphHeight * ( 1 -  (values.vals[i].y - values.gMin) / values.gRange));

        if (oldPx == px) {  // same pixel, use latest py
          pixelLine.pop(); // remove last py
          pixelLine.push(py);
        } else {
          // we want the chart points to be connected by horizontal+vartical lines instead of diagonal ones
          if(nonDiagonal && !isNaN(oldPy)){
            pixelLine.push(px);
            pixelLine.push(oldPy);
          }
          pixelLine.push(px);
          pixelLine.push(py);
          oldPx = px;
        }
        oldPy = py;
      }
    }
    retObj.line = pixelLine;

    if (values.oldClose) {
      retObj.oldClosePy = Math.floor(this._chart._chartLayout.marginTop
          + this._chart._chartLayout.paddingTop
          + this._chart._chartLayout.graphHeight * ( 1 - (values.oldClose - values.gMin) / values.gRange));
    }

    return retObj;
  }


});

