// ============================================================================
//
// Copyright (C) 2006-2019 Talend Inc. - www.talend.com
//
// This source code is available under agreement available at
// %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
//
// You should have received a copy of the agreement
// along with this program; if not, write to Talend SA
// 9 rue Pages 92150 Suresnes, France
//
// ============================================================================
package org.talend.repository.services;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.util.EList;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.talend.camel.core.model.camelProperties.CamelProcessItem;
import org.talend.camel.core.model.camelProperties.RouteletProcessItem;
import org.talend.commons.exception.BusinessException;
import org.talend.commons.exception.ExceptionHandler;
import org.talend.commons.exception.PersistenceException;
import org.talend.commons.ui.gmf.util.DisplayUtils;
import org.talend.core.CorePlugin;
import org.talend.core.GlobalServiceRegister;
import org.talend.core.IESBService;
import org.talend.core.model.general.Project;
import org.talend.core.model.metadata.builder.connection.Connection;
import org.talend.core.model.process.EParameterFieldType;
import org.talend.core.model.process.IElementParameter;
import org.talend.core.model.process.INode;
import org.talend.core.model.process.IProcess;
import org.talend.core.model.process.IProcess2;
import org.talend.core.model.properties.ConnectionItem;
import org.talend.core.model.properties.Item;
import org.talend.core.model.properties.ProcessItem;
import org.talend.core.model.properties.PropertiesFactory;
import org.talend.core.model.properties.Property;
import org.talend.core.model.repository.ERepositoryObjectType;
import org.talend.core.model.repository.IRepositoryViewObject;
import org.talend.core.repository.model.ProjectRepositoryNode;
import org.talend.core.repository.model.ProxyRepositoryFactory;
import org.talend.core.runtime.CoreRuntimePlugin;
import org.talend.core.utils.IXSDPopulationUtil;
import org.talend.core.utils.KeywordsValidator;
import org.talend.designer.core.IDesignerCoreService;
import org.talend.designer.core.model.components.EParameterName;
import org.talend.designer.core.model.components.EmfComponent;
import org.talend.designer.core.model.utils.emf.talendfile.ElementParameterType;
import org.talend.designer.core.model.utils.emf.talendfile.NodeType;
import org.talend.designer.core.model.utils.emf.talendfile.ProcessType;
import org.talend.designer.core.ui.AbstractMultiPageTalendEditor;
import org.talend.designer.core.ui.editor.cmd.ChangeValuesFromRepository;
import org.talend.designer.core.ui.editor.nodes.Node;
import org.talend.designer.maven.model.TalendMavenConstants;
import org.talend.designer.runprocess.IProcessor;
import org.talend.repository.ProjectManager;
import org.talend.repository.RepositoryPlugin;
import org.talend.repository.model.IProxyRepositoryFactory;
import org.talend.repository.model.IRepositoryNode;
import org.talend.repository.model.IRepositoryNode.EProperties;
import org.talend.repository.model.IRepositoryService;
import org.talend.repository.model.RepositoryConstants;
import org.talend.repository.model.RepositoryNode;
import org.talend.repository.model.RepositoryNodeUtilities;
import org.talend.repository.services.action.CreateNewJobAction;
import org.talend.repository.services.maven.OSGIJavaProcessor;
import org.talend.repository.services.maven.ServiceMavenJavaProcessor;
import org.talend.repository.services.model.services.ServiceConnection;
import org.talend.repository.services.model.services.ServiceItem;
import org.talend.repository.services.model.services.ServiceOperation;
import org.talend.repository.services.model.services.ServicePort;
import org.talend.repository.services.model.services.ServicesPackage;
import org.talend.repository.services.utils.ESBRepositoryNodeType;
import org.talend.repository.services.utils.LocalWSDLEditor;
import org.talend.repository.services.utils.OperationRepositoryObject;
import org.talend.repository.services.utils.PortRepositoryObject;
import org.talend.repository.services.utils.WSDLPopulationUtil;
import org.talend.repository.services.utils.WSDLUtils;
import org.talend.repository.utils.EmfModelUtils;

/**
 * DOC nrousseau class global comment. ESB SOAP Service
 */
public class ESBService implements IESBService {

    // public AbstractMetadataObject getServicesOperation(Connection connection, String operationName) {
    // List<ServiceOperation> list = new ArrayList<ServiceOperation>();
    // if (connection instanceof ServiceConnection) {
    // ServiceConnection serConnection = (ServiceConnection) connection;
    // EList<ServicePort> serPort = serConnection.getServicePort();
    // for (ServicePort port : serPort) {
    // list.addAll(port.getServiceOperation());
    // }
    // }
    // for (ServiceOperation ope : list) {
    // if (ope.getLabel().equals(operationName)) {
    // return ope;
    // }
    // }
    // return null;
    // }

    // public void changeOperationLabel(RepositoryNode newNode, INode node, Connection connection) {
    // if (!(connection instanceof ServiceConnection)) {
    // return;
    // }
    // ServiceConnection serConn = (ServiceConnection) connection;
    // changeOldOperationLabel(serConn, node);
    // changenewOperationLabel(newNode, node, serConn);
    // }

    private static final String GROUP_ID_ROUTE_SUFFIX = "route";

    private static final String GROUP_ID_SERVICE_SUFFIX = "service";

    private void changeOldOperationLabel(RepositoryNode topParent, INode node, ServiceOperation newOperation) {
        // here should be all the ports, not just ports of one connection
        List<IRepositoryNode> nodeList = topParent.getChildren();
        IElementParameter elePara = node.getElementParameter("PROPERTY:" + EParameterName.REPOSITORY_PROPERTY_TYPE.getName());
        if (elePara == null) {
            return;
        }
        ServiceConnection serConn = null;
        ServiceItem servicesItem = null;
        String paraValue = (String) elePara.getValue();
        if (paraValue == null || "".equals(paraValue)) {
            return;
        }
        String connID = null;
        if (paraValue.contains(" - ")) {
            connID = paraValue.split(" - ")[0];
        } else {
            connID = paraValue;
        }
        for (IRepositoryNode repNode : nodeList) {
            String id = repNode.getObject().getProperty().getId();
            if (id.equals(connID)) {
                servicesItem = (ServiceItem) repNode.getObject().getProperty().getItem();
                serConn = (ServiceConnection) servicesItem.getConnection();
                break;
            }
        }
        if (serConn == null) {
            return;
        }
        EList<ServicePort> portList = serConn.getServicePort();

        IElementParameter portPara = node.getElementParameter(WSDLUtils.PORT_NAME);
        IElementParameter opePara = node.getElementParameter(WSDLUtils.OPERATION_NAME);
        if (portPara != null && opePara != null) {
            String portValue = (String) portPara.getValue();
            String opeValue = (String) opePara.getValue();
            if (portValue != null && !"".equals(portValue) && opeValue != null && !"".equals(opeValue)) {
                out: for (ServicePort port : portList) {
                    if (port.getName().equals(portValue)) {
                        for (ServiceOperation ope : port.getServiceOperation()) {
                            if (ope.getName().equals(opeValue) && newOperation != null
                                    && !ope.getId().equals(newOperation.getId())) {
                                ope.setLabel(opeValue);
                                ope.setReferenceJobId(null);
                                if (servicesItem != null) {
                                    IProxyRepositoryFactory factory = ProxyRepositoryFactory.getInstance();
                                    try {
                                        factory.save(servicesItem);
                                    } catch (PersistenceException e) {
                                        ExceptionHandler.process(e);
                                    }
                                }
                                break out;
                            }
                        }
                    }
                }
            }
        }
    }

    @Override
    public String getWsdlFilePath(Item item) {
        if (item != null && item instanceof ServiceItem) {
            ServiceItem si = (ServiceItem) item;
            IFile wsdlFile = WSDLUtils.getWsdlFile(si);
            if (wsdlFile != null) {
                return wsdlFile.getLocation().toPortableString();
            }
        }
        return null;
    }

    // private void changenewOperationLabel(RepositoryNode newNode, INode node, ServiceConnection serConn) {
    // String operationName = newNode.getObject().getLabel();
    // String parentPortName = newNode.getParent().getObject().getLabel();
    //
    // String wsdlPath = serConn.getWSDLPath();
    // try {
    // Map<String, String> serviceParameters = WSDLUtils.getServiceParameters(wsdlPath);
    // IRepositoryViewObject newObj = newNode.getObject();
    // if (newObj instanceof OperationRepositoryObject) {
    // ServiceOperation newOpe = (ServiceOperation) ((OperationRepositoryObject) newObj).getAbstractMetadataObject();
    //
    // IProxyRepositoryFactory factory = ProxyRepositoryFactory.getInstance();
    //
    // if (newOpe.getReferenceJobId() != null) {
    // changeOtherJobSchemaValue(factory, newOpe, serConn);
    // MessageDialog.openWarning(DisplayUtils.getDefaultShell(false), "warning",
    // "This other job which based on the Operation will be unset!");
    // }
    //
    // IEditorPart activeEditor =
    // PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
    // IEditorInput input = activeEditor.getEditorInput();
    // if (input instanceof ProcessEditorInput) {
    // Item jobItem = ((ProcessEditorInput) input).getItem();
    // String jobID = jobItem.getProperty().getId();
    // String jobName = jobItem.getProperty().getLabel();
    //
    // newOpe.setReferenceJobId(jobID);
    // newOpe.setLabel(newOpe.getName() + "-" + jobName);
    //
    // serviceParameters.put(WSDLUtils.PORT_NAME, parentPortName);
    // serviceParameters.put(WSDLUtils.OPERATION_NAME, operationName);
    //
    // CreateNewJobAction.setProviderRequestComponentConfiguration(node, serviceParameters);
    //
    // try {
    // factory.save(jobItem);
    // } catch (PersistenceException e) {
    // e.printStackTrace();
    // }
    // try {
    // factory.save(newNode.getParent().getParent().getObject().getProperty().getItem());
    // } catch (PersistenceException e) {
    // e.printStackTrace();
    // }
    // RepositoryManager.refreshSavedNode(newNode);
    // }
    // }
    //
    // } catch (CoreException e1) {
    // ExceptionHandler.process(e1);
    // } catch (PersistenceException e) {
    // ExceptionHandler.process(e);
    // }
    // }

    private void changeOtherJobSchemaValue(IProxyRepositoryFactory factory, ServiceOperation newOpe, /*
                                                                                                      * ServiceConnection
                                                                                                      * serConn,
                                                                                                      */
            RepositoryNode selectNode) throws PersistenceException, CoreException {
        IRepositoryViewObject jobObj = factory.getLastVersion(newOpe.getReferenceJobId());
        if (jobObj == null) {
            return;
        }
        ProcessItem processItem = (ProcessItem) jobObj.getProperty().getItem();

        IDesignerCoreService service = CorePlugin.getDefault().getDesignerCoreService();
        boolean foundInOpen = false;

        IProcess2 process = null;
        IEditorReference[] reference = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getEditorReferences();
        List<IProcess2> processes = RepositoryPlugin.getDefault().getDesignerCoreService().getOpenedProcess(reference);
        for (IProcess2 processOpen : processes) {
            if (processOpen.getProperty().getItem() == processItem) {
                foundInOpen = true;
                process = processOpen;
                break;
            }
        }
        if (!foundInOpen) {
            IProcess proc = service.getProcessFromProcessItem(processItem);
            if (proc instanceof IProcess2) {
                process = (IProcess2) proc;
            }
        }

        newOpe.setReferenceJobId(null);
        newOpe.setLabel(newOpe.getName());

        if (process != null) {
            List<? extends INode> nodelist = process.getGraphicalNodes();
            for (INode node : nodelist) {
                if (node.getComponent().getName().equals("tESBProviderRequest")) {
                    repositoryChange(selectNode, node, process);
                    break;
                }
            }
            try {
                if (!foundInOpen) {
                    processItem.setProcess(process.saveXmlFile());
                    factory.save(processItem);
                }
            } catch (PersistenceException e) {
                ExceptionHandler.process(e);
            } catch (IOException e) {
                ExceptionHandler.process(e);
            }
        }

        // ProcessType process = item.getProcess();
        // EList<NodeType> nodeList = process.getNode();
        //
        // for (NodeType node : nodeList) {
        // EList parameters = node.getElementParameter();
        // for (Object paramObj : parameters) {
        // ElementParameterType param = (ElementParameterType) paramObj;
        // String name = param.getName();
        // if (name.equals(WSDLUtils.OPERATION_NAME)) {
        // if (!newOpe.getName().equals(param.getValue())) {
        // break;
        // }
        // param.setValue(null);
        // }
        // if (name.equals("SCHEMA:SCHEMA_TYPE")) {
        // param.setValue("BUILT_IN");
        // break;
        // }
        //
        // }
        //
        // }
        // factory.save(item);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.talend.core.IESBService#getServicesType()
     */
    @Override
    public ERepositoryObjectType getServicesType() {
        return ESBRepositoryNodeType.SERVICES;
    }

    @Override
    public String getServiceLabel(Item item, String linkedRepository) {
        String serviceName = item.getProperty().getLabel();
        if (item instanceof ServiceItem) {
            String[] ids = linkedRepository.split(" - ");
            ServiceConnection serConn = (ServiceConnection) ((ServiceItem) item).getConnection();
            if (ids.length == 3) {
                String portName = "";
                String operationName = "";
                EList<ServicePort> portList = serConn.getServicePort();
                out: for (ServicePort port : portList) {
                    if (port.getId().equals(ids[1])) {
                        portName = port.getName();
                        EList<ServiceOperation> opeList = port.getServiceOperation();
                        for (ServiceOperation ope : opeList) {
                            if (ope.getId().equals(ids[2])) {
                                operationName = ope.getName();
                                return serviceName + " - " + portName + " - " + operationName;
                            }
                        }
                    }
                }
            }

        }
        return serviceName;
    }

    @Override
    public void updateOperation(INode node, String linkedRepository, RepositoryNode selectNode) {
        String[] ids = linkedRepository.split(" - ");
        if (ids.length == 3) {
            try {
                IRepositoryViewObject reViewObject = ProxyRepositoryFactory.getInstance().getLastVersion(ids[0]);
                ServiceItem servicesItem = (ServiceItem) reViewObject.getProperty().getItem();
                ServiceConnection serConn = (ServiceConnection) servicesItem.getConnection();

                String portName = "";
                EList<ServicePort> portList = serConn.getServicePort();
                ServiceOperation operation = null;
                for (ServicePort port : portList) {
                    if (port.getId().equals(ids[1])) {
                        portName = port.getName();
                        EList<ServiceOperation> opeList = port.getServiceOperation();
                        for (ServiceOperation ope : opeList) {
                            if (ope.getId().equals(ids[2])) {
                                operation = ope;
                                break;
                            }
                        }
                        break;
                    }
                }

                if (operation == null) {
                    return;
                }

                RepositoryNode topParent = getServicesTopNode(selectNode);
                changeOldOperationLabel(topParent, node, operation);

                IProxyRepositoryFactory factory = ProxyRepositoryFactory.getInstance();

                IProcess2 process = (IProcess2) RepositoryPlugin.getDefault().getDesignerCoreService().getCurrentProcess();
                Item jobItem = process.getProperty().getItem();
                String jobID = jobItem.getProperty().getId();
                String jobName = jobItem.getProperty().getLabel();

                if (operation.getReferenceJobId() != null && !operation.getReferenceJobId().equals(jobID)) {
                    changeOtherJobSchemaValue(factory, operation, /* serConn, */selectNode);
                    MessageDialog.openWarning(DisplayUtils.getDefaultShell(false), Messages.ESBService_DisconnectWarningTitle,
                            Messages.ESBService_DisconnectWarningMsg);
                }

                operation.setReferenceJobId(jobID);
                operation.setLabel(operation.getName() + '-' + jobName);

                IFile wsdlPath = WSDLUtils.getWsdlFile(selectNode);