// Behavior for Product Options overlay.
var ProductSelector = function() {
  this.initialProductID   = $('initial_product_id').value;
  this.displayOptions     = this.initDisplayOptions();;

  this.selectedOptions    = {};
  this.availableOptions   = {};
  this.data               = {};
  this.initialize();
};

ProductSelector.prototype = {

  // Get the product database, passing on the setup callback.
  initialize: function() {
    this.fetchProductDatabase(this.setup.bind(this));
  },

  // Select the initial product, activate the product options, and add events.
  setup: function() {
    this.selectProduct(this.initialProductID);
    this.activateProductOptions(this.selectedOptions);
    this.addEventHandlers();
  },

  // Find out which features the options overlay is using.
  initDisplayOptions: function() {
    var productFeatures = [];
    $$('.product_option').each(function(input) {
      var feature = input.identify().split('__')[0];
      if(productFeatures.indexOf(feature) == -1)
        productFeatures.push(feature);
    });

    return productFeatures;
  },
         
  // Called when the overlay is reopened, so that options 
  // are reset to the current product.
  reset: function() {
    $$('.highlight').each(function(el) {
      el.removeClassName('highlight');
    });
    this.selectProduct(this.initialProductID); 
    this.activateProductOptions(this.selectedOptions);
  },

  // Gets the product database and then invokes this.setup.
  fetchProductDatabase: function(callback) {
    var url     = '/store/product_options/' + this.initialProductID;
    var request = new Ajax.Request(url ,{
      method: 'get',
      onComplete: function(transport) {
        this.data = transport.responseJSON;   
        callback();
      }.bind(this)
    });
  },

  // Given a product ID, selected that product.
  selectProduct: function(productID) {
    var params  = {'id': productID};
    var product = this.findFirstRecord(params);
    this.displayOptions.each(function(option) {
      var optionID = option + "__" + product[option];
      var optionElement = $(optionID);
      if(optionElement) {
        this.highlightOption(optionElement);
        this.activateOption(optionElement);
        this.selectedOptions[option] = product[option];
      }
    }.bind(this));
  },

   // Based on the selected options passed in, activate 
  // and deactive remaining products based on availability.
  activateProductOptions: function(selectedOptions) {
    $$('.product_option').each(function(el) {
      var opts = this.optionsFromElement(el);

      // If this option isn't selected, determine if a product exists
      // where this element can be combined with the other three selected elements.
      if(selectedOptions[opts.type] != opts.value) {
        var searchParams   = {};
        searchParams[opts.type] = opts.value; 

        this.displayOptions.each(function(displayOption) {
          if(displayOption != opts.type) {
            searchParams[displayOption] = selectedOptions[displayOption];
          }
        }.bind(this));
        
        if(this.findFirstRecord(searchParams)) {
          this.activateOption(el);
        }
        else {
          this.deactivateOption(el);
        }

      }
      else {
      }
    }.bind(this));

    this.updateProductDetails(selectedOptions);
  },

  // Extracts an option type and value from the element's id.
  optionsFromElement: function(el) {
    var element = this.getSurroundingLI(el);
    var options = element.id.split('__');      
    var type    = options[0];
    var value   = options[1];

    return {type: type, value: value};
  },

  // Merges selected options with current options. Then activates.
  updateSelectedOptions: function(el) {
    var opts     = this.optionsFromElement(el);
    var selected = {};
    this.displayOptions.each(function(displayOption) {
      if(opts.type == displayOption) {
        selected[displayOption] = opts.value;
      }
      else {
        selected[displayOption] = this.selectedOptions[displayOption];
      }
    }.bind(this));

    this.activateProductOptions(selected);
  },

  // Updates name, price, and url of product.
  updateProductDetails: function(selectedOptions) {
    var product = this.findFirstRecord(selectedOptions);  
    $('overlay_product_url').href = product.url;
    $('overlay_product_name').update(product.name + "<em>$" + product.price + "</em>");
  },

  // Invoked on click: updates selected options and available products.
  changeProductSelection: function(el) {
    var opts = this.optionsFromElement(el);
    var oldElement = opts.type + "__" + this.selectedOptions[opts.type];
    this.unhighlightOption($(oldElement));
    this.selectedOptions[opts.type] = opts.value;

    var product = this.findFirstRecord(this.selectedOptions);
    this.selectProduct(product.id);
    this.updateProductDetails(this.selectedOptions);
  },

  // Searches json database for a record with given attributes.
  //   Example: this.findFirstRecord({finish: 'Nickel', diameter: '8.5'});
  findFirstRecord: function(attributes) {
    for(var index = 0, len = this.data.length; index < len; ++index) {

      var found   = 0;
      var checked = 0;
      for(var prop in attributes) {
        if(attributes.hasOwnProperty(prop)) {
          checked += 1;
          if(this.data[index][prop] == attributes[prop]) {
            found += 1;
          }
        }
      }

      if(found == checked) {
        return this.data[index];
      }
    }
    
    // No matching record found.
    return false;
  },

  // Returns a product option li, in case event
  // returns on the of the li's child elements.
  getSurroundingLI: function(el) {
    if(el.hasClassName('product_option')) {
      return el;
    }
    else {
      return el.up('li');
    }
  },

  // Given an Event object, returns the needed containing element (li).
  getElement: function(ev) {
    var element = Event.element(ev);
    return this.getSurroundingLI(element);
  },

  activateOption: function(element) {
    element.removeClassName('inactive');
  },

  deactivateOption: function(element) {
    element.addClassName('inactive');
  },

  highlightOption: function(element) {
    element.addClassName('highlight');
  },

  unhighlightOption: function(element) {
    element.removeClassName('highlight');
  },

  changeCursor: function(element, value) {
    element.setStyle({cursor: value});
  },
  
  // The element is not inactive.
  elementAvailable: function(element) {
    if(element.hasClassName('inactive')) {
      return false;
    }
    else {
      return true;
    }
  },

  // Attach mouseover, mouseout, and click handlers.
  addEventHandlers: function() {
    $$('.product_option').each(function(element) {

      element.observe('mouseover', function(ev) {
        var el = this.getElement(ev);
        if(this.elementAvailable(el)) {
          this.changeCursor(el, 'pointer');
          this.updateSelectedOptions(el);
        }
      }.bind(this));

      element.observe('mouseout', function(ev) {
        var el = this.getElement(ev);
        if(this.elementAvailable(el)) {
          this.changeCursor(el, 'default');
        }
        this.activateProductOptions(this.selectedOptions);
      }.bind(this));

      element.observe('click', function(ev) {
        var el = this.getElement(ev);
        if(this.elementAvailable(el)) {
          this.changeProductSelection(el);
        }
      }.bind(this));

    }.bind(this));
  }
};

// Returns the proper product options overlay via ajax.
var ProductSelectorInitializer = function(options) {
  this.initializers = $$('.' + options.linkClass);
  this.spinners     = $$('.' + options.spinnerClass);
  this.productID    = $(options.productID).value;
  this.baseURL      = options.baseURL;
  var selector      = null;

  this.initialize();
};

ProductSelectorInitializer.prototype = {

  initialize: function() {
    this.initializers.each(function(element) {
      element.observe("click", this.initOverlay.bindAsEventListener(this));
    }.bind(this));
  },

  initOverlay: function(ev) {
    if($('overlay_options')) {
      this.popup.open();
      selector.reset();
    }
    else {
      this.getOverlay(ev);
    }
  },

  // Return the product options overlay, and initialize the ProductSelector.
  getOverlay: function(ev) {
    var context = this;
    var element = $('content_body');
    var url     = this.baseURL + this.productID + '/overlay';

    var request = new Ajax.Request(url, {
      method: 'get',
      onLoading: function() {
        context.spinners.each(function(el) { el.show(); });
      },

      onSuccess: function(transport) {
        element.insert({after: transport.responseText}); 
      },

      onComplete: function() {
        // Shut down the spinners,
        context.spinners.each(function(el) { el.hide(); });

        // Pop open the overlay (CustomPopUp defined in generic.js.),
		    context.popup = new CustomPopUp($('overlay_options'), { modal: true });
        context.popup.open();

        // And initialize the ProductSelector.
        selector = new ProductSelector();
      }
    });
  }

};


document.observe('dom:loaded', function() {
  var selectorInit = new ProductSelectorInitializer({linkClass: 'select_initializer', 
    productID: 'product_id', spinnerClass: "spinner", baseURL: '/store/product_options/'});
});
