/**
 * @class Static Class for parsing LexisNexis API data and turning it into MasterQuery Objects.
 * <br /><br /> 
 * <b>	EXAMPLES:</b><br /> 
<dl>
	<dt>{@link #propertyFactory PROPERTY FACTORY}</dt>
	<dd><pre>var chunnel = LNParser.propertyFactory('properties.txt');</pre></dd>
	<dt>{@link #queryStringFactory QUERY STRING}</dt>
	<dd><pre>var chunnel = LNParser.queryStringFactory("client=FEDTAX02&source=718dis;dguide&search=file-name(fedday)&view=full&ORIGINATION_CODE=00004");
   chunnel.submit();</pre></dd>
	<dt>{@link #factory EMBEDDED IN LINK}</dt>
	<dd><pre>&lt;a href=&quot;http://www.lexis.com/research/xlink?client=FEDTAX02&source=718dis;dguide&search=file-name(fedday)&view=full&ORIGINATION_CODE=00004&quot; 
   onclick=&quot;LNParser.factory(this).submit(); return false;&quot;&gt;link&lt;/a&gt;</pre>
		You can also override any of the links value by passing in a properties file:
		<pre>&lt;a href=&quot;http://www.lexis.com/research/xlink?client=FEDTAX02&source=718dis;dguide&search=file-name(fedday)&view=full&ORIGINATION_CODE=00004&quot; 
   onclick=&quot;LNParser.propertyFactory('properties.txt', LNParser.factory(this)).submit(); return false;&quot;&gt;link loader&lt;/a&gt;</pre>
	<br />
	</dd>
</dl>
 
 * @author Christopher Baker <Christopher.Baker@lexisnexis.com>
 * @version 0.1 
 * @constructor
 */
function LNParser()
{
	throw new Error("LNParser is a static class. Do not instantiate.");
}

/**
 * Regular expression for stripping off the query string from a URL
 * @type String
 */ 
LNParser.queryStringRE  = /\?(.+)$/;

/**
 * Factory method that takes an Anchor Object with a L/N Query as the HREF or
 * a L/N link string and returns a MasterQuery object based upon it. 
 *
 * @return A MasterQuery Object based upon the link
 * @param {Object} 
 * @type MasterQuery
 */
LNParser.factory = function(link, props)
{
	var strurl;
	var lnAPI;
	var target = "";
	// Check to see if it is an HREF String or an ANCHOR tag that is passed in.
	if (typeof link  == "object")
	{
		/* Might be useful later for adding web form parsing. 
		// are we a web form
			if (link.action) 
			{
				with (myForm)
				{
					strurl = action
					//check for text boxes
					for (elem = 0; elem < elements.length; elem++)
					{
					}
				}
			}*/
		
		strurl = link.href;
		target = link.target;
	}
	else
	{
		strurl = link;
	}
	
	lnAPI = LNParser.getAPI(strurl);

	// Set default API to XLink
	if (lnAPI === null)
	{
		lnAPI == MasterQuery.API_XLINK;
	}
	
	var m = strurl.match(LNParser.queryStringRE);
	
	var chunnel = new MasterQuery(lnAPI);
	
	chunnel.setTarget(target);
	
	if (props)
	{
		try
		{
			// if it's a Properties Object, get the values
			props = props.getProperties();
		}
		catch(e)
		{
			// otherwise do nothing. 
		}
		
		try{
			for (var prop in props) 
			{	
				chunnel.set(prop, props[prop]);
			}
		}
		catch(e)
		{
			if (isDev())
				alert(e.message)
		}		
	}
	
	if (strurl.indexOf("https://") != -1) 
	{
		chunnel.isUsingSSL = true;
	}
	
	/*
	doesn't seem to be working
	// Determine the domain that is being used.
	// For xlink to maintain 
	var mm = strurl.match(new RegExp("^https?:\/\/([A-Za-z]*\.?lexisnexis|lexis-nexis|lexis|nexis\.com)", "i"))
	
	if (mm) 
	{
		chunnel.setDomain(m[1]);
	}
	*/
	
	if (lnAPI === MasterQuery.API_DOSSIER)
	{
		chunnel.set("verb", "accessGW");
		chunnel.set("gw", "FC");
		chunnel.set("nm", chunnel.get("name"));
	}
	
	// http://www.lexisnexis.com/api.universe/v1_dossier_launch_forms?ORIGINATION_CODE=00004&prod=CD&host=nexis_com_r2&searchType=advancedFind

	chunnel = LNParser.queryStringFactory(m[1], chunnel);

	if (lnAPI === MasterQuery.API_DOSSIER_FORMS)
	{
		// http://www.lexisnexis.com/api.universe/v1_dossier_launch_forms?prod=CD
		// http://www.lexisnexis.com/api.universe/v1_dossier_launch_forms?prod=ID
		// http://www.lexisnexis.com/api.universe/v1_dossier_launch_forms?prod=CD&searchType=advancedFind
		// http://www.lexisnexis.com/api.universe/v1_dossier_launch_forms?prod=CD&searchType=quickFind
		
		chunnel.set("verb", "accessGW");
		
		if (chunnel.get("prod") == "ID") 
		{
			chunnel.set("gw", "FI");
		} 
		else 
		{
			if (chunnel.getSearchtype() == "quickFind")
			{
				chunnel.set("gw", "FC");
			} 
			else
			{
				chunnel.set("gw", "CL");
			} 
		}
	}

	

	return chunnel;

};

/**
 * Takes a querystring and returns a MasterQuery Object. If 
 * a MasterQuery Object is passed in as the second parameter
 * then the data is folded into that object. 
 *
 * See the overview for usage examples.
 *
 * @return A MasterQuery Object based upon the Query String
 * @param {String} A URL QueryString to be based
 * @param {MasterQuery} Optional MasterQuery Object for the values to be folded into
 * @type MasterQuery
 */ 
LNParser.queryStringFactory = function(queryString, chunnel)
{
	if (!chunnel)
	{
		chunnel = new MasterQuery();
	}
	var queryArray = queryString.split("&");
	for (var i = 0; i < queryArray.length; i++) 
	{		
		var sp = queryArray[i].split("=");
		var key = sp[0];
		var val = "";
        
        if (sp.length > 2)
        {
            for(var x = 2; x < sp.length; x++)
            {
                sp[1] = sp[1] + "=" + sp[x];
            }
        }
        
		try
		{
			if (sp[1]) val = decodeURIComponent(sp[1]);
		}
		catch(e)
		{
			try
			{
				if (sp[1]) val = unescape(sp[1]);
			}
			catch(e)
			{
				if (isDev())
				{
					prompt("",e.message)
				}
			}
		}
		
		if (key.toLowerCase() != "submit")
    		chunnel.set(key, val);
	}
	return chunnel;
};

/**
 * Takes a JSProperties Object and returns a MasterQuery Object.  If 
 * a MasterQuery Object is passed in as the second parameter
 * then the data is folded into that object.
 * <br /><br />
 * This method includes support for a special property value  
 * <tt>__TIME_DELAY__</tt>. Setting this property allows a developer
 * to override a specific value at a specific time. This allows you
 * to change from one API to another when the time is reached. 
 * <br /><br />
 * The format is Y:M:D:H:M:S;<i>key</i>;<i>value</i>
 * 
 * @return A MasterQuery Object based upon the properties file pased in
 * @param {Object} JSProperties Object or file name for the properties file.
 * @param {MasterQuery} Optional MasterQuery Object for the values to be folded into
 * @type MasterQuery
 */
LNParser.propertyFactory = function(props, chunnel)
{
	if (props)
	{
		if (typeof props == "string")
		{
			props = new JSProperties(props);
		}
	}
	else
	{
		// Otherwise create a default JSProperties object
		props = new JSProperties();
	}
	
	// Create the object that will be if it isn't already passed in. 
	if (!chunnel) 
	{
		chunnel = new MasterQuery();
	}
	
	// Make sure that the var passed in is an object.
	if ((arguments.length > 0))
	{
		if (typeof arguments[0] == "object")
		{
			props = arguments[0];
		}
		else
		{
			props = new JSProperties(arguments[0]);
		}		
	}
	
	// Now punch in the properties that match the MQ object main fields
	var properties = props.getProperties();
	try{
		for (var prop in properties) 
		{	
			switch(prop)
			{
				case "__TIME_DELAY__":
					// format for the var split is date;varName;varValue
					var pOver = properties[prop].split(";");
					
					var dOver = pOver[0].split(":");
					
					// alert(dOver[0] + " " + dOver[1] + " " + dOver[2] + " " + dOver[3] + " " + dOver[4])
					
					//          new Date(aYear,    aMonth,   aDate,    anHour,   aMinute,  aSecond)
					var dDate = new Date(dOver[0], dOver[1] - 1, dOver[2], dOver[3], dOver[4], dOver[5]);
					
					dDate.setFullYear(dOver[0]);
					
					var currentDate = new Date();
					
					// alert(dDate + " " + currentDate);
					
					// prompt("", dDate.getTime() + " " + currentDate.getTime())
					
					if (dDate.getTime() < currentDate.getTime())
					{
						// alert()
						chunnel.set(pOver[1], pOver[2]);
					}
					break;
				default:
					chunnel.set(prop, properties[prop]);
					break;
			}
		}
	}
	catch(e)
	{
		if (isDev())
			alert(e.message)
	}
	
	return chunnel;
};

/** 
 * Returns the API being used by a specific LN link.
 * @param {String} QueryString to determine what L/N API is being used.
 * @type int
 */
LNParser.getAPI = function(strURL)
{
	if(strURL) 
	{ 
		strURL = strURL.toLowerCase(); 
	}
	else
	{
		return null;
	}
	
	if (strURL.lastIndexOf(".com/clients/cui/search.asp") != -1)
	{
		return MasterQuery.API_CUI_BUILDER;
	}
	else if (strURL.lastIndexOf(".com/clients/cuiint/search.asp") != -1)
	{
		return MasterQuery.API_CUI_INTERNATIONAL;
	}	
	else if ((strURL.lastIndexOf("lexis-nexis.com/professional") != -1) || (strURL.lastIndexOf("lexisnexis.com/professional") != -1))
	{
		return MasterQuery.API_PROFESSIONAL;
	}	
	else if ((strURL.lastIndexOf("lexisnexis.com/api.universe/v1_dossier_launch_results") != -1) || (strURL.lastIndexOf("lexis-nexis.com/api.universe/v1_dossier_launch_results") != -1))
	{
		return MasterQuery.API_DOSSIER;
	}
	else if ((strURL.lastIndexOf("lexisnexis.com/api.universe/v1_portfolio") != -1) || (strURL.lastIndexOf("lexis-nexis.com/api.universe/v1_portfolio") != -1))
	{
		return MasterQuery.API_V1_PORTFOLIO;
	}	
	else if (strURL.lastIndexOf("nexis.com/api.universe/v1_searchform") != -1)
	{
		return MasterQuery.API_V1_SEARCHFORM;
	}
	else if ((strURL.lastIndexOf("lexisnexis.com/api.universe/v1_search") != -1) || (strURL.lastIndexOf("lexis-nexis.com/api.universe/v1_search") != -1))
	{
		return MasterQuery.API_V1_SEARCH;
	}	
	else if ((strURL.lastIndexOf("nexis.com/api.universe/v1_snews") != -1) || (strURL.lastIndexOf("lexis-nexis.com/api.universe/v1_snews") != -1))
	{
		return MasterQuery.API_V1_SNEWS;
	}	
//	else if ((strURL.lastIndexOf("exis.com/xlink") != -1) || (strURL.lastIndexOf("exis.com/research/xlink" != -1)))
//	{
//		return MasterQuery.API_XLINK;
//	}
	
	// ADDED
	else if (strURL.lastIndexOf("nexis.com/api.universe/v1_dossier_launch_forms") != -1)
	{		 // http://www.lexis-nexis.com/api.universe/v1_dossier_launch_forms
		
		return MasterQuery.API_DOSSIER_FORMS;
	}
	else if (strURL.lastIndexOf("nexis.com/api.universe/search/searchform") != -1)
	{
		return MasterQuery.API_NEXIS_SEARCHFORM;
	}
	
	// ADDED for IA
	
	else if ((strURL.lastIndexOf("exis.com/auth/lnu/activate.asp") != -1) || (strURL.lastIndexOf("exis.com/auth/lnu/activateLexis.asp") != -1) || (strURL.lastIndexOf("exis.com/auth/lnu/activatenexis.asp") != -1) || (strURL.lastIndexOf("exis.com/auth/lnu/activatenexisorgid.asp") != -1))
	{
		return MasterQuery.API_IA_LNDB_ACTIVATION;
	}
	
	else if ((strURL.lastIndexOf("exis.com/auth/lnu/lckill.asp") != -1) || (strURL.lastIndexOf("exis.com/auth/lnu/nckill.asp") != -1) || (strURL.lastIndexOf("exis.com/auth/lnu/lnckill.asp") != -1))
	{
		return MasterQuery.API_IA_LNDB_LOGOUT;
	}
	
	else if (strURL.lastIndexOf("exis.com/auth/lnu/setCookie.asp") != -1)
	{
		return MasterQuery.API_IA_LNDB_LOGIN;
	}
	return null;
};



MasterQuery.API_IA_LNDB_LOGOUT = 16;

/**
 * Returns a human readable version of a LN Api names. Used for debugging window.
 * @param {int} QueryString to determine what L/N API is being used.
 * @type String
 */
LNParser.getAPIName = function(strAPI)
{
	switch(strAPI) 
	{
		case MasterQuery.API_CUI_BUILDER:
			return "CUI Builder";
			break;
		case MasterQuery.API_CUI_INTERNATIONAL:
			return "CUI International";
			break;
		case MasterQuery.API_DOSSIER:
			return "Dossier";
			break;
		case MasterQuery.API_DOSSIER_FORMS:
			return "Dossier Launch Forms";
			break;	
		case MasterQuery.API_GET_AND_PRINT:
			return "Get and Print";
			break;
		case MasterQuery.API_NEXIS_SEARCHFORM:
			return "Nexis Search Form";
			break;	
		case MasterQuery.API_PROFESSIONAL:
			return "Professional";
			break;
		case MasterQuery.API_URL_API:
			return "Rosetta";
			break;
		case MasterQuery.API_V1_PORTFOLIO:
			return "Smart Tools v1_portfolio";
			break;
		case MasterQuery.API_V1_SEARCH:
			return "Smart Tools v1_search";
			break;
		case MasterQuery.API_V1_SEARCHFORM:
			return "Smart Tools v1_searchform";
			break;
		case MasterQuery.API_V1_SNEWS:
			return "Smart Tools v1_snews";
			break;
		case MasterQuery.API_XLINK:
			return "XLink";
			break;
		case MasterQuery.API_XLINK_SEISINT:
			return "XLink Seisint";
			break;
		case MasterQuery.API_IA_LNDB_ACTIVATION:
			return "LNDB Instant Activation";
			break;
		case MasterQuery.API_IA_LNDB_LOGOUT:
			return "LNDB IA Logout";
			break;
		case MasterQuery.API_IA_LNDB_LOGIN:
			return "LNDB IA Login";
			break;
		case MasterQuery.API_XLINK_PUBREC:
			return "XLink Public Records";
			break;
		case MasterQuery.API_URL_API_PUBREC:
			return "Rosetta Public Records";
			break;

	}
	return "";
};

/** 
 * Parses various after/relative date values and returns an after value.
 * @param {String} Any relative date/after value
 * @type String 
 */
LNParser.parseAfter = function(strAfter)
{

	if (!Truth.isTrue(strAfter))
	{
		return "";
	}
		
	try
	{
		strAfter = strAfter.toUpperCase();
	}
	catch(e)
	{
		strAfter = strAfter.toString();
	}
	
	// /^\d+:DY|WK|MO|YR$/ 
	if (strAfter.search(/^\d+:(DY|WK|MO|YR)$/i) != -1)
	{
		return strAfter;
	}

	strAfter = strAfter.toLowerCase();

	switch(strAfter)
	{
		case "today":
			return "1:DY";
			break;
		case "this_week":
		case "previous_week":
			return "1:WK";
			break;
		case "this_month":
		case "previous_month":
			return  "1:MO";
			break;
		case "this_year":
		case "previous_year":
			return "1:YR";
			break;
	}

	if (strAfter.lastIndexOf("_") != -1)
	{	
		var relAr = strAfter.split("_");
		// The number for the relative date
		var relInt = relAr[1];
		// The type, such as days or weeks
		var relPeriod = relAr[2];
		
		if (relInt.search(/^\d+$/) == -1)
		{	
			return null;
		}

		switch(relPeriod)
		{
			case "days":
				return relInt + ":DY";
				break;
			case "weeks":
				return relInt + ":WK";
				break;
			case "months":
				return relInt + ":MO";
				break;
			case "years":
				return relInt + ":YR";
				break;
		}
	}
	return null;

};

/**
 * Takes a valid after date value and returns it in the relative date format
 * @param {String} After date value
 * @type String 
 */
LNParser.convertAfterToRelative = function(after)
{
	if (!Truth.isTrue(after))
	{
		return "";
	}
	
	var dArray = after.split(":")
	// The integer for the length
	var dInt = dArray[0];
	var dPeriod = dArray[1];
	
	switch(dPeriod)
	{
		case "DY":
			if (dInt == 1)
			{
				return "today";
			}
			return "previous_" + dInt + "_days";
			break;
		case "WK":
			if (dInt == 1)
			{
				return "previous_week";
			}
			return "previous_" + dInt + "_weeks";
			break;
		case "MO":
			if (dInt == 1)
			{
				return "previous_month";
			}
			return "previous_" + dInt + "_months";
			break;
		case "YR":
			if (dInt == 1)
			{
				return "previous_year";
			}
			return "previous_" + dInt + "_years";
			break;
	}
	return "";
};

/**
 * Takes a MasterQuery Object and converts any date properties to L/N Query syntax.
 * Used for implementors whose APIs don't support relative date paramaters.
 * @param {MasterQuery} 
 * @type MasterQuery
 */

LNParser.convertDateToQuery = function(mq, format)
{
	if (mq.after) 
	{
		var dRange     = mq.after.substring(0, mq.after.length - 3);
		var dRangeType = mq.after.substring(mq.after.length - 2);
		
		var theDate = new Date();
		var currDate = new Date();
		
		switch(dRangeType)
		{
			case "DY":
				theDate.setDate(theDate.getDate() - dRange);
				break;
			case "WK":
				theDate.setDate(theDate.getDate() - (dRange * 7));
				break;
			case "MO":
				theDate.setMonth(theDate.getMonth() - dRange);
				break;
			case "YR":
				theDate.setFullYear(theDate.getFullYear() - dRange);
				break;
		}
		var oneDay = 86400000;
		var dayDiff = Math.ceil((currDate.getTime()-theDate.getTime())/(oneDay));
        dayDiff = dayDiff + 1;
        
		var dStr = "date aft(%currdate-" + dayDiff + "%)";
		if((mq.get("adaption") == "uk") && ((mq.get("type") == "nexis") || (mq.get("type") == "business")))
		    dStr = "date aft(" + theDate.getDate() + "/" + (parseInt(theDate.getMonth())+parseInt("1")) + "/" + theDate.getFullYear() + ")";
		LNParser.foldIn(mq, dStr);
		mq.after = "";
	}
	else if (mq.fromDate && mq.toDate) 
	{
		var dStr = "date(geq (" + mq.fromDate + ") and leq (" + mq.toDate + "))";
		
		LNParser.foldIn(mq, dStr);
		mq.fromDate = "";
		mq.toDate = "";
		
	}
	else if (mq.fromDate) 
	{
		var dStr = "date(geq (" + mq.fromDate + ")";
		LNParser.foldIn(mq, dStr);
		
		mq.fromDate = "";
	}
	else if (mq.toDate) 
	{
		var dStr = "date(leq (" + mq.toDate + "))";
		LNParser.foldIn(mq, dStr);
		
		mq.toDate = "";
	}

	return mq;
};

/**
 * Method to fold in a LN Query version of a date. Determines if the
 * date should be at the beginning or the end of the query depending
 * upon if it has a NOT value in the Query. This is to avoid a bug
 * in certain LN APIs.
 * @param {MasterQuery} 
 * @param {String} Date String
 * @type MasterQuery
 */
LNParser.foldIn = function(mq, dStr)
{
	var searchIncludesNot = mq.search.match(new RegExp("NOT", "i"));
	
	if (mq.search) 
	{
		if (searchIncludesNot)
		{
			mq.search = dStr + " and " + mq.search;
		}
		else
		{
			mq.search =  mq.search + " and " + dStr;
		}
	}
	else
	{
		mq.search = dStr;
	}
	return mq;
};
