package org.project.openbaton.plugin.interfaces.main;

import org.project.openbaton.catalogue.nfvo.EndpointType;
import org.project.openbaton.catalogue.nfvo.PluginAnswer;
import org.project.openbaton.catalogue.nfvo.PluginEndpoint;
import org.project.openbaton.catalogue.nfvo.PluginMessage;
import org.project.openbaton.clients.interfaces.ClientInterfaces;
import org.project.openbaton.plugin.exceptions.PluginException;
import org.project.openbaton.plugin.interfaces.agents.PluginSender;
import org.project.openbaton.plugin.utils.AgentBroker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;

import javax.jms.MessageListener;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Properties;

/**
 * Created by lto on 13/08/15.
 */
public abstract class Plugin implements MessageListener {

    protected static Logger log = LoggerFactory.getLogger(Plugin.class);

    @Autowired
    private ConfigurableApplicationContext context;

    protected PluginSender pluginSender;

    protected String concurrency;

    protected Object pluginInstance;

    private EndpointType senderType;
    private EndpointType receiverType;

    protected PluginEndpoint endpoint;
    protected String type;

    protected String interfaceName;

    @Autowired
    private AgentBroker agentBroker;

    protected void loadProperties() throws IOException {
        Properties properties = new Properties();
        properties.load(pluginInstance.getClass().getResourceAsStream("/plugin.conf.properties"));
        this.senderType = getEndpointType(properties.getProperty("sender-type", "JMS").trim());
        this.receiverType = getEndpointType(properties.getProperty("receiver-type", "JMS").trim());
        this.type = properties.getProperty("type");
//        pluginEndpoint = properties.getProperty("endpoint");
        concurrency = properties.getProperty("concurrency", "1");
        endpoint = new PluginEndpoint();
        endpoint.setEndpoint(properties.getProperty("endpoint"));
        endpoint.setEndpointType(receiverType);
        endpoint.setType(type);
        try {
            endpoint.setInterfaceVersion((String) pluginInstance.getClass().getField("interfaceVersion").get(pluginInstance));
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        log.debug("Plugin instance is: " + pluginInstance);
        log.debug("Plugin instance class is: " + pluginInstance.getClass().getName());
        interfaceName = pluginInstance.getClass().getInterfaces()[0].getName();
        log.debug("interfaceName is: " + interfaceName);
        endpoint.setInterfaceClass(interfaceName);
        log.debug("Loaded properties: " + properties);
    }

    private EndpointType getEndpointType(String trim) {
        log.debug("Endpoint type is: " + trim);
        return EndpointType.valueOf(trim);
//        switch (trim) {
//            case("JMS"):
//                return EndpointType.JMS;
//            case ("REST"):
//                return EndpointType.REST;
//            default:
//                return EndpointType.JMS;
//        }
    }

    protected void setup(){
        setPluginInstance();
        try {
            loadProperties();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        pluginSender = agentBroker.getSender(senderType);
        register();
    }

    protected void shutdown(){
        unregister();
    }

    protected abstract void unregister();

    public void setPluginInstance(){
        pluginInstance = context.getBean(ClientInterfaces.class);
    }

    /**
     * This method will invoke the method defined in the <b>pluginMessage</b>.
     * it needs also to check for error coming from the nfvo.
     *
     * @param pluginMessage
     * @return the answer
     * @throws PluginException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    protected PluginAnswer onMethodInvoke(PluginMessage pluginMessage) throws PluginException, InvocationTargetException, IllegalAccessException {
        switch (pluginMessage.getMethodName()){
            case "ERROR":
                log.error("There was an error");
                log.error("message is: " + pluginMessage.getParameters().iterator().next());
                this.context.close();
                System.exit(1);
            case "CLOSE":
                this.context.close();
                System.exit(0);
            default:
                Object result = null;
                log.debug(pluginMessage.getInterfaceClass().getName() + " == " + interfaceName);
                if (pluginMessage.getInterfaceClass().getName().equals(interfaceName)){
                    for (Method m : pluginInstance.getClass().getMethods()){
                        if (m.getName().equals(pluginMessage.getMethodName())){
                            log.debug("Method name is " + m.getName());
                            log.debug("Method parameter types are: ");
                            for (Type t : m.getParameterTypes()){
                                log.debug("\t*) " + t.toString());
                            }
                            log.debug("Actual Parameters are: " + pluginMessage.getParameters());
                            result =  m.invoke(pluginInstance, pluginMessage.getParameters().toArray());
                        }
                    }
                }else throw new PluginException("Wrong interface!");

                PluginAnswer pluginAnswer = new PluginAnswer();
                pluginAnswer.setAnswer((Serializable) result);
                return pluginAnswer;
        }
    }

    protected abstract void register();
}

@@ -656,16 +656,67 @@
 owired;%0A
+import org.springframework.boot.ExitCodeGenerator;%0A
 import o
@@ -774,16 +774,16 @@
 ontext;%0A
-
 %0Aimport 
@@ -1012,16 +1012,170 @@
 rties;%0A%0A
+import org.springframework.context.support.AbstractApplicationContext;%0Aimport org.springframework.context.annotation.AnnotationConfigApplicationContext;%0A%0A
 /**%0A * C
@@ -2908,32 +2908,41 @@
 n(e);%0A        %7D%0A
+        %0A
         log.debu
@@ -4953,62 +4953,310 @@
     
-this.context.close();%0A                System.exi
+log.error(%22please wait the shutdown of the plugin......%22);%0A%0A                try %7B%0A                    Thread.currentThread().sleep(5000);%0A                %7D catch (InterruptedException e) %7B%0A                    e.printStackTrace();%0A                %7D%0A%0A                Thread.currentThread().interrup
 t(
-1
 );%0A
+%0A
     

add shutdown of the plugin with wrong version