//
// EndecaCatalog class.
// This class parses the Endeca JSON data into category, product, and sku objects,
// and maintains lists (hashes) for each type.  You can then access each list by id,
// such as:
//		var catObj = this.categoryList[catid];
// The structure of the lists/objects is essentially the same as the "CatProdData" class
// of the CL-US project.  Thus, each product object also contains a "skus" list which
// is a list of references to sku objects (also on the skuList) which are skus for that
// product.  Thus, when you get a product object from the productList, that product
// object will have a list of all it's skus (at least the skus that were included
// in the data).
//
// There is a "rec_type" property on each data record that indicates either "product"
// or "content".  "product" record types have cat/prod/sku data, and are stored as such.
// "content" records are not products, and the data for these records is stored
// essentially verbatim on the "contentList" hash.  Typically content records are
// things like articles and videos on the site.
//
// NOTE - this class is intended only to be a convenient container for the data
// returned from an Endeca query.  Please do not add page/state data to this class.
//
// A discussion of the Endeca data format is at the end of this file.
//	

var EndecaCatalog = Class.create({

	initialize: function(args) {
		
		this.categoryList = {};
		this.productList = {};
		this.skuList = {};
		this.contentList = $A();
		this.responseError = '';
		this.jsonResult = null;
		this.parseOrderHi = 0;
		this.parseOrderLo = 0;
		this.insert = false;
		
		// Load up passed params
        Object.extend(this, args || {});
        
        // If we had data given to us, handle it now.
        if ( this.jsonResult ) {
        	this.parseData(this.jsonResult,this.insert);
        }
	},
	
	parseData: function(jsonResult,insert) {
	  	// Check for errors
	  	if ( jsonResult.methodResponse &&
			 jsonResult.methodResponse.fault &&
			 jsonResult.methodResponse.fault.value &&
			 jsonResult.methodResponse.fault.value.faultString ) {
			
			this.responseError = jsonResult.methodResponse.fault.value.faultString;
	  	}
	  	
	  	if ( jsonResult.AggrRecords ) {
		  	// Dig out the aggregated records.
		  	// This actually merges the new records into our existing catalog, if present.
		  	jsonResult.AggrRecords.each ( function(aggrRec) {
		  		
		  		// Handle the main aggregate properties.
		  		// This is a "sample" sku record.
		  		this.parseERecord(aggrRec,insert);
		  		
		  		// All sub-records represent skus.
		  		aggrRec.Records.each ( function(skuRec) {
		  			this.parseERecord(skuRec,insert);
		  		}, this);
		  	}, this);
		} else if ( jsonResult.Records ) {
			jsonResult.Records.each ( function(skuRec) {
	  			this.parseERecord(skuRec,insert);
			}, this);
		}
	},
	
	parseERecord: function(endecaRecord,insert) {
		
		// If the record type is not "product", we assume it's
		// some kind of content and just save the whole record.
		// Else we'll pull it apart into cat/prod/sku data
		var recType = endecaRecord.Properties["rec_type"];
		if ( recType != 'product' ) {
			var recId = endecaRecord.Properties["rec_id"];
			if ( recId ) {
				//var recObj = this.contentList[recId] || {};
				//this.contentList[recId] = Object.extend(recObj,endecaRecord);
				
				this.contentList.push({
				    "image" : '',
				    "header_text" : endecaRecord.Properties.p_PROD_RGN_NAME,
				    "description" : endecaRecord.Properties.p_DESCRIPTION,
				    "link_url" : endecaRecord.Properties.p_url,
				    "link_text" : 'View more &raquo;'
                });
			}
		} else {
			// Must be a product...
	
			// The "Properties" hash is a list off all fields of a sku.
			// It actually contains cat, prod, and sku properties, each
			// of which is prefixed with "c_", "p_", and "s_", so we can pull them out.
			var catProps = {};
			var prodProps = {};
			var skuProps = {};
			
			// This is a list of properties that we always want to force to an integer value.
			// Note - in theory, this should be settable in the export process... TODO...
			var intProps = {
				"DISPLAY_ORDER":true,
				"shaded":true,
				"sized":true,
				"GIFTWRAP":true,
				"HAZARDOUS_PRODUCT":true,
				"REFILLABLE":true,
				"SUPRESS_IN_CART":true,
				"ONLY_RATINGS_COUNT":true,
				"PRODUCT_TYPE": true,
				"SKIN_TONE": true,
				"SKIN_TYPE": true,
				"TOTAL_REVIEW_COUNT": true,
				"INVENTORY_STATUS": true,
				"MISC_FLAG": true
			};
			
			// Props to force to float values
			var floatProps = {
				"AVERAGE_RATING": true
			};
			
			var splitRec = function(rec) {
				var key = rec.key.substr(2);
				var val = ( intProps[key] ? parseInt(rec.value) : 
				 			floatProps[key] ? parseFloat(rec.value) :
							rec.value );
				
				//Set up product tabs
				
				if (rec.key == 'p_tab_json' && val != "") {
				    prodProps['tabs'] = val.evalJSON();
				}
				
				if ( rec.key.indexOf ( "c_" ) == 0 ) {
					catProps[key] = val;
				} else if ( rec.key.indexOf ( "p_" ) == 0 ) {
					prodProps[key] = val;
				} else if ( rec.key.indexOf ( "s_" ) == 0 ) {
					skuProps[key] = val;
				} else if ( rec.key == "DGraph.WhyDidItMatch" ) {
				    prodProps['matched'] = 1;
                    skuProps['matched'] = 0;
                    skuProps['matchedOn'] = rec.value;
                    
                    [rec.value].each( function(matchedOn){
                        if ( matchedOn.indexOf ( "s_" ) == 0 ) {
    				        prodProps['matched'] = 0;
    				        skuProps['matched'] = 1;
    				    } 
                    });
				}
			};
			
			$H(endecaRecord.Properties).each ( splitRec, this);
			$H(endecaRecord.Dimensions).each ( splitRec, this);
			
			// Save the Endeca-specific info we care about.
			// First, do sku info.
			['Record Spec','Dimensions','Record Detail Link'].each ( function(prop) {
				skuProps[prop] = ( endecaRecord[prop] ? endecaRecord[prop] : '' );
			});
			
			// Check product props
			['Record Count','AggRec Detail Link'].each ( function(prop) {
				prodProps[prop] = ( endecaRecord[prop] ? endecaRecord[prop] : '' );
			});
			
			this.addProps ( catProps, prodProps, skuProps, insert );
		}
	},
	
	addProps: function ( catProps, prodProps, skuProps, insert ) {
		// For now, i'm using id's, but we may want to use the "path", which is more specific...
		var catId = catProps.CATEGORY_ID;
		var prodId = prodProps.PRODUCT_ID;
		var skuId = skuProps.SKU_ID;
		
		// I'm paranoid - check for id's
		if ( !catId || !prodId || !skuId ) return;
		
		// Insert/update sku object
		var skuObj = this.skuList[skuId] || {};
		this.skuList[skuId] = Object.extend(skuObj,skuProps);
		
		// If existing product record, use that and update.
		// Else create new one.
		var prodObj = this.productList[prodId] || { parseOrder: ++this.parseOrderHi };
		
		// If inserting, parse order should be negative.
		if ( insert && prodObj.parseOrder > 0 ) {
			prodObj.parseOrder = --this.parseOrderLo;
		}
		
		prodObj = Object.extend(prodObj,prodProps);
		if ( !prodObj.skus )
			prodObj.skus = [];
		if ( !prodObj.skuList )
			prodObj.skuList = {};
		// Make sure each sku is listed only once per product
		if ( !prodObj.skuList[skuId] ) 
			prodObj.skus.push(skuObj);
		prodObj.skuList[skuId] = skuObj;
		this.productList[prodId] = prodObj;
		
		var catObj = this.categoryList[catId] || {};
		catObj = Object.extend(catObj,catProps);
		if ( !catObj.prods ) catObj.prods = [];
		catObj.prods.push(prodObj);		
		this.categoryList[catId] = catObj;
	},
	
	// Return an array of product objects, sorted by parseOrder
	getProducts: function() {
		var prods = [];
		$H(this.productList).sortBy( function(ps) { 
			return ps.value.parseOrder; 
		}).each ( function(p) {
		    var skus = [];
		    p.value.skus.sortBy(function(ss) {
		        return ss.DISPLAY_ORDER;
            }).each(function(s) {
                skus.push(s);
            });
            p.value.skus = skus;
            
            prods.push(p.value); 
		});
		/*
		$H(this.productList).each( function(p) {
			prods.push(p.value); 
		});
		*/
		
		return prods;
	},
	
	// Return an array of sku objects
	getSkus: function() {
		var skus = [];
		$H(this.skuList).each( function(s) {
			skus.push(s.value);
		});
		return skus;
	},
	
	getCategory: function(catid) {
		var catObj = ( this.categoryList ? this.categoryList[catid] : null );
		return catObj;
	},
	
	getProduct: function(prodid) {
		var prodObj = ( this.productList ? this.productList[prodid] : null );
		return prodObj;
	},
	
	getSku: function(skuid) {
		var skuObj = ( this.skuList ? this.skuList[skuid] : null );
		return skuObj;
	},
	
	productCount: function() {
		return $H(this.productList).size();
	},
	
	contentCount: function() {
	    this.contentList.size();
		//return $H(this.contentList).size();
	},
	
	recordCount: function() {
		return this.productCount() + this.contentCount();
	}
	
});


/*
The Endeca data arrives as a JSON hash.  Each record in the hash is a complete sku record,
including all the category, product, and sku properties for that sku.

When we request the data, we request a rollup on PRODUCT_ID.  This groups the
data by product.  The "parent" product is called the "Aggregate Record" (in Endeca-land).
Thus, in the hash, the first sku is the "AggrRecord" (which is a sku representative of
the rolled-up product record), and all the other skus (if any) then follow that record.

Because Endeca flattens the entire database into sku-specific records, before sending
the data to Endeca, we pre-pend each property name with a "c_", "p_", or "s_", to indicate
if that property is a Category, Product, or Sku property (respectively). Then, when
we parse out each Endeca record, we can split the record into category, product, and
sku properties.  As we parse the data, we create a category, product, and sku object
for each sku, and then merge that object into the list of cats/prods/skus in our
EndecaCatalog class.

Note that because a product often has multiple skus, we will see the same product id
more than once as we process those skus.  Thus, we want to "update" the product record
in our list with the new sku info, and not just "add" the product record (since that would
potentially create duplicate product records).  A similar scenario may occur if a
product belongs to more than one category.

Here is a very simplified synopsys of the Endeca data JSON format:

	AggrRecords: [
		{	-- new product
			Records: [
				{
					Properties: {
						c_*
						p_*
						s_*
					}
				}
				{
					Properties: {
						c_*
						p_*
						s_*
					}
				}
			]
		}
		{	-- new product
			Records: [
				{
					Properties: {
						c_*
						p_*
						s_*
					}
				}
				{
					Properties: {
						c_*
						p_*
						s_*
					}
				}
			]
		}
	]
*/
	

