package org.configureme.parser.json;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import net.anotheria.util.StringUtils;

import org.configureme.Environment;
import org.configureme.environments.DynamicEnvironment;
import org.configureme.parser.ArrayParsedAttribute;
import org.configureme.parser.CompositeParsedAttribute;
import org.configureme.parser.ConfigurationParser;
import org.configureme.parser.ConfigurationParserException;
import org.configureme.parser.ParsedAttribute;
import org.configureme.parser.ParsedConfiguration;
import org.configureme.parser.PlainParsedAttribute;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * ConfigurationParser implementation for JSON.
 * @author lrosenberg
 */
public class JsonParser implements ConfigurationParser {
	/**
	 * The prefix string distinguishing names of environment JSON objects from composite JSON objects.
	 * The names of last ones shall starts with this prefix.
	 */
	private static final String COMPOSITE_ATTR_PREFIX = "@";

	@Override
	public ParsedConfiguration parseConfiguration(String name, String content) throws ConfigurationParserException {

		content = StringUtils.removeCComments(content);
		content = StringUtils.removeCPPComments(content);

		try {
			JSONObject j = new JSONObject(content);
			ParsedConfiguration pa = new ParsedConfiguration(name);

			DynamicEnvironment env = new DynamicEnvironment();

			String[] names = JSONObject.getNames(j);
			if(names != null)
				for (String key : names) {
					List<? extends ParsedAttribute<?>> attList = parse(key, j.get(key), env);
					for (ParsedAttribute<?> att : attList)
						pa.addAttribute(att);
				}

			return pa;

		} catch (JSONException e) {
			throw new ConfigurationParserException("JSON Error", e);
		}
	}

	private static List<? extends ParsedAttribute<?>> parse(String key, Object value, DynamicEnvironment environment) throws JSONException{
		// an object value means a change in environment, let's see what it is
		if (value instanceof JSONObject && key.startsWith(COMPOSITE_ATTR_PREFIX))
			return asList(parseComposite(key, (JSONObject) value, environment));
        else if (value instanceof JSONArray && key.startsWith(COMPOSITE_ATTR_PREFIX))
        	return asList(parseArray(key, (JSONArray) value, environment));
        else if (value instanceof JSONObject)
        	return parseObject(key, (JSONObject) value, environment);
		else if (value instanceof JSONArray)
			return Arrays.asList(parseArray(key, (JSONArray) value, environment));
		else
			return Arrays.asList(new PlainParsedAttribute(key, (Environment)environment.clone(), value.toString()));
	}

	@SuppressWarnings("unchecked")
	private static <T> List<T> asList(T item) {
		return (item != null) ? Arrays.<T>asList(item) : Collections.<T>emptyList();
	}

	private static List<ParsedAttribute<?>> parseObject(String key, JSONObject value, DynamicEnvironment environment) throws JSONException {
		List<ParsedAttribute<?>> parsed = new ArrayList<ParsedAttribute<?>>();

		environment.extendThis(key);
		try {
			String[] names = JSONObject.getNames(value);
			if(names != null)
				for (String subKey : names)
					parsed.addAll(parse(subKey, value.get(subKey), environment));
		} finally {
			environment.reduceThis();
		}

		return parsed;
	}

	private static CompositeParsedAttribute parseComposite(String key, JSONObject value, DynamicEnvironment environment) throws JSONException {
        String[] names = JSONObject.getNames(value);
        if (names == null)
        	return null;

        List<ParsedAttribute<?>> leafAttr = new ArrayList<ParsedAttribute<?>>();
        for (String subKey : names)
            leafAttr.addAll(parse(subKey, value.get(subKey), environment));

        return new CompositeParsedAttribute(stripKey(key), (Environment) environment.clone(), leafAttr);
	}

	private static ArrayParsedAttribute parseArray(String key, JSONArray value, DynamicEnvironment environment) throws JSONException {
		List<ParsedAttribute<?>> parsed = new ArrayList<ParsedAttribute<?>>(value.length());
		for (int index = 0; index < value.length(); index++)
			parsed.addAll(parse(key, value.get(index), environment));

		return new ArrayParsedAttribute(stripKey(key), (Environment) environment.clone(), parsed);
	}

	private static String stripKey(String key) {
		return key.startsWith(COMPOSITE_ATTR_PREFIX)
				? key.substring(COMPOSITE_ATTR_PREFIX.length())
				: key;
	}
}

@@ -2138,32 +2138,39 @@
 FIX))%0A%09%09%09return 
+Arrays.
 asList(parseComp
@@ -2211,32 +2211,26 @@
 ironment));%0A
-        
+%09%09
 else if (val
@@ -2295,32 +2295,26 @@
 REFIX))%0A
-        
+%09%09
 %09return 
 asList(p
@@ -2305,16 +2305,23 @@
 %09return 
+Arrays.
 asList(p
@@ -2369,24 +2369,18 @@
 ment));%0A
-        
+%09%09
 else if 
@@ -2409,24 +2409,18 @@
 Object)%0A
-        
+%09%09
 %09return 
@@ -2682,185 +2682,64 @@
 (), 
-value.toString()));%0A%09%7D%0A%0A%09@SuppressWarnings(%22unchecked%22)%0A%09private static %3CT%3E List%3CT%3E asList(T item) %7B%0A%09%09return (item != null) ? Arrays.%3CT%3EasList(item) : Collections.%3CT%3EemptyList(
+JSONObject.NULL.equals(value) ? null : value.toString())
 );%0A%09
@@ -3370,24 +3370,18 @@
 ption %7B%0A
-        
+%09%09
 String%5B%5D
@@ -3417,24 +3417,18 @@
 value);%0A
-        
+%09%09
 if (name
@@ -3442,39 +3442,146 @@
 ll)%0A
-        %09return null;%0A%0A        
+%09%09%09return new CompositeParsedAttribute(stripKey(key), (Environment) environment.clone(), Collections.%3CParsedAttribute%3C?%3E%3EemptyList());%0A%0A%09%09
 List
@@ -3649,24 +3649,18 @@
 %3C?%3E%3E();%0A
-        
+%09%09
 for (Str
@@ -3683,20 +3683,11 @@
 es)%0A
-            
+%09%09%09
 leaf
@@ -3751,16 +3751,10 @@
 );%0A%0A
-        
+%09%09
 retu

Fix for null attribute value