
/*
Group: gucci-data.js
  Gucci data layer.
  
Note:
  uniqe identifiers:
    gucci.Data.panels                               panel_id
    gucci.Data.looks                                id missing
    gucci.Data.<looks/panels>[xyz].displayGroups    link_id
*/

/*
Class: gucci.Data
  A data container for json data object responsed by the gucci webserver.

Note:
  static class

Example:
  (start code)
  var jsonObj = {....} // JSON object which contains displayGroups data.
  gucci.Data.setPanels(jsonObj);
  (end)
*/
gucci.Data = {
  panels: {},
  //currently no "look_id" so use an array instead of a Object aka. HashMap
  looks: {},
  look_counter: 0,
  
  addPanel: function(panel) {
    if (this.panels[panel.panel_id] instanceof gucci.Panel) {
      this.panels[panel.panel_id].updateDisplayGroups(panel.displayGroups);
    }
    this.panels[panel.panel_id] = new gucci.Panel(panel);
  },
  
  addLook: function(look) {
    //HACK no look_id yet
    this.looks[look.look_id] = new gucci.Look(look);
  }
  
};

/*
Class: gucci.DataHelper
  helper functions for <gucci.Data>. Some of them are hacks.

Note:
  static class

Example:
  (start code)
  var sorted = gucci.DataHelper.getOrderedDisplayGroups('1568');
  (end)
*/
gucci.DataHelper = {
  
  getPanelsDisplayGroups: function() {
    var displayGroups = {};
    Object.keys(gucci.Data.panels).each(function(key) {
      Object.extend(displayGroups, gucci.Data.panels[key].displayGroups);
    });
    return displayGroups;
  },
  
  getLooksDisplayGroups: function() {
    var displayGroups = {};
    Object.keys(gucci.Data.looks).each(function(key) {
      Object.extend(displayGroups, gucci.Data.looks[key].displayGroups);
    });
    return displayGroups;
  },
  
  getOrderedDisplayGroups: function(category) {
    var sorted = {
      withCategory: new Hash(),
      withoutCategory: new Hash(),
      allProducts: new Hash()
    };
    var displayGroups = this.getPanelsDisplayGroups();
    if (category == "all") {
      sorted.withCategory = $H(displayGroups);
    } else {
      category = parseInt(category, 10);
      Object.keys(displayGroups).each(function(key) {
        var displayGroup_id = key;
        var displayGroup = displayGroups[key];

        if (displayGroup.leadStyle.categories.indexOf(category) == -1) {
          sorted.withoutCategory.set(displayGroup_id, displayGroup);
        } else {
          sorted.withCategory.set(displayGroup_id, displayGroup);
        }
      });
    }
    sorted.allProducts = sorted.withCategory.clone();
    sorted.withoutCategory.each(function(pair) {
      sorted.allProducts.set(pair.key, pair.value);
    });
    return sorted;
  },
  
  /*
  Property:
    getLookDisplayGroup
  
  Note:
    Loops throu all <gucci.DisplayGroup>s of all <gucci.Look>s and 
    returns the first with the specified link_id.
  
  */
  getLookDisplayGroup: function(searched_displayGroup_id) {
    var foundDisplayGroup = false;
    var keys = Object.keys(gucci.Data.looks);

    for (var i=0; i < keys.length; i++) {
      if (gucci.Data.looks[keys[i]].displayGroups[searched_displayGroup_id]) {
        foundDisplayGroup = gucci.Data.looks[keys[i]].displayGroups[searched_displayGroup_id];
        return foundDisplayGroup;
      }
    }
  },

  updateLookDisplayGroup: function(newDisplayGroup) {
    var key          = Object.keys(newDisplayGroup)[0];
    var displayGroup = gucci.DataHelper.getLookDisplayGroup(key);
    displayGroup.update(newDisplayGroup);
  },
  
  updatePanelDisplayGroup: function(displayGroup_id, newDisplayGroup) {
    var displayGroup = gucci.DataHelper.getPanelDisplayGroup(displayGroup_id);
    displayGroup.update(newDisplayGroup);
  },
  
  updatePanelDisplayGroups: function(newDisplayGroups) {
    Object.keys(newDisplayGroups).each(function(key) {
      this.updatePanelDisplayGroup(key, newDisplayGroups[key]);
    }.bind(this));
  },
  
  getPanelDisplayGroup: function(searched_displayGroup_id) {
    var foundDisplayGroup = false;
    var keys = Object.keys(gucci.Data.panels);
    
    //HACK serverside uses this for looks too so check if there are panels.
    //if not use the looks as fallback
    if (keys.length == 0) {
      keys = Object.keys(gucci.Data.looks);
      for (var i=0; i < keys.length; i++) {
        if (gucci.Data.looks[keys[i]].displayGroups[searched_displayGroup_id]) {
          foundDisplayGroup = gucci.Data.looks[keys[i]].displayGroups[searched_displayGroup_id];
          return foundDisplayGroup;
        }
      }
    } else {
      for (var i=0; i < keys.length; i++) {
        if (gucci.Data.panels[keys[i]].displayGroups[searched_displayGroup_id]) {
          foundDisplayGroup = gucci.Data.panels[keys[i]].displayGroups[searched_displayGroup_id];
          return foundDisplayGroup;
        }
      }
    }
  },
  
  getPanelDisplayGroupByLinkId: function(searched_link_id) {
    var panelKeys = Object.keys(gucci.Data.panels);

    for (var i=0; i < panelKeys.length; i++) {
      var displayGroupKeys = Object.keys(gucci.Data.panels[panelKeys[i]].displayGroups);
      for (var j=0; j < displayGroupKeys.length; j++) {
        if (gucci.Data.panels[panelKeys[i]].displayGroups[displayGroupKeys[j]].link_id == searched_link_id) {
          return gucci.Data.panels[panelKeys[i]].displayGroups[displayGroupKeys[j]];
        }
      }
    }
  },
  
  getPanelDisplayGroupByStyleNumber: function(searched_style_number) {
    var panelKeys = Object.keys(gucci.Data.panels);
    
    for (var i=0; i < panelKeys.length; i++) {
      var displayGroupKeys = Object.keys(gucci.Data.panels[panelKeys[i]].displayGroups);
      for (var j=0; j < displayGroupKeys.length; j++) {
        if (gucci.Data.panels[panelKeys[i]].displayGroups[displayGroupKeys[j]].leadStyle.style_number == searched_style_number) {
          return gucci.Data.panels[panelKeys[i]].displayGroups[displayGroupKeys[j]];
        }
      }
    }
  },
  
  getLookDisplayGroupByLinkId: function(searched_link_id) {
    var panelKeys = Object.keys(gucci.Data.looks);

    for (var i=0; i < panelKeys.length; i++) {
      var displayGroupKeys = Object.keys(gucci.Data.looks[panelKeys[i]].displayGroups);
      for (var j=0; j < displayGroupKeys.length; j++) {
        if (gucci.Data.looks[panelKeys[i]].displayGroups[displayGroupKeys[j]].link_id == searched_link_id) {
          return gucci.Data.looks[panelKeys[i]].displayGroups[displayGroupKeys[j]];
        }
      }
    }
  },
  
  getLookDisplayGroupByStyleNumber: function(searched_style_number) {
    var panelKeys = Object.keys(gucci.Data.looks);

    for (var i=0; i < panelKeys.length; i++) {
      var displayGroupKeys = Object.keys(gucci.Data.looks[panelKeys[i]].displayGroups);
      for (var j=0; j < displayGroupKeys.length; j++) {
        if (gucci.Data.looks[panelKeys[i]].displayGroups[displayGroupKeys[j]].leadStyle.style_number == searched_style_number) {
          return gucci.Data.looks[panelKeys[i]].displayGroups[displayGroupKeys[j]];
        }
      }
    }
  },
  
  addPanelDisplayGroups: function(json) {
    if (!gucci.Data.panels['0']) gucci.Data.panels['0'] = new gucci.Panel({panel_id: '0'});
    gucci.Data.panels['0'].setDisplayGroups(json);
  }
  
};


/*
Class: gucci.Look
  Model of Panel
*/
gucci.Panel = Class.create({
  _parent: gucci.Data,
  
  initialize: function(json) {
    this.panel_id = json.panel_id;
    this.displayGroups = {};
    if (json.displayGroups) this.setDisplayGroups(json.displayGroups);
  },
  
  setDisplayGroups: function(displayGroups) {
    Object.keys(displayGroups).each(function(key) {
      this.setDisplayGroup(key, displayGroups[key]);
    }.bind(this));
  },
  
  setDisplayGroup: function(displayGroup_id, displayGroup) {
    displayGroup.displayGroup_id = displayGroup_id;
    this.displayGroups[displayGroup_id] = new gucci.DisplayGroup(displayGroup, this);
  },
  
  updateDisplayGroups: function(displayGroups) {
    Object.keys(displayGroups).each(function(key) {
      this.displayGroups[key].update(displayGroups[key]);
    }.bind(this));
  }
  
});


/*
Class: gucci.Look
  Model of Look/RTW Panel
*/
gucci.Look = Class.create(gucci.Panel, {
  _parent: gucci.Data,
  
  initialize: function(json) {
    this.look_id = json.look_id || json.panel_id;
    this.panel_id = json.panel_id || json.look_id;
    this.images = json.images;
    
    this.displayGroups = {};
    
    this.systemtexts = json.systemtexts;
    this.templates = json.templates;
    
    this.setDisplayGroups(json.displayGroups);
  }
  
});


/*
Class: DisplayGroup
  Maps the server side DisplayGroup class. A container for leadProduct (<Product>),
  its variations (Array() of <Product>s) and accessories.

Example:
  (start code)
  var obj = {....} //"json"-object with displayGroups data inside.
  var myDisplayGroup = gucci.Data.looks['123'].displayGroups['234324'];
  myDisplayGroup.update(obj);
  (end)
*/
gucci.DisplayGroup = Class.create({
  
  initialize: function(json, parent) {
    this._parent                           = parent;
    this.link_id                           = json.link_id;
    this.displayGroup_id                   = json.displayGroup_id;
    this.path                              = json.path;

    json.leadStyle.variation_index         = 0;
    this.leadStyle                         = new gucci.Variation(json.leadStyle, this);

    this.accessories                       = [];
    this.variations                        = [];
    this.templates                         = {};
    this.systemtexts                       = {};

    if (json.variations) this.setVariations(json.variations);
    if (json.accessories) this.setAccessories(json.accessories);

    if (json.templates) this.templates     = json.templates;
    if (json.systemtexts) this.systemtexts = json.systemtexts;
  },
  
  update: function(json) {
    if (json.path) this.path = json.path;
    
    // especially for search it can be possible that the leadstyle which is present in the html script is different from the leadstyle delivered by the product file
    // therefore check if leadstyle is present in the incoming variations array 
    
    var leadDifference=false;
    if (json.leadStyle) {
      if (json.leadStyle.style_number!=this.leadStyle.style_number)
      for (var idx=0;idx<json.variations.length;idx++) {
        if (this.leadStyle.style_number==json.variations[idx].style_number) {
          leadDifference=true;
          this.leadStyle = new gucci.Variation(json.leadStyle, this);
          this.leadStyle.variation_index=idx+((json.variations[idx].style_number == json.leadStyle.style_number) ? 0 : 1);
          break; 
        }
      }
      if (!leadDifference) {
        json.variation_index=0; // currently never included in the javascript   
        this.leadStyle.update(json.leadStyle);
      }
    } 
    
    if (json.variations) {
      var reorderarr=[];
      for (var j=0;(j<this.variations.length) && (json.leadStyle);j++) {
        if (this.variations[j].style_number==json.leadStyle.style_number) {
          // leadStyle is in variation, therefore update it
          this.variations[j].update(json.leadStyle);
					
          reorderarr.push(this.variations[j]);
          break;
        }
      }
      for (var k=0;k<json.variations.length;k++) 
        for (var j=0;((j<this.variations.length) && (json.leadStyle));j++) {
          if ((this.variations[j].style_number==json.variations[k].style_number) && (this.variations[j].style_number!=json.leadStyle.style_number)) {
            reorderarr.push(this.variations[j]);
            break;
          }
        }
      this.variations=reorderarr;
			
      this.updateVariations(json.variations);
    } 
    if (json.accessories) this.updateAccessories(json.accessories);

    //display accessories additionally as variations ...
    if (json.accessories) this.updateVariations(json.accessories);
    
    if (json.templates) Object.extend(this.templates, json.templates);
    if (json.systemtexts) Object.extend(this.systemtexts, json.systemtexts);
  },
  
  updateVariations: function(variations) {
    var newVariations = [];
    //update existing variations
    for (var i = 0; i < variations.length; i++) {
      var updated = false;
      for (var j = 0; j < this.variations.length; j++) {
        if (this.variations[j].style_number == variations[i].style_number) {
          this.variations[j].update(variations[i]);
          updated = true;
          break;
        }
      }
      if (!updated) {
		var newvar = variations[i];
		// parse new variation
		newvar.text.vardesc = newvar.text.vardesc.replace(/\*(.+<\/li>)/g,'</li><li>$1');
		newvar.text.grpdesc = gucci.OverlayParser.parseText(newvar.text.grpdesc);
		newvar.text.vardesc = gucci.OverlayParser.parseText(newvar.text.vardesc);
		newVariations.push(newvar);
      }
    }
    //add new variations
    this.setVariations(newVariations);
  },
  
  updateAccessories: function(accessories) {
    for (var i = 0; i < accessories.length; i++) {
      for (var j = 0; j < this.accessories.length; j++) {
        if (this.accessories[j].style_number == accessories[i].style_number) {
          this.accessories[j].update(accessories[i]);
        }
      }
    }
  },
  
  setVariations: function(variations) {
    var isLeadStyleInVariations = false;
    for (var i = 0; i < variations.length; i++) {
      if (this.leadStyle.style_number != variations[i].style_number) {
        variations[i].variation_index = i + (isLeadStyleInVariations ? 0 : 1);
        this.variations.push(new gucci.Variation(variations[i], this));
      } else {
        isLeadStyleInVariations = true;
      }
    }
    isLeadStyleInVariations = false;
    for (var i = 0; i < this.variations.length; i++) {
      if (this.leadStyle.style_number == this.variations[i].style_number) {
        isLeadStyleInVariations = true;
      }
    }
    if (!isLeadStyleInVariations) {
      this.variations.unshift(this.leadStyle);
    }
  },
  
  setAccessories: function(accessories) {
    accessories.each(function(accessory) {
      this.accessories.push(new gucci.Accessory(accessory, this));
    }.bind(this));
  },
  
  getUpdateUrl: function() {
    //NOTE old version, maybe we need it in future
    return '/'+Cookie.get('site')+'/product-shots/'+Cookie.get('language')+'/'+Cookie.get('site')+'/'+this.link_id+'.asp';
    // return '/'+Cookie.get('site')+'/product-shots/'+Cookie.get('language')+'/'+Cookie.get('site')+'/'+this.displayGroup_id+'.asp';
  },
  
  getDetails: function(onDataUpdated, fromDeepLink) {
    var url = this.getUpdateUrl();
    
    new Ajax.Request(url, {
      method: 'get',
      onComplete: function(transport, json) {
        //the response is js and calls displayGroup.update({...});
        if (transport.status == 200 && Object.isFunction(onDataUpdated)) {
          onDataUpdated(this, fromDeepLink);
        }
      }.bind(this),
      onFailure: function(err) {},
      onException: function(req, err) {
        if (Prototype.Browser.IE)
          alert(Object.toJSON(err));
        else
          console.error(err);
      }
    });
  },
  
  getProduct: function(searched_style_number) {
    if (this.leadStyle.style_number == searched_style_number) {
      return this.leadStyle;
    }
    for (var i = 0; i < this.variations.length; i++) {
      var variation = this.variations[i];
      if (variation.style_number == searched_style_number) {
        return variation;
      }
    }
  },
  
  /*
  Property: getAllThumbUrls
    Gets thumbnail urls of leadStyle and all variations.

  Returns:
    Array of url strings.
  */
  getAllThumbUrls: function() {
    return this.getVariationsWithLeadStyleFirst().map( function(variation) {
      return variation.images.panel_thumb;
    });
  },

  /*
  Property: getVariationsWithLeadStyleFirst
    Creates Array containing leadStyle and all variations (=<Product>s).

  Note:
    Checks if Array items (=<Product>s) are unique.

  Returns:
    Array of <Product>s.
  */
  getVariationsWithLeadStyleFirst: function() {
    var variations = [this.leadStyle];
    this.variations.each(function(variation) {
      if (variation.style_number != this.leadStyle.style_number) {
        variations.push(variation);
      }
    }.bind(this));
    return variations;
  },

  /*
  Property: getVariationsWithoutLeadStyle
    Creates Array containing all variations (=<Product>s) without the leadstyle.

  Note:
    Checks if Array items (=<Product>s) are unique.

  Returns:
    Array of <Product>s.
  */
  getVariationsWithoutLeadStyle: function() {
    var variations = this.variations.select(function(variation) {
      return variation.style_number != this.leadStyle.style_number;
    }.bind(this));
    return variations;
  },

  /*
  Property: getStyleNumbersWithLeadStyleFirst
    Creates Array containing leadStyle and all variations (=<Product>s).

  Returns:
    Array of of <Product>s.
  */
  getStyleNumbersWithLeadStyleFirst: function() {
    return this.getVariationsWithLeadStyleFirst().map(function(variation) {
      return variation.style_number;
    });
  },
  
  getThumbIndexForVariation: function(variation_style_number) {
    var index = null;
    this.getVariationsWithLeadStyleFirst().each(function(variation, i) {
      if (variation.style_number == variation_style_number) {
        index = i;
        $break;
      } 
    });
    return index;
  }
  
});

/*
Class: Product
  Creates a Product. Maps the server side Product class. 

Arguments:
  data - the "json" data responsed by the server
  displayGroup - the <Product.displayGroup> used for rendering engine.
*/

gucci.Product = Class.create({
  initialize: function(json, parent) {
    this._parent = this.displayGroup = parent;
    this.skus = [];
    this.text = {};
    this.images = {};
    this.variation_index = json.variation_index;
    this.style_number = json.style_number;
    
    if (json.skus) {
      this.addSkus(json.skus);
      delete json.skus;
    }
    Object.extend(this, json);
    if (this.images.rtw_zoom) {
      this.images.zoom = this.images.rtw_zoom;
    }
    
    this.setDeepLink();
  },
  
  setDeepLink: function() {
    this.deep_link = (this._parent._parent.panel_id || this._parent._parent.look_id || 0) + '-' 
                     + this._parent.displayGroup_id + '-' 
                     + this.style_number;
  },
  
  update: function(newProduct,parseText) {
	if (parseText == null)
		parseText = true;
    // Gucci search uses different thumbnails eg. Alter 3 
    // this code should not be necessary here, but there was no choice...
    var obj = {};
    Object.extend(obj,newProduct);    

    if ((this.images) && (obj.images) && (typeof(GucciSearch) != 'undefined')) {
      if (this.images["panel_thumb"]) {
        newProduct.images["panel_thumb"]=this.images["panel_thumb"];
      }
    }    
    
    //HACK overwrite skus. they doesn't get merged yet.
    if (obj.skus) {
      this.updateSkus(obj.skus);
      delete obj.skus;
    }
	if (parseText) {
		// remove asterisks, convert to <li>
		obj.text.vardesc = obj.text.vardesc.replace(/\*(.+<\/li>)/g,'</li><li>$1');
		obj.text.grpdesc = gucci.OverlayParser.parseText(obj.text.grpdesc);
		obj.text.vardesc = gucci.OverlayParser.parseText(obj.text.vardesc);
	}
    Object.extend(this.text, obj.text);
    delete obj.text;
    Object.extend(this.images, obj.images);
    delete obj.images;
    if(this.images.rtw_zoom) {
      this.images.zoom = this.images.rtw_zoom;
    }
    Object.extend(this, obj);
  },
  
  addSkus: function(skus) {
    for (var i=0; i < skus.length; i++) {
      this.addSku(skus[i]);
    }
  },
  
  addSku: function(sku) {
    this.skus.push(new gucci.Sku(sku, this));
  },
  
  updateSkus: function(newSkus) {
    for (var i=0; i < newSkus.length; i++) {
      var oldSku = this.getSku(newSkus[i].sku);
      if (oldSku) {
        oldSku = new gucci.Sku(newSkus[i], this);
      } else {
        this.addSku(newSkus[i]);
      }
    }
  },
  
  /*
  Property: getStyleNumber
    returns a formatted stylenumber
  
  Arguments:
    delimiter - a delimiter string used to format <Product.style_number>, e.g. "_" => "194315_EDH00_6206"
  
  Returns:
    formatted <Product.style_number>
  */
  formatStyleNumber: function(delimiter) {
    if (delimiter) {
      return this.style_number.replace(/(.{6})(.{5})(.{4})/, '$1'+delimiter+'$2'+delimiter+'$3');
    } else {
      return this.style_number;
    }
  },
  
  getSku: function(searched_sku_number) {
    for (var i=0; i < this.skus.length; i++) {
      var sku = this.skus[i];
      if (sku.sku == searched_sku_number) {
        return sku;
      }
    }
    return false;
  },
  
  toQueryString: function() {
    //FIXME stuff missing
    return "";
  }
  
});

gucci.OverlayParser = {
	_iconText: null,
	_iconTerms: null,
	
	getIconSearchTerms: function() {
		new Ajax.Request('/json/icontext.js',{ method: 'get',
			onSuccess: function(transport){
				gucci.OverlayParser._iconText = transport.responseText.evalJSON(true);	
				var iconlib = gucci.OverlayParser._iconText[decodeURI(Cookie.get('language'))];
				if (typeof iconlib == 'undefined' || !iconlib.terms) return;
				gucci.OverlayParser._iconTerms = iconlib.terms;
			},
			onFailure: function(transport){
				alert('failure:' + transport.status);
			}
		});
	},
	
	parseText: function(textstring) {
		if (!this._iconTerms || (textstring.indexOf('showIconOverlay') > -1))
			return textstring;
		var re = new RegExp('(\\W+)(' + this._iconTerms + ')(\\W+)','ig');
		textstring = textstring.replace(re,'$1<a class=\'iconlink\' href=\'#\' onclick=\'Shop.showIconOverlay("$2",this); return false;\'>$2</a>$3');
		return textstring;
	}
};


gucci.Variation = Class.create(gucci.Product, {});

gucci.Accessory = Class.create(gucci.Product, {});

gucci.Sku = Class.create({
  initialize: function(json, parent) {
    Object.extend(this, json);
    this._parent = parent;
    this.setDeepLink();
  },
  
  setDeepLink: function() {
    this.deep_link = (this._parent._parent._parent.panel_id || this._parent._parent._parent.look_id || 0) + '-' 
                     + this._parent._parent.displayGroup_id + '-' 
                     + this._parent.style_number + '-' 
                     + this.sku;
  }
});

/*
Class: Partials
  Rendering engine.
  
Note:
  static class
*/
gucci.Partials = {
  
  /*
  Property: renderTemplate
    recursively renders a template string
    
  Arguments:
    template - the template which should evaluate the data from an eval_object
    eval_object - an object which contains data to fill a template
  
  Returns:
    a rendered template
  */
  renderTemplate: function(template, eval_object) {
    var _template = new Template(template.templateString);
    if (template.collection) {
      template.renderedString = '';
      var render_count = 1;
      // maybe find a more elegant solution to check if it's a string or an array
      template.collection = template.collection.split ? template.collection.split(".") : template.collection;
      var collection = eval_object;
      template.collection.each(function(obj) {
        collection = collection[obj];
      });
      if (!(collection instanceof Array)) {
        Object.keys(collection).each(function(key) {
          this.missingRendering(template, collection[key]);
          template.renderedString = [template.renderedString,
            _template.evaluate(Object.extend(collection[key], {render_count: render_count, render_index: render_count - 1}))
            ].join('');
          render_count++;
        }.bind(this));
      } else {
        collection.each(function(collectionObject) {
          this.missingRendering(template, collectionObject);
          template.renderedString = [ template.renderedString,
            _template.evaluate(Object.extend(collectionObject, {render_count: render_count}))
            ].join('');
          render_count++;
        }.bind(this));
      }
      return template.renderedString;
    }
    else {
      this.missingRendering(template, eval_object);
      template.renderedString = _template.evaluate(eval_object);
      return template.renderedString;
    }
  },
  
  /*
  Property: missingRendering
    
  Arguments:
    template
    eval_object
  */
  missingRendering: function(template, eval_object) {
    if (template.templates) {
      Object.keys(template.templates).each(function(key){
        if(eval_object._templates)
          Object.extend(eval_object._templates, template.templates);
        else
          eval_object._templates = template.templates;
        this.renderTemplate(template.templates[key], eval_object);
      }.bind(this));
    } 
  }
  
};

Element.observe(document,"dom:loaded", function() { gucci.OverlayParser.getIconSearchTerms(); });