/**
 * 
 */
package gov.va.med.cds.filter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.dom4j.Element;

/**
 * @author susarlan
 *
 */
public class ParameterMap implements ParameterMapInterface 
{
	//private Map<String, QueryParameter<?>> queryParameterMap = new HashMap<String, QueryParameter<?>>();
	private Map<String, QueryParameter<?>> queryParameterMap = new LinkedHashMap<String, QueryParameter<?>>();
	
	/***
	 * Return the names of the parameters that are specified
	 * in the filter based which this map was created
	 *  
	 */
	public Set<String> getFilterParameterNames()
	{
		Set<String> parameterNames = new LinkedHashSet<String>();
		if(queryParameterMap != null && queryParameterMap.size() > 0)
		{
			parameterNames.addAll(queryParameterMap.keySet());
		}
		return parameterNames;
	}

	/***
	 * Returns the QueryParameter to which the specified 'parameterName is mapped
	 * or null if this map contains no mapping for the 'parameterName'.
	 * 
	 * @param String parameterName - name of the query parameter
	 * 
	 * @return QueryParameter - QueryParameter object that maps to teh specified parameterName or null
	 */
	public QueryParameter<?> getParameterValue(String parameterName) 
	{
		return queryParameterMap.get(parameterName);
	}
	
	protected ParameterMap(Element additionalParametersElement)
	{
		if(additionalParametersElement != null && 
				"otherQueryParameters".equals(additionalParametersElement.getName()))
		{
			extractAdditionalParameters(additionalParametersElement);
		}
	}

    /**
     * @param additionalParametersElement
     */
    private void extractAdditionalParameters( Element additionalParametersElement)
    {
        List<Element> additionalParametersList = (List<Element>)additionalParametersElement.elements();
		for(Element additionalParameter : additionalParametersList)
		{
            String paramName = (additionalParameter.attributeValue( "name" ) != null ) ? additionalParameter.attributeValue( "name" ) : additionalParameter.getName();
            String paramType = (additionalParameter.attributeValue( "type" ) != null ) ? additionalParameter.attributeValue( "type" ) : "string";
            QueryParameter<?> queryParameter = queryParameterMap.get(paramName);
		    if (additionalParameter.elements().size() > 0) 
		    {
		        queryParameter = extractMapTypeParameter( additionalParameter, paramName );
		    }
		    else
		    {
    			String paramValue = additionalParameter.getTextTrim();
    			if(queryParameter != null)
    			{
    				queryParameter = extractListTypeParameter( paramName, paramValue, queryParameter );	
    			}
    			else
    			{
    				queryParameter = extractSingleValueParameter( paramName, paramValue );
    			}
		    }
		    queryParameter.setType( paramType );
            queryParameterMap.put(paramName, queryParameter);
		    
		}
    }

    /**
     * @param additionalParameter
     * @param paramName
     * @return
     */
    private QueryParameter<?> extractMapTypeParameter( Element additionalParameter, String paramName )
    {
        QueryParameter<?> queryParameter;
        QueryParameter<Map<String, Object>> mapParameter = new QueryParameter<Map<String, Object>>();
        mapParameter.setName(paramName);
        Map<String, Object> complexParam = new HashMap<String, Object>();
        for (Element element : (List<Element>)additionalParameter.elements())
        {
            complexParam.put( paramName + "_" + element.getName(), element.getTextTrim() );
        }
        mapParameter.setValue(complexParam);
        queryParameter = mapParameter;
        return queryParameter;
    }

    /**
     * @param paramName
     * @param paramValue
     * @param queryParameter
     * @return
     */
    private QueryParameter<?> extractListTypeParameter( String paramName, String paramValue, QueryParameter<?> queryParameter )
    {
        if(queryParameter.getValue() instanceof List)
        {
        	((List)queryParameter.getValue()).add(paramValue);
        }
        else
        {
        	// TODO: determine the actual type - may need schema to do this - for now set to String
        	QueryParameter<List<String>> listParameter = new QueryParameter<List<String>>();
        	listParameter.setName(paramName);
        	
        	List<String> values = new ArrayList<String>();
        	values.add((String)queryParameter.getValue());
        	values.add(paramValue);
        	listParameter.setValue(values);
        	
        	queryParameter = listParameter;
        }
        return queryParameter;
    }

    /**
     * @param paramName
     * @param paramValue
     * @return
     */
    private QueryParameter<?> extractSingleValueParameter( String paramName, String paramValue )
    {
        QueryParameter<?> queryParameter;
        // TODO: determine the actual type - may need schema to do this - for now set to String
        QueryParameter<String> textParameter = new QueryParameter<String>();
        textParameter.setName(paramName);
        textParameter.setValue(paramValue);
        queryParameter = textParameter;
        return queryParameter;
    }
}
