package org.pikater.core.agents.system;

import jade.content.lang.Codec.CodecException;
import jade.content.onto.Ontology;
import jade.content.onto.OntologyException;
import jade.content.onto.basic.Action;
import jade.domain.FIPAAgentManagement.NotUnderstoodException;
import jade.domain.FIPAAgentManagement.RefuseException;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.proto.AchieveREResponder;
import jade.wrapper.AgentController;
import jade.wrapper.ControllerException;
import jade.wrapper.PlatformController;

import org.pikater.core.agents.system.management.AgentTypeDefinition;
import org.pikater.core.agents.system.management.AgentTypesProvider;
import org.pikater.core.agents.system.management.ManagerAgentRequestResponder;
import org.pikater.core.agents.configuration.Argument;
import org.pikater.core.agents.AgentNames;
import org.pikater.core.agents.PikaterAgent;
import org.pikater.core.ontology.AgentManagementOntology;
import org.pikater.core.ontology.subtrees.management.CreateAgent;
import org.pikater.core.ontology.subtrees.management.LoadAgent;
import org.pikater.core.ontology.subtrees.management.SaveAgent;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;


public class Agent_ManagerAgent extends PikaterAgent {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 4898611781694963107L;

	private Map<String, AgentTypeDefinition> agentTypes = new HashMap<>();
    private AgentTypesProvider agentTypesProvider=(AgentTypesProvider)context.getBean(AgentNames.TYPES_PROVIDER);
    private ManagerAgentRequestResponder responder=new ManagerAgentRequestResponder(this);

    public static String saveDirectoryPath =
    		"core" + System.getProperty("file.separator") +
    		"saved" + System.getProperty("file.separator");
    
    
	@Override
	public java.util.List<Ontology> getOntologies() {
		
		List<Ontology> ontologies = new ArrayList<Ontology>();
		
		//ontologies.add(MessagesOntology.getInstance());
		ontologies.add(AgentManagementOntology.getInstance());
		
		return ontologies;
	}
    
	@Override
	protected void setup() {
        initDefault();
		File data = new File(saveDirectoryPath);
        if (!data.exists()) {
            log("Creating directory saved");
            if (data.mkdirs()) {
                log("Succesfully created directory saved");
            } else {
                logError("Error creating directory saved");
            }
        }

        registerWithDF();
        
		getAgentTypesFromFile();
		
		Ontology ontology = AgentManagementOntology.getInstance();
		MessageTemplate mt = MessageTemplate.and(MessageTemplate
				.MatchOntology(ontology.getName()), MessageTemplate
				.MatchPerformative(ACLMessage.REQUEST));

		addBehaviour(new AchieveREResponder(this, mt) {

			private static final long serialVersionUID = 7L;

			@Override
			protected ACLMessage handleRequest(ACLMessage request)
					throws NotUnderstoodException, RefuseException {
				try {
					Action a = (Action) getContentManager().extractContent(request);
                    if (a.getAction() instanceof LoadAgent) {
                        return responder.RespondToLoadAction(request);
					}
					else if (a.getAction() instanceof SaveAgent) {
						// write it into database
                        return responder.RespondToSaveAction(request);
					}
				 	else if (a.getAction() instanceof CreateAgent){
                        return responder.RespondToCreateAction(request);
					}
				} catch (OntologyException e) {
					e.printStackTrace();
					logError("Problem extracting content: " + e.getMessage());
				} catch (CodecException e) {
					e.printStackTrace();
					logError("Codec problem: " + e.getMessage());
				} catch (Exception e) {
					e.printStackTrace();
				}

				ACLMessage failure = request.createReply();
				failure.setPerformative(ACLMessage.FAILURE);
				logError("Failure responding to request: " + request.getContent());
				return failure;
			}
		});
	}
    
	public String createAgent(String type, String name, List<Argument> args){
		// get a container controller for creating new agents
		PlatformController container = getContainerController();

		Argument[] args1 = new Argument[0];
        Argument[] args2 = new Argument[0];

		if (agentTypes.get(type).getOptions() != null){
			args1 = agentTypes.get(type).getOptions();
		}
		
		if (args != null){
            args2=new Argument[args.size()];
            for (int i=0;i<args.size();i++)
            {
                args2[i]=(Argument)args.get(i);
            }
		}

		int size = args1.length + args2.length;
		Argument[] Args = new Argument[size];
        System.arraycopy(args1,0,Args,0,args1.length);
        System.arraycopy(args2,0,Args,args1.length,args2.length);
		String nameToGenerate=name = generateName(name);
			try {
                String agentType=agentTypes.get(type).getTypeName();
                if (agentType==null){
                    agentType=type;
                }

                // TODO:
                // THIS IS HACK OF SOME BUG
                if (agentType.startsWith("pikater.Agent_OptionsManager") ) {
                        agentType = "pikater.Agent_OptionsManager";
                }
                
				AgentController agent = container.createNewAgent(name, agentType, Args);
				agent.start();
			} catch (ControllerException e) {
				 e.printStackTrace();
            }
		// provide agent time to register with DF etc.
		doWait(300);
		return nameToGenerate;
	}

	private String generateName(String name) {
        PlatformController container = getContainerController();
        try {
            AgentController agentWithTheSameName= container.getAgent(name);
        }
        catch (ControllerException exc)
        {
            //agent with the same name does not exist, we are good
            return name;
        }

        for (Integer i=0; i<10000; i++)
        {
            String currentName = name+i.toString();
            try {
                //TODO: write without exceptions
                AgentController agentWithTheSameName= container.getAgent(currentName);
            }
            catch (ControllerException exc)
            {
                //agent with the same name does not exist, we are good
                name = currentName;
                break;
            }
        }
        return name;
	}
	
	private void getAgentTypesFromFile(){
		// Sets up a file reader to read the agent_types file
		agentTypes= agentTypesProvider.GetAgentTypes();
	}
}
@@ -5552,17 +5552,20 @@
 te;%0A%09%7D%0A%0A
-%09
+    
 private 
@@ -5595,24 +5595,131 @@
 ing name) %7B%0A
+        if (nodeName != null && !nodeName.isEmpty()) %7B%0A            name = name + %22-%22 + nodeName;%0A        %7D%0A
         Plat

add suffix for agents created on slave nodes