/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package peaconnector;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import org.w3c.dom.Node;

/**
 *
 * @author Csubák Péter
 */
public abstract class Pea {

    private String schemaName = null;
    private String remoteOID = null;
    private PeaConnector connector = null;

    public Pea(PeaConnector connector) throws RemoteException{
        this.connector = connector;
        this.schemaName = connector.getSchemaName();
        remoteOID = this.construct();
    }

    public Pea(){
        if (this.getClass().isAnnotationPresent(Stateful.class)){
            throw new PeaException("This constructor only usable for Statless Peas!");
        }
        remoteOID = null;
    }

    private String construct() throws RemoteException{
        if (this.getClass().isAnnotationPresent(Stateful.class)){
            //session nyitás ha még nincs és példányosítás kérés
            if (connector != null){
                return connector.startSession(this.getClass());
            }else{
                throw new PeaException("The Pea Connector has to be setted by the constructor in every Stateful Pea.");
            }
        }else{
            return null;
        }
    }

    protected <E, F> E methodCall(String methodName, F... args) throws RemoteException{
        Class clazz = this.getClass();
        String xmlOut = null;
        SchemaMethodElement sme = null;
        ArrayList<Class> paramTypes = new ArrayList<Class>();
        for (F p : args){
            
            paramTypes.add(p.getClass());
        }
        Method m = null;
        try{
            m = clazz.getDeclaredMethod(methodName, (Class[]) paramTypes.toArray(new Class[paramTypes.size()]));
        }catch (Exception e){
            return null;
        }

        if (m.isAnnotationPresent(XmlMethodSchemas.class)){
            XmlMethodSchemas xs = m.getAnnotation(XmlMethodSchemas.class);
            SEMethod sem = null;
            for (SEMethod se : xs.value()) {
                if (se.schemaName().equals(this.schemaName)) {
                    sem = se;
                    break;
                }
            }
            if (sem == null) return null;
            sme = new SchemaMethodElement(sem);
            xmlOut = XmlSerializer.serializeMethod(methodName, this, sme, args);
        }else{
            return null;
        }

        if (connector != null){
            Node xmlIn = connector.sendMethodCall(xmlOut, this.getClass(), this.remoteOID);
            if (xmlIn.hasChildNodes() && xmlIn.getFirstChild().getNodeName().equals("exception")){
                RemoteException ex = XmlSerializer.deserializeException(xmlIn.getFirstChild());
                if (ex != null){
                    throw ex;
                }
            }

            if (sme.getReturnType() == null){
                if (m.getReturnType() == null){
                    return null;
                }
                if (m.getReturnType().isPrimitive()){
                    //primitív típusok
                    String cn = m.getReturnType().getCanonicalName();
                    if (cn.equals("int")) {
                            return (E) (Object) Integer.parseInt(xmlIn.getFirstChild().getNodeValue());
                        } else if (cn.equals("boolean")) {
                            return (E) (Object) Boolean.parseBoolean(xmlIn.getFirstChild().getNodeValue());
                        } else if (cn.equals("double")) {
                            return (E) (Object) Double.parseDouble(xmlIn.getFirstChild().getNodeValue());
                        } else if (cn.equals("float")) {
                            return (E) (Object) Float.parseFloat(xmlIn.getFirstChild().getNodeValue());
                        } else if (cn.equals("byte")) {
                            return (E) (Object) Byte.parseByte(xmlIn.getFirstChild().getNodeValue());
                        } else if (cn.equals("long")) {
                            return (E) (Object) Long.parseLong(xmlIn.getFirstChild().getNodeValue());
                        } else if (cn.equals("short")) {
                            return (E) (Object) Short.parseShort(xmlIn.getFirstChild().getNodeValue());
                        } else if (cn.equals("char")) {
                            return (E) (Object) xmlIn.getFirstChild().getNodeValue();
                        }else if (cn.equals("void")){
                            return null;
                        } else {
                            throw new SerializerException("Unsupported primitive type (It's an impossible Exception :-) )");
                        }
                }else{
                    if (String.class.isAssignableFrom(m.getReturnType())){
                        return (E) xmlIn.getFirstChild().getNodeValue();
                    }
                    return (E) XmlSerializer.deserializeObject(xmlIn, m.getReturnType(), schemaName);
                }
            }else{
                if (sme.getReturnType() instanceof SchemaListElement){
                    return (E) XmlSerializer.deserializeList(xmlIn, (Class<Collection>) m.getReturnType(),(SchemaListElement) sme.getReturnType());
                }else if (sme.getReturnType() instanceof SchemaMapElement){
                    return (E) XmlSerializer.deserializeMap(xmlIn, (Class<Map>) m.getReturnType(),(SchemaMapElement) sme.getReturnType());
                }else{
                    throw new SerializerException("Only use returnType in SchemaMethodElement when the return type is subclass of Collection (or Map), otherwise you get this exception. :-)");
                }
            }
        }else{
            return null;
        }
    }

    /**
     * @param connector the connector to set
     */
    public void setConnector(PeaConnector connector) {
        this.connector = connector;
    }

    //public abstract String getSchema(String schemaName);

}
