﻿var postalCodeCapturingPattern = "(\\d{5}){1}(?:-)?(\\d{4})?";
var stateCapturingPattern = "^(A[LKZR]|C[AOT]|D[EC]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$";
var postalCodeRegex = new RegExp(postalCodeCapturingPattern);
var stateRegex = new RegExp(stateCapturingPattern);

var multiwordState = ["DISTRICT OF COLUMBIA", 
                    "NEW HAMPSHIRE",
                    "NEW JERSEY",
                    "NEW MEXICO",
                    "NEW YORK",
                    "NORTH CAROLINA",
                    "NORTH DAKOTA",
                    "RHODE ISLAND",
                    "SOUTH CAROLINA",
                    "SOUTH DAKOTA",
                    "WEST VIRGINIA"];

var stateMap = {"ALABAMA":"AL",
                "ALASKA":"AK",
                "ARIZONA":"AZ",
                "ARKANSAS":"AR",
                "CALIFORNIA":"CA",
                "COLORADO":"CO",
                "CONNECTICUT":"CT",
                "DELAWARE":"DE",
                "DISTRICT OF COLUMBIA":"DC",
                "D.C.":"DC",
                "D.C":"DC",
                "FLORIDA":"FL",
                "GEORGIA":"GA",
                "HAWAII":"HI",
                "IDAHO":"ID",
                "ILLINOIS":"IL",
                "INDIANA":"IN",
                "IOWA":"IA",
                "KANSAS":"KS",
                "KENTUCKY":"KY",
                "LOUISIANA":"LA",
                "MAINE":"ME",
                "MARYLAND":"MD",
                "MASSACHUSETTS":"MA",
                "MICHIGAN":"MI",
                "MINNESOTA":"MN",
                "MISSISSIPPI":"MS",
                "MISSOURI":"MO",
                "MONTANA":"MT",
                "NEBRASKA":"NE",
                "NEVADA":"NV",                
                "NEW HAMPSHIRE":"NH",
                "NEW JERSEY":"NJ",
                "NEW MEXICO":"NM",
                "NEW YORK":"NY",
                "NORTH CAROLINA":"NC",
                "NORTH DAKOTA":"ND",
                "OHIO":"OH",
                "OKLAHOMA":"OK",
                "OREGON":"OR",
                "PENNSYLVANIA":"PA",
                "RHODE ISLAND":"RI",
                "SOUTH CAROLINA":"SC",
                "SOUTH DAKOTA":"SD",
                "TENNESSEE":"TN",
                "TEXAS":"TX",
                "UTAH":"UT",
                "VERMONT":"VT",
                "VIRGINIA":"VA",
                "WASHINGTON":"WA",
                "WEST VIRGINIA":"WV",
                "WISCONSIN":"WI",
                "WYOMING":"WY"                                                           
                };



//UNUSED for reference
//var commaRequiredDelimiterPattern = "[ ]*,[ ]*";
//var delimiterPattern = "[ ,]*";
//var cityOrNeighborhoodCapturingPattern = "([\\w ]+)*";
//var commaRequiredDelimiterRegex = new RegExp(commaRequiredDelimiterPattern);
//var cityOrNeighborhoodRegex = new RegExp(cityOrNeighborhoodCapturingPattern);

String.prototype.trim = function ()
{
    return this.replace(/^\s*/, "").replace(/\s*$/, "");
}

/*
	Basic constructor for AddressJSON
	@return AddressJSON
*/

function createAddressJSON()
{
	return createAddressJSON("", "", "", "", "", "");
}

/*
	Full constructor for AddressJSON
	@return AddressJSON
*/
function createAddressJSON(street, neighborhood, city, state, postalCode, postalCodePlusFour)
{
    return {"street": street,
    	    "neighhborhood":neighborhood,
            "city": city,
            "state": state,
            "postalCode": postalCode,
            "postalCodePlusFour": postalCodePlusFour};
}


/*	
	Breaks down input string into usable components for addresses
	BNF
	address	:= <city | neighborhood><delimiter><state>
		:= <city><delimiter><state><delimiter><postalCode>
		:= <state><delimiter><postalCode>
		:= <postalCode><delimiter><state>
		:= <neighborhood><commaRequiredDelimiter><city>
		:= <neighborhood><commaRequiredDelimiter><city><delimiter><state>
	
	delimiter := ,| \040
	
	@return AddressJSON
*/

function processAddressInput(inputAddressString)
{   
    // strip leading/trailing spaces and normalize the string to single spaces    
	return _processAddressInput(inputAddressString.toUpperCase().trim().replace(/\s\s+/g," ").replace(new RegExp(", ", "g"), ","), createAddressJSON()); 
}

function isPostalCode(testString)
{
	return postalCodeRegex.test(testString);
}

function isState(testString, lookAheadToken)
{
    var returnVal = false;
    if(parseState(testString, lookAheadToken)!=null)
    {
        returnVal = true;
    }
    return returnVal;
}

function parseState(testString, fullStateName)
{
    var returnVal = null;
    if (!stateRegex.test(testString))
    {
        var converted = convertToStateCodeViaMap(fullStateName==null?testString:fullStateName);        
        if (converted!=undefined && converted != testString)
        {
            //it must have been a full state name, so return true
            if(fullStateName==null)
            {
                returnVal = {"state":converted, usedLookAhead:false};
            }
            else
            {
                returnVal = {"state":converted, usedLookAhead:true};
            }
        }        
    }
    else
    {
        returnVal = {"state":testString, usedLookAhead:false};
    }
    return returnVal;
}

/*

The following are all *private* methods

*/

function _cleanUpAddressJSON(addressJSON)
{
	addressJSON.street = _processElement(addressJSON.street);
	addressJSON.neighborhood = _processElement(addressJSON.neighborhood);	
	addressJSON.city = _processElement(addressJSON.city);
	addressJSON.state = _processElement(addressJSON.state);	
	addressJSON.postalCode = _processElement(addressJSON.postalCode);
	addressJSON.postalCodePlusFour = _processElement(addressJSON.postalCodePlusFour);	
	if(addressJSON.neighborhood==undefined)
	{
	    addressJSON.neighborhood = addressJSON.city;
	}	
	return addressJSON;
}

function _processElement(element)
{
	if(element==undefined || element.length==1)
	{
		element = null;
	}
	else
	{
		element = element.trim();
		element = element.replace(",","");
	}
	return element;
}

function _processAddressInput(inputAddressString, addressJSON, foundNeighborhoodDelimiter)
{
	if(inputAddressString==null || inputAddressString==undefined || inputAddressString.length>0)
	{
		var testString = inputAddressString.trim();		
		var stringLength = testString.length;		
		var foundComma = true;
		
		var lastIndexOfSpace = testString.lastIndexOf(" ");
		var lastIndexOfComma = testString.lastIndexOf(",");
		var lastIndexOf = lastIndexOfSpace;				
		
		// Commas have greater precedence, due to neighborhoods
		if(lastIndexOfComma!=lastIndexOfSpace)
		{
			if(lastIndexOfComma>lastIndexOfSpace)
			{
				lastIndexOf = lastIndexOfComma;
			}
			else
			{				
				foundComma = false;
			}
		}
		
		var lastToken = testString.slice(lastIndexOf<1?0:lastIndexOf+1);
		
		// Attempt to find a full state name
		if(addressJSON.state == undefined)
		{
		    var fullStateName = null;		    		    
		    for(var i=0;i<multiwordState.length;i++)
		    {
		        var indexOfFullStateName = inputAddressString.lastIndexOf(multiwordState[i]);
		        if(indexOfFullStateName!=-1 && testString.substr(indexOfFullStateName) == multiwordState[i])
		        {
		            fullStateName = multiwordState[i];
		            break;
		        }
		    }
		}
		
		if(isPostalCode(lastToken))
		{
			//alert("Processing Postal Code");
			var postalCodeArray = postalCodeRegex.exec(lastToken);
			addressJSON.postalCode = postalCodeArray[1];
			addressJSON.postalCodePlusFour = postalCodeArray[2];
			if(lastIndexOf>0)
			{
				return _processAddressInput(inputAddressString.substr(0, lastIndexOf), addressJSON);
			}
		}
		else if(addressJSON.state == undefined && (parsedStateResult = parseState(lastToken, fullStateName))!=null)
		{
			//alert("Processing State");			
			addressJSON.state = parsedStateResult.state;
			
			if(parsedStateResult.usedLookAhead)
			{
			    lastIndexOf = stringLength-fullStateName.length;
			}
			
			if(lastIndexOf>0)
			{
				return _processAddressInput(inputAddressString.substr(0, lastIndexOf), addressJSON);
			}
		}
		else
		{
			// determine if it's a neighborhood or city by detecting if a comma was found			
			if(foundComma && lastIndexOf==lastIndexOfComma && lastIndexOfComma!=-1)
			{
			    //alert("Processing Neighborhood and City");
			    var splitString = testString.split(",");
			    if(splitString!=null && splitString.length>1)
			    {
			        addressJSON.neighborhood = splitString[0];

			        if(addressJSON.city==undefined || addressJSON.city.length==0)
			        {
			            if(splitString[1].length==0)
			            {
			                addressJSON.city = addressJSON.neighborhood;			                
			            }
			            else
			            {
			                addressJSON.city = splitString[1];
			            }			           
			        }
			        else
			        {
			            addressJSON.city = splitString[1].trim()+" "+addressJSON.city.trim();
			        }			        			        			        
			    }			
			}
			else
			{
				//alert("Processing City");				
				if(addressJSON.city!=undefined)
				{
					addressJSON.city = lastToken +" "+addressJSON.city;				
				}
				else
				{
					addressJSON.city = lastToken;
				}				
				if(lastIndexOf>0)
				{
					return _processAddressInput(testString.substr(0, lastIndexOf), addressJSON);
				}
			}			
		}
	}
	return _cleanUpAddressJSON(addressJSON);
}

function outputArray(inputAddressString, label, array)
{

	document.write("-----"+label+"-----<br/>");
	document.write("ORIGINAL: "+inputAddressString+"<br/>");
	if(array!=null)
	{
		for(var i=0;i<array.length;i++)
		{			
			document.write(i+":"+array[i]+"<br/>");
		}
	}
	else
	{
		document.write("no results<br/>");
	}
	document.write("-----"+label+"-----<br/>");
}

function convertToStateCodeViaMap(state)
{
    return stateMap[state];
}

function convertToStateCode(state)
{
    var abbrev = state
    if (state != "")
    {
        switch (state.toUpperCase())
        {
            case "ALABAMA":
                abbrev = "AL";
                break;
            case "ALASKA":
                abbrev = "AK";
                break;
            case "ARIZONA":
                abbrev = "AZ";
                break;
            case "ARKANSAS":
                abbrev = "AR";
                break;
            case "CALIFORNIA":
                abbrev = "CA";
                break;
            case "COLORADO":
                abbrev = "CO";
                break;
            case "CONNECTICUT":
                abbrev = "CT";
                break;
            case "DELAWARE":
                abbrev = "DE";
                break;
            case "DISTRICT OF COLUMBIA":
                abbrev = "DC";
                break;
            case "FLORIDA":
                abbrev = "FL";
                break;
            case "GEORGIA":
                abbrev = "GA";
                break;
            case "HAWAII":
                abbrev = "HI";
                break;
            case "IDAHO":
                abbrev = "ID";
                break;
            case "ILLINOIS":
                abbrev = "IL";
                break;
            case "INDIANA":
                abbrev = "IN";
                break;
            case "IOWA":
                abbrev = "IA";
                break;
            case "KANSAS":
                abbrev = "KS";
                break;
            case "KENTUCKY":
                abbrev = "KY";
                break;
            case "LOUISIANA":
                abbrev = "LA";
                break;
            case "MAINE":
                abbrev = "ME";
                break;
            case "MARYLAND":
                abbrev = "MD";
                break;
            case "MASSACHUSETTS":
                abbrev = "MA";
                break;
            case "MICHIGAN":
                abbrev = "MI";
                break;
            case "MINNESOTA":
                abbrev = "MN";
                break;
            case "MISSISSIPPI":
                abbrev = "MS";
                break;
            case "MISSOURI":
                abbrev = "MO";
                break;
            case "MONTANA":
                abbrev = "MT";
                break;
            case "NEBRASKA":
                abbrev = "NE";
                break;
            case "NEVADA":
                abbrev = "NV";
                break;
            case "NEW HAMPSHIRE":
                abbrev = "NH";
                break;
            case "NEW JERSEY":
                abbrev = "NJ";
                break;
            case "NEW MEXICO":
                abbrev = "NM";
                break;
            case "NEW YORK":
                abbrev = "NY";
                break;
            case "NORTH CAROLINA":
                abbrev = "NC";
                break;
            case "NORTH DAKOTA":
                abbrev = "ND";
                break;
            case "OHIO":
                abbrev = "OH";
                break;
            case "OKLAHOMA":
                abbrev = "OK";
                break;
            case "OREGON":
                abbrev = "OR";
                break;
            case "PENNSYLVANIA":
                abbrev = "PA";
                break;
            case "RHODE ISLAND":
                abbrev = "RI";
                break;
            case "SOUTH CAROLINA":
                abbrev = "SC";
                break;
            case "SOUTH DAKOTA":
                abbrev = "SD";
                break;
            case "TENNESSEE":
                abbrev = "TN";
                break;
            case "TEXAS":
                abbrev = "TX";
                break;
            case "UTAH":
                abbrev = "UT";
                break;
            case "VERMONT":
                abbrev = "VT";
                break;
            case "VIRGINIA":
                abbrev = "VA";
                break;
            case "WASHINGTON":
                abbrev = "WA";
                break;
            case "WEST VIRGINIA":
                abbrev = "WV";
                break;
            case "WISCONSIN":
                abbrev = "WI";
                break;
            case "WYOMING":
                abbrev = "WY";
                break;
        }
    }
    return abbrev;
}
