/*********************************************************************************/
/**			copyright (c) PlanetCalc.com, 2007  			**/
/**		    	Recordset class					 	*/
/*********************************************************************************/
function GetObjectPropertiesCount( obj ) {
	var total = 0;
	for( var Id in obj ) {
		++total;		
	}
	return total;
}

/// Recordset is a list of objects (records). Recordset can load its state from or save to json.
function Recordset( data, idPropertyName ) {
	var m_records = {};
	var m_idPropertyName = idPropertyName?idPropertyName:"pkID";
	var m_recordPkPrefix = "pk";
	var m_total = 0;
	var m_newPkIds = {};
	this.QueryContext = { "from":0,"items":10,"sortcolumn":1,"sortdirection":"ASC" };

	this.GetTotal = function () {
		return m_total;
	}

	this.LoadFromJson = function ( jsonText ) {
		if ( !jsonText || jsonText=='' ) {
			this.Clear();
		} else {
			var jsonObject = eval( '(' + jsonText + ')' );
			if ( jsonObject.error ) {
				reportScriptError( jsonObject.error );
			} else {
				this.LoadFromObject( jsonObject );
			}
		}
	}

	this.LoadFromRecordsetNode = function( recordset ) {
		this.Clear();
		if (!recordset  ) {
			alert("empty node passed into recordset");
			return;
		}
		var attrs = recordset.attributes;
		for (var i = 0; i < attrs.length; ++i) {
			var attr = attrs.item(i);
			this.QueryContext[ attr.nodeName ] = attr.nodeValue;
		}
		var items = recordset.childNodes;
		for (var i = 0; i < items.length; ++i) {
			var record = items[i];
			if ( record.nodeName=='record' ) {
				var fields = record.childNodes;
				var record = {};
				for (var j = 0; j < fields.length; j++) {
					var field = fields[j];
					if ( field.nodeType == 1 ) {
						var value = null;
						if ( field.firstChild && field.firstChild.nodeType == 1 ) {
							value = new Recordset();
							value.LoadFromRecordsetNode( field.firstChild );
						} else  {
							value = field.firstChild?field.firstChild.nodeValue:"";	
						}
						record[ field.nodeName ] = value;
					}
				}
				this.SetRecord( record );		
			} else if ( record.nodeName == 'summary' ) {
				m_total = new Number( record.getAttribute("total") );
			}
		}

	}

	this.LoadFromObject = function ( jsonObject ) {

		this.Clear();
		if ( jsonObject.params ) {
			this.QueryContext = jsonObject.params;
		}
		if ( jsonObject.summary ) {
			m_total = jsonObject.summary.total;
		}
		var records = jsonObject.recordset;
		if ( records ) {
			this.LoadFromArray( records );
		}
	}
	this.LoadFromArray = function ( records ) {
		for(var i=0;i<records.length;++i ) {
			var record = records[i];
			for(  var fieldName in  record ) {
				var field = record[fieldName];
				if ( typeof(field)=='object' && field.recordset ) { //child recordset
					var childRecordset = new Recordset("");
					childRecordset.LoadFromObject( field );
					record[fieldName] = childRecordset;
				} else if ( typeof(field)=='string' ) {
					record[fieldName] = TryString2Date( field );
				}
				this.SetRecord(record);
	
			}
		}
	}

	function GetJsonValue( v ) {
		if ( typeof(v) == 'object' && v.SaveToJson ) {
			return v.SaveToJson();
		} else {
			return Value2Json( v );
		}
	}

	this.SaveToJson = function ( ) {
		var result = '{ \"params\":{';
		var comma ='';
		for( var attrid in this.QueryContext ) {
			result += comma + '\"'+ attrid + "\":\"" + this.QueryContext[attrid] + "\"";
			comma = ',';
		}
		result += '} , \"recordset\":[';
		comma ='';
		for( var recordId in m_records ) {
			result+= comma + "{";
			var record = m_records[ recordId ];
			comma ="";
			for( var fieldId in record ) {			
				if (  fieldId != m_idPropertyName || !this.IsNew( record[ m_idPropertyName ] ) ) {
					result+= comma + "\"" + fieldId + "\":";
					var propVal = record[fieldId];
					result+= GetJsonValue(propVal);
					comma = ",";

				}
			}
			result+="}";			
			comma = ',';
		}
		result += ']}';
		return result;
	}
	this.GetPrimaryKey = function ( newRecord ) {
		var pkId = "";
		if ( newRecord[m_idPropertyName]!=undefined && newRecord[m_idPropertyName]!="" ) {
			pkId = m_recordPkPrefix + newRecord[ m_idPropertyName ];
		} else {
			pkId = this.GetUniquePropertyValue( m_idPropertyName , "" );
			newRecord[ m_idPropertyName ] = pkId;
			pkId = m_recordPkPrefix + pkId;
			m_newPkIds[ pkId ] = true;
		}
		return pkId;
	}

	this.SetRecord = function ( newRecord ) {
		var pkId = this.GetPrimaryKey( newRecord );
		m_records[ pkId ] = newRecord;				
	}


	this.InsertRecord = function ( newRecord, after ) {
		var pkId = this.GetPrimaryKey( newRecord );
		var newRecords = {};
		var inserted = false;
		for( var propId in m_records) {
			newRecords[ propId ] = m_records[ propId ];
			if ( m_recordPkPrefix + after==propId ) {
				inserted = true;
				newRecords[ pkId ] = newRecord;
			} 
		}
		if ( !inserted ) {
			newRecords[ pkId ] = newRecord;
		}
		m_records = newRecords;
	}

	this.Exists = function ( id ) {
		return typeof( m_records[ id ] )!='undefined';
	}
	this.GetRecord = function ( id ) {
		return m_records[ m_recordPkPrefix + id ];				
	}
	this.IsNew = function ( id ) {
		
		return true == m_newPkIds[ m_recordPkPrefix + id ];				
	}

	function RemoveObjectProperty( obj, propertyName ) {
		var newRecords = {};
		for( var propId in obj) {
			if ( propertyName!=propId ) {
				newRecords[ propId ] = obj[ propId ];
			} 
		}
		return newRecords;
	}

	this.RemoveRecord = function ( id ) {
		var pkId = m_recordPkPrefix + id;
		m_records = RemoveObjectProperty( m_records, pkId);
		if ( this.IsNew( id ) ) {
			m_newPkIds = RemoveObjectProperty( m_newPkIds, pkId );
		}
	}
	
	this.FindByProperty = function ( propertyName, propertyValue ) {
		for( var recordId in m_records ) {
			var record = m_records[ recordId ];
			if ( record[ propertyName ] ==  propertyValue ) {
				return record;
			}
		}
		return null;
	}

	this.GetUniquePropertyValue = function ( propertyName, prefix ) {
		var index = 1;
		var result;
		do {
			result = prefix + index++;
		} while( this.FindByProperty( propertyName, result ) );

		return result;
	}

	this.IsEmpty = function() {
		for( var recordId in m_records ) {
			return false;
		}
		return true;
	}
	this.GetRecordArray = function() {
		var result = [];
		for( var recordId in m_records ) {
			result[ result.length ] = m_records[ recordId ];
		}				
		return result;
	}
	this.GetCount = function() {
		return GetObjectPropertiesCount( m_records );
	}
	this.Clear = function () {
		m_newPkIds = {};
		m_records = {};
	}

	this.AddNewRecord = function ( ) {
		var record = {};
		this.SetRecord( record );
		return record;
	}

	this.SelectByField = function( searchPattern , fieldName ) {
		var resultRecordset = new Recordset( "", m_idPropertyName );
		for( var recordId in m_records ) {
			var record = m_records[ recordId ];
			var fieldValue = record[ fieldName  ];
			if ( fieldValue.indexOf( searchPattern )!=-1 ) {
				resultRecordset.SetRecord( record );
			}
		}
		return resultRecordset;
	}
	this.Rollback = function ( ) {
		var newPkIds = m_newPkIds;
		for( var id in newPkIds ) {
			this.RemoveRecord( id.substring( m_recordPkPrefix.length ) );
		}
	}

	function AscSortCondition( fieldName ) {
		var m_field = fieldName;
		this.IsLess = function( a , b ) {
			return a[m_field]<b[m_field];
		}
	}
	function DescSortCondition( fieldName ) {
		var m_field = fieldName;
		this.IsLess = function( a , b ) {
			return a[m_field]>b[m_field];
		}
	}

	this.SortAsc = function( fieldName ) {
		this.Sort( new AscSortCondition( fieldName ) );
	}
	this.SortDesc = function( fieldName ) {
		this.Sort( new DescSortCondition( fieldName ) );
	}
	this.Sort = function( condition ) {
		var result = new Array();
		for( var recordId in m_records ) {
			var i=0;
			var obj = m_records[ recordId ];
			for( ;i<result.length;++i ) {
				if ( condition.IsLess(obj,result[i]) ) {
					break;
				}
			}
			result.splice( i,0, obj );
		}				
		this.Clear();
		for( var i=0;i<result.length;++i ) {
			this.SetRecord( result[i] );
		}
	}
	
	if ( data!=undefined && data!="" ) {
		this.LoadFromJson( data );
	}

}


function Value2Json( v, raw ) {
	if (null == v)
		return 'null';
	var propType = typeof(v);
	var quotation=raw?"":'"';
	if ( propType =='string' ) {
		var text = new String( v );
		return quotation +  text.replace(/\\/g,"\\\\").replace(/\"/g,"\\\"").replace(/\n/g,"\\n") +quotation;
	} else if ( propType=='object' ) {
		var r = '';
		if (v instanceof Array) {
			r= '';
		        for( var i=0;i<v.length;++i ) {
				r+=',' + Value2Json( v[i] );
			}
			r=v.length?'[' +  r.substr(1) + ']':'[]';
		} else if (v instanceof Date) {
			r = quotation + Date2String(v) + quotation;
		} else {
			r= '';
			var i=0;
		    for( var propName in v ) {
				++i;
				r+=',"' + propName + '":' + Value2Json( v[propName] );
			}
			r=i?'{' +  r.substr(1) + '}':'{}';
		}
		return r;
	} else if ( propType=='undefined' ) {
		return quotation+quotation;
	} else {
		return v;
	}
}

