package client_engine;

import hu.swankey.ammo.common.controll.AmmoException;
import hu.swankey.ammo.common.script.Synthetizer;
import hu.swankey.ammo.common.script.statements.Statement;
import hu.swankey.ammo.common.script.yunits.YClass;
import hu.swankey.ammo.common.yggdrasil.Yggdrasil;
import hu.swankey.ammo.common.yggdrasil.basics.BooleanField;
import hu.swankey.ammo.common.yggdrasil.basics.ComplexYObject;
import hu.swankey.ammo.common.yggdrasil.basics.IntegerField;
import hu.swankey.ammo.common.yggdrasil.basics.Reference;
import hu.swankey.ammo.common.yggdrasil.basics.StringField;
import hu.swankey.ammo.common.yggdrasil.basics.YObject;
import hu.swankey.ammo.common.yggdrasil.definition.Definition;
import hu.swankey.ammo.common.yggdrasil.ext.Command;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Commander {

	private static final String EXECUTION_TERMINATED = "Execution terminated.";
	private static final String MSG_NO_PRIMARY_SELECTION = "Execution terminated: No primary element selected.";
	
//	private static final String GENERAL_VAR = "%([SBIC])\\(([\\w\\s]+(\\:([\\w\\s\\._]+))?)\\)";
	private static final String GENERAL_VAR = "%([SBIC])\\(([\\w\\s]+)(\\:([\\w\\s\\._]+))?\\)";
	private static final Pattern patternGeneralVar = Pattern.compile(GENERAL_VAR);

	private static final char VAR_STRING = 'S';
	private static final char VAR_INTEGER = 'I';
	private static final char VAR_BOOLEAN = 'B';
	private static final char VAR_CODE = 'C';
	private static final String VALUE_TRUE = "true";
	private static final String VALUE_FALSE = "false";

	private static final String VAR_PRIMARY = "%1";
	private static final String VAR_SECONDARY = "%2";

	private static final String VAR_CURSORX = "%x";
	private static final String VAR_CURSORY = "%y";
	
	public static final String CMD_ASSIGN_INTEGER = "Assign Integer";

	private final ClientController controller;
	

	public Commander(ClientController controller) {
		this.controller = controller;
	};


	
	
	public static final int STATUS_OK = 0;
	public static final int STATUS_SYNTAX_ERROR = -1;
	public static final int STATUS_PRIMARY_CLASS_NOT_FOUND = 1;
	public static final int STATUS_SECONDARY_CLASS_NOT_FOUND = 2;
	public static final int STATUS_UNEXPECTED_PRIMARY_CLASS = 3;
	public static final int STATUS_UNEXPECTED_SECONDARY_CLASS = 4;
	public static final int STATUS_PRIMARY_ELEMENT_NULL = 5;
	public static final int STATUS_SECONDARY_CLASS_NULL = 6;
	
//	public static c
	
	
	public int getCommandStatus(Command cmd){
		
		if (controller.getPrimaryElement() == null)
			return STATUS_PRIMARY_ELEMENT_NULL;
		
		if (controller.getSecondaryElement() == null)
			return STATUS_SECONDARY_CLASS_NULL;
		
		YClass expectedPrimary = YClass.getYClass(cmd.getPrimaryClass());
		YClass expectedSecondary = YClass.getYClass(cmd.getSecondaryClass());
		
		if (expectedPrimary == null)
			return STATUS_PRIMARY_CLASS_NOT_FOUND;
		
		if (expectedSecondary == null)
			return STATUS_SECONDARY_CLASS_NOT_FOUND;
		
		if ( !expectedPrimary.isTypeOf(controller.getPrimaryElement()) )
			return STATUS_UNEXPECTED_PRIMARY_CLASS;
			
		if ( !expectedSecondary.isTypeOf(controller.getSecondaryElement()))
			return STATUS_UNEXPECTED_SECONDARY_CLASS;
		
		String script = createScript(cmd.getCommand(), controller.getPrimaryElement(), false);
		if (script == null)
			return STATUS_SYNTAX_ERROR;
		
//		Statement statement = Syntetizer.createStatement(script);
//		
//		
//		Map<String, YObject> env = generateEnvrionment();
//		Map<String, YClass> envTypes = new HashMap<String, YClass>();
//		for(String id: env.keySet())
//			envTypes.put(id, env.get(id).getYClass());
		
		return STATUS_OK;
	}
	
	
	public YObject execute(String command) {
				
		if (controller.getPrimaryElement() == null)
			throw new AmmoException(MSG_NO_PRIMARY_SELECTION);

		YObject thisElement = controller.getPrimaryElement();

		Map<String, YObject> envrionment = generateEnvrionment();

		String script = createScript(command, thisElement, true);
		
		if (script != null) { 
			Statement statement = Synthetizer.createStatement(script);
			YObject result = statement.evaulate(envrionment, thisElement);
			controller.getUI().printToConsole( thisElement.getPathString() + YObject.DELIMITER + script );
			return result;
		}
		
		controller.printToConsole(EXECUTION_TERMINATED);
		return null;
	}
	
	private Map<String, YObject> generateEnvrionment(){
		Map<String, YObject> env = new HashMap<String, YObject>();
		env.put(Yggdrasil.VAR_WORLD, controller.getWorld());
		return env;
	}

	public ComplexYObject getLocation() {
		YObject location = controller.getPrimaryElement();

		while (!(location instanceof ComplexYObject) && location != null)
			location = location.getParent();
		
		return (ComplexYObject) location;
	}

	private String createScript(String command, YObject location, boolean askForUser) {
		
		// Primary selection:
		command = command.replaceAll(VAR_PRIMARY, controller.getPrimaryElement().getPathString());
		
		// Secondary selection:
		if (command.contains(VAR_SECONDARY)) {
//			if (controller.getSecondaryElement() == null)
//				throw new AmmoCommandException("");
//				return null;
				//throw new AmmoScriptRuntimeException("Cannot execute the command without second selection!");
				
				String secondaryPath = controller.getSecondaryElement().getPathString();
				command = command.replaceAll(VAR_SECONDARY, secondaryPath);
		}

		
		// Cursor:
		String coordx = "0";
		String coordy = "0";
		if (controller.getCursor() != null) {
			coordx = new Integer(controller.getCursor().width).toString();
			coordy = new Integer(controller.getCursor().height).toString();
		}
		command = command.replaceAll(VAR_CURSORX, coordx);
		command = command.replaceAll(VAR_CURSORY, coordy);		
		
		
		
		// Dialogs:

		Matcher matcher = patternGeneralVar.matcher(command);
		matcher.useAnchoringBounds(false);

		while (matcher.find()) {

			String varType = matcher.group(1);
			String varName = matcher.group(2);
			
			Reference defaultValueLink = new Reference(matcher.group(4));
			
						
//			if (controller.getSecondaryElement() == null ||
//					!defaultValueLink.isValid(controller.getSecondaryElement(), generateEnvrionment()))
//				return null;
			
			YObject defaultValueObject = defaultValueLink.getFinalTarget(controller.getPrimaryElement(), generateEnvrionment());

			String value = null;

			switch (varType.charAt(0)) {
			case VAR_STRING:
				if (askForUser) {
					value = controller.getUI().askForString(varName + ":", "");
					if (value != null)
						value = "\"" + value + "\""; // TODO: egyetlen helyen
														// tárolni az idézőjelet
				} else
					value = "\"" + StringField.yclass().getDefaultValue()
							+ "\"";

				break;

			case VAR_INTEGER:
				if (askForUser) {
					Integer integer = controller.getUI().askForInteger(varName + ":");
					if (integer != null)
							value = integer.toString();
				}
				else
					value = IntegerField.yclass().getDefaultValue().toString();
				break;

			case VAR_BOOLEAN:
				if (askForUser)
					value = (controller.getUI().askForBoolean(varName + "?") ? VALUE_TRUE
							: VALUE_FALSE);
				else
					value = BooleanField.yclass().getDefaultValue().toString();
				break;
				
			case VAR_CODE:
				//String defaultValue = (defaultValueObject != null ? ((StringField)defaultValueObject).getValue() : StringField.yclass().getDefaultValue() );
				if (askForUser) {					
					value = controller.getUI().askForCode((Definition)controller.getSecondaryElement());
					if (value != null)
						value = "\"" + value + "\"";
				} else
					value = controller.getSecondaryElement().getYClass().getWrapperElement().getSource();
				break;
			default:
				throw new RuntimeException("Unknown variable type");
			}

			if (value == null)
				return null;

			command = command.replaceFirst(GENERAL_VAR, value);
		}

		


		return command;
	}
	
	
//	public Command getCreateCommand(){
//		return new Command("Create Element", "create(name:%S(name), class:%2, container:%1)", controller);
//	}
//	
//	public Command getCreateObjectCommand(){
//		return new Command("Create Object", "create(name:%S(name), class:%2, targetX:%x, targetY:%y)", controller);
//	}
//	
//	public Command getCreateLinkCommand(){
//		return new Command("Create Link", "link(name:%S(name), container:%1, target:%2)", controller);
//	}

}
