/* Slider widget */
/* (c) Copyright 2006 ClickRSVP */

/* Requires: prototype.js */

var SliderParams = Class.create();
SliderParams.prototype.extend({
  initialize: function (overrides) {
    this.minAmount = 5000;
    this.maxAmount = 100000;
    this.arrowOffset = 15;
    if (overrides) Object.extend(this, overrides);
  }
});

var RateGroup = Class.create();
Object.extend(RateGroup.prototype, {
  initialize: function ($rate, $rateArea, $rateLabel, $dollarLabel, $rateBar, $left, $width) {
    this.rate = $rate;
    this.rateArea = $($rateArea);
    this.rateLabel = $($rateLabel);
    this.dollarLabel = $($dollarLabel);
    this.left = $left;
    this.width = $width;
  }
});

var Slider = Class.create();
Object.extend(Slider.prototype, {
  initialize: function ($sliderBackground, $sliderRates, $sliderMain, $sliderLabels, $arrow, $hvc) {
    this.elBackground = $($sliderBackground);
    this.elRates = $($sliderRates);
    this.elMain = $($sliderMain);
    this.elLabels = $($sliderLabels);
    this.elArrow = $($arrow);
    this.hvc = $hvc;
    this.params = $hvc.params;
    this.rates = $hvc.rates;
    this.size = new Size(Element.findStyleProperty(this.elMain, 'width'), Element.findStyleProperty(this.elMain, 'height'));
    this.arrowSize = new Size(Element.findStyleProperty(this.elArrow, 'width'), Element.findStyleProperty(this.elArrow, 'height'));
    this.params.sliderWidth = this.size.width;
    this.rateGroups = new Array();
  },
  getValidRates: function () {
    var boundaries = new Array(),
        validRates = new Array(),
        tempRates = new Array();
    function addToList(list, item, key) {
      if (typeof(key) == 'undefined' || isFinite(key)) key = 'item' + item;
      var result = list[key] = list.length; 
      list[list.length] = item;
      return result;
    }
    function inList(list, key) {
      if (isFinite(key)) key = 'item' + key;
      return list[key] >= 0; 
    }
    function sortBoundariesFunc(amount1, amount2) {
      return amount1 - amount2;
    }
    this.bestRate = null;
    // create list of rate boundaries and valid rates
    for (var i = 0; i < this.rates.length; i++) {
      var rate = this.rates[i],
          range = this.getRateRange(rate);
      if (range.AmountMin < range.AmountMax) {
        if (!inList(tempRates, rate.RateId))
          addToList(tempRates, rate, rate.RateId); 
        if (!inList(boundaries, range.AmountMin))
          addToList(boundaries, range.AmountMin);
        if (!inList(boundaries, range.AmountMax))
          addToList(boundaries, range.AmountMax);
      }
    }
    if (boundaries.length > 0) { 
      boundaries.sort(sortBoundariesFunc);
      // find best rate in each of the ranges created by boundaries
      // and add to list of rates
      var prevBoundary = boundaries[0];
      for (var i = 1; i < boundaries.length; i++) {
        var rate = this.findBestRate(tempRates, (prevBoundary + boundaries[i]) / 2);
        if (rate) {
          rate.sliderMin = prevBoundary;
          rate.sliderMax = boundaries[i] - this.params.dollarLabelPrecision;
          validRates.push(rate);
          if (!this.bestRate || (this.bestRate.Rate > rate.Rate))
            this.bestRate = rate;
        }
        prevBoundary = boundaries[i];
      }
    }
    return validRates;    
  },
  getRateRange: function (rate) {
    // get working range for a rate
    // should be overridden
    var minAmount = Math.max(this.minAmount, rate.AmountMin || 0),
        maxAmount = Math.min(this.maxAmount, rate.AmountMax || Number.MAX_VALUE);
    return {'AmountMin': minAmount, 'AmountMax': maxAmount};
  },
  findBestRate: function (rates, amount) {
    // find best rate between two dollar values
    // this should be overridden
    var rate = null, i = 0;
    while (i < rates.length) { 
      var range = this.getRateRange(rates[i]);
      if (range.AmountMin >= amount && range.AmountMax <= amount)
        if (!rate || rates[i].Rate < rate.Rate) {
          rate = rates[i];
        }
      i++;
    }
    return rate;
  },
  assignRateTemperatures: function (rates) {
    function sortByRateFunc (rate1, rate2) {
      return rate2.Rate - rate1.Rate; // reverse sort
    }
    if (rates.length == 1) 
      rates[0].temperature = 1.0;
    else {
      var tempRates = new Array();
      for (var i = 0; i < rates.length; i++) {
        tempRates[i] = rates[i];
        tempRates[i].origRate = rates[i];
      }
      tempRates.sort(sortByRateFunc);
      for (var i = 0; i < tempRates.length; i++) {
        var temperature = (Math.pow(i, 2) / Math.pow(tempRates.length - 1, 2)) * 0.8 + 0.2; // never below 20% opacity
        tempRates[i].origRate.temperature = temperature;
      }
    }
  },
  createRatesAreas: function () {
    var tempRates = this.validRates,
        width = this.size.width / tempRates.length,
        x = 0;
    this.assignRateTemperatures(tempRates);
    for (var i = 0; i < tempRates.length; i++) {
      var pct = this.formatRate ? this.formatRate(tempRates[i]) : tempRates[i].Rate + '%',
          amountLow = tempRates[i].sliderMin,
          amountHigh = tempRates[i].sliderMax,
          label = this.formatDollars(amountLow, false);
      x = i * width;
      var rateArea = this.createRateArea(x, width, tempRates[i].temperature, pct),
          rateBar = this.createRateBar(x, Math.abs(i)),
          rateLabel = this.createRateLabel(x, width, pct),
          dollarLabel = this.createDollarLabel(x, label),
          rateGroup = new RateGroup(tempRates[i], rateArea, rateLabel, dollarLabel, rateBar, x, width);
      this.rateGroups.push(rateGroup);
    }
    this.createRateBar(this.size.width, -1);
    this.createDollarLabel(this.size.width, this.formatDollars(amountHigh, false)); // this.maxAmount??? --> causes payment problems if highest amount is entered!
  },
  createRateArea: function (left, width, temperature, pct) {
    // Create a new rate area
    var div = Element.newElement('DIV'),
        img = Element.newElement('IMG'),
        img2 = Element.newElement('IMG');
    div.className = this.params.rateAreaStyle;
    div.style.left = Element.intToPx(left);
    div.style.width = Element.intToPx(width);
    div.title = pct;
    if (Browser.isGecko || Browser.isOpera) {
      // compute color
      var color = new Color(this.params.rateAreaColor);
      color.saturateTo(temperature);
      var hex = color.toHex().substr(1); // remove #
      hex = '%' + hex.substr(0, 2) + '%' + hex.substr(2, 2) + '%' + hex.substr(4, 2);
      // create the image using a data url
      img.src = 'data:image/gif,GIF87a' // GIF header
        + '%01%00%01%00%80%00%00' // logical screen descriptor
        + '%FF%FF%FF' + hex.toUpperCase() // global color pallette
        + '%2C%00%00%00%00%01%00%01%00%00' // image header
        + '%02%02L%01%00' // image data (LZW)
        + '%3B'; // trailer
    }
    else { 
      // use a pre-created image and set opacity
      img.src = this.params.rateAreaImages[temperature >= 0 ? 1 : 0];
      Element.setOpacity(img, Math.abs(temperature));
    }
    img2.src = this.params.rateAreaBackgroundImage
    this.elMain.appendChild(div); 
    img.style.position = img2.style.position = 'absolute';
    img.style.top = img2.style.top = '0px';
    img.style.left = img2.style.left = '0px';
    Element.setSize(img, Element.getSize(div));
    div.appendChild(img);
    Element.setSize(img2, Element.getSize(div));
    div.appendChild(img2);
    return div;
  },
  createRateBar: function (left, pos) {
    var div = Element.newElement('DIV');
    div.className = pos == 0 ? this.params.barStyleFirst : pos < 0 ? this.params.barStyleLast : this.params.barStyle;
    div.style.left = Element.intToPx(left - Element.getSize(div).width / 2);
    this.elMain.appendChild(div);
  },
  createRateLabel: function (left, width, pct) {
    // Create a new rate area
    var div = document.createElement('DIV');
    div.className = this.params.rateLabelStyle;
    div.style.left = Element.intToPx(left);
    div.style.width = Element.intToPx(width);
    div.title = pct;
    div.innerHTML = pct;
    this.elRates.appendChild(div); 
  },
  createDollarLabel: function (left, label) {
    // Create a new rate area
    var div = document.createElement('DIV');
    div.className = this.params.labelStyle;
    if (isNaN(width)) width = 0;
    div.title = label;
    div.innerHTML = div.title;
    this.elRates.appendChild(div); 
    var width = Element.pxToInt(Element.findStyleProperty(div, 'width')); // IE needs this after adding to parent
    div.style.left = Element.intToPx(left - width / 2);
  },
  findRateGroup: function (dollars) {
    var rateGroup = null;
    for (var i = 0; i < this.rateGroups.length && rateGroup == null; i++) {
      if (dollars >= this.rateGroups[i].rate.sliderMin && dollars <= this.rateGroups[i].rate.sliderMax)
        rateGroup = this.rateGroups[i];
    }
    return rateGroup;
  },
  moveArrow: function (dollars) {
    if (typeof(dollars) != 'undefined') {
      dollars = Math.min(Math.max(dollars, this.minAmount), this.maxAmount);
      var rateGroup = this.findRateGroup(dollars),
          pos = -1000;
      if (!(this.arrowSize.width > 0))
        this.arrowSize = Floater.getSize(this.elArrow);
      if (rateGroup)
        pos = rateGroup.left + rateGroup.width / 2 - this.arrowSize.width / 2;
      Floater.setLeft(this.elArrow, pos);
    }
  },
  render: function () {
    // get valid rates
    this.minAmount = this.params.minAmount;
    this.maxAmount = this.params.maxAmount;
    this.validRates = this.getValidRates();
    // adjust max and min amounts
    if (this.validRates.length > 0) {
      var range1 = this.getRateRange(this.validRates[0]),
          range2 = this.getRateRange(this.validRates[this.validRates.length - 1]);
      this.minAmount = this.validRates[0].sliderMin;
      this.maxAmount = this.validRates[this.validRates.length - 1].sliderMax;
    }
    else {
      this.minAmount = 0;
      this.maxAmount = 0;
    }
    this.params.minAmount = this.minAmount;
    this.params.maxAmount = this.maxAmount;
    // create tick marks, dollar labels, etc.
    this.createRatesAreas();
    //this.createArrow();
    this.moveArrow(null);
  }
});
