/*
 * Copyright 2015 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

package org.jbpm.eclipse.action;

import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.drools.compiler.builder.impl.KnowledgeBuilderConfigurationImpl;
import org.kie.api.definition.process.Node;
import org.kie.api.definition.process.NodeContainer;
import org.kie.api.definition.process.Process;
import org.drools.core.xml.SemanticModules;
import org.eclipse.core.internal.resources.File;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.jbpm.bpmn2.xml.BPMNDISemanticModule;
import org.jbpm.bpmn2.xml.BPMNExtensionsSemanticModule;
import org.jbpm.bpmn2.xml.BPMNSemanticModule;
import org.jbpm.compiler.xml.XmlProcessReader;
import org.jbpm.compiler.xml.processes.RuleFlowMigrator;
import org.jbpm.eclipse.JBPMEclipsePlugin;
import org.jbpm.process.core.context.variable.Variable;
import org.jbpm.process.core.context.variable.VariableScope;
import org.jbpm.ruleflow.core.RuleFlowProcess;
import org.jbpm.workflow.core.node.HumanTaskNode;

public class GenerateForms implements IObjectActionDelegate {

    private IFile file;
    private IWorkbenchPart targetPart;
    
    public void setActivePart(IAction action, IWorkbenchPart targetPart) {
        this.targetPart = targetPart;
    }

    public void run(IAction action) {
        if (file != null && file.exists()) {
            try {
                generateForms();
            } catch (Throwable t) {
                JBPMEclipsePlugin.log(t);
            }
        }

    }

    public void selectionChanged(IAction action, ISelection selection) {
        if (selection instanceof IStructuredSelection) {
            IStructuredSelection structured = (IStructuredSelection) selection;
            if (structured.size() == 1) {
                Object element = structured.getFirstElement();
                if (element instanceof IFile) {
                    file = (IFile) element;
                }
            }
        }
    }

    public void generateForms() {
        try {
            final IJavaProject javaProject = JavaCore.create(file.getProject());
            if (javaProject == null || !javaProject.exists()) {
                return;
            }
            InputStreamReader isr = new InputStreamReader(((File) file).getContents());
            KnowledgeBuilderConfigurationImpl configuration = new KnowledgeBuilderConfigurationImpl();
            SemanticModules modules = configuration.getSemanticModules();
            modules.addSemanticModule(new BPMNSemanticModule());
            modules.addSemanticModule(new BPMNDISemanticModule());
            modules.addSemanticModule(new BPMNExtensionsSemanticModule());
            XmlProcessReader xmlReader = new XmlProcessReader( modules, Thread.currentThread().getContextClassLoader() );
            String xml = RuleFlowMigrator.convertReaderToString(isr);
            Reader reader = new StringReader(xml);
            List<Process> processes = xmlReader.read(reader);
            if (processes != null && processes.size() == 1) {
            	final RuleFlowProcess process = (RuleFlowProcess) processes.get(0);
            	List<HumanTaskNode> result = new ArrayList<HumanTaskNode>();
            	processNodes(process.getNodes(), result);
            	final Map<String, TaskDef> tasks = new HashMap<String, TaskDef>();
            	for (HumanTaskNode node: result) {
            		String taskName = (String) node.getWork().getParameter("TaskName");
            		if (taskName == null) {
            			break;
            		}
            		TaskDef task = tasks.get(taskName);
            		if (task == null) {
            			task = new TaskDef(taskName);
            			tasks.put(taskName, task);
            		}
            		for (Map.Entry<String, String> entry: node.getInMappings().entrySet()) {
            			if (task.getInputParams().get(entry.getKey()) == null) {
            				VariableScope variableScope = (VariableScope) node.resolveContext(VariableScope.VARIABLE_SCOPE, entry.getValue());
            	        	if (variableScope != null) {
            	        		task.getInputParams().put(entry.getKey(), variableScope.findVariable(entry.getValue()).getType().getStringType());
            	        	}
            			}
            		}
            		for (Map.Entry<String, String> entry: node.getOutMappings().entrySet()) {
            			if (task.getOutputParams().get(entry.getKey()) == null) {
            				VariableScope variableScope = (VariableScope) node.resolveContext(VariableScope.VARIABLE_SCOPE, entry.getValue());
            	        	if (variableScope != null && !"outcome".equals(entry.getKey())) {
            	        		task.getOutputParams().put(entry.getKey(), variableScope.findVariable(entry.getValue()).getType().getStringType());
            	        	}
            			}
            		}
            	}
                WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
                    public void execute(final IProgressMonitor monitor)
                            throws CoreException {
                        try {
                            IFolder folder = file.getProject().getFolder("src/main/resources");
                        	for (TaskDef task: tasks.values()) {
	                        	String fileName = task.getTaskName() + ".ftl";
	                        	String output = 
	                        		"<html>\n" +
	                        		"<body>\n" +
	                        		"<h2>" + task.getTaskName() + "</h2>\n" +
	                        		"<hr>\n" +
	                        		"<#if task.descriptions[0]??>\n" +
	                        		"Description: ${task.descriptions[0].text}<BR/>\n" +
	                        		"</#if>\n";
	                        	for (String input: task.getInputParams().keySet()) {
	                        		output += input + ": ${" + input + "}<BR/>\n"; 
	                        	}
	                        	output +=
	                        		"<form action=\"complete\" method=\"POST\" enctype=\"multipart/form-data\">\n";
	                        	for (String outputP: task.getOutputParams().keySet()) {
	                        		output += outputP + ": <input type=\"text\" name=\"" + outputP + "\" /><BR/>\n"; 
	                        	}
	                        	output +=
	                        		"<BR/>\n" +
	                        		"<input type=\"submit\" name=\"outcome\" value=\"Complete\"/>\n" +
	                        		"</form>\n" +
	                        		"</body>\n" +
	                        		"</html>";
	                        	IFile file = folder.getFile(fileName);
	                        	if (!file.exists()) {
	                                file.create(new ByteArrayInputStream(output.getBytes()), true, monitor);
	                            } else {
	                                file.setContents(new ByteArrayInputStream(output.getBytes()), true, false, monitor);
	                            }
                        	}
                        	String fileName = process.getId() + ".ftl";
                        	String output = 
                        		"<html>\n" +
                        		"<body>\n" +
                        		"<h2>" + process.getName() + "</h2>\n" +
                        		"<hr>\n" + 
                        		"<form action=\"complete\" method=\"POST\" enctype=\"multipart/form-data\">\n";
                        	for (Variable variable: process.getVariableScope().getVariables()) {
                        		if ("String".equals(variable.getType().getStringType())) {
                        			output += variable.getName() + ": <input type=\"text\" name=\"" + variable.getName() + "\" /><BR/>\n";
                        		}
                        	}
                        	output +=
                        		"<input type=\"submit\" value=\"Complete\"/>\n" +
                        		"</form>\n" +
                        		"</body>\n" +
                        		"</html>";
                        	IFile file = folder.getFile(fileName);
                        	if (!file.exists()) {
                                file.create(new ByteArrayInputStream(output.getBytes()), true, monitor);
                            } else {
                                file.setContents(new ByteArrayInputStream(output.getBytes()), true, false, monitor);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                };

                try {
                    new ProgressMonitorDialog(targetPart.getSite().getShell()).run(false, true, op);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
    
    private static void processNodes(Node[] nodes, List<HumanTaskNode> result) {
    	for (Node node: nodes) {
    		if (node instanceof HumanTaskNode) {
    			result.add((HumanTaskNode) node);
    		} else if (node instanceof NodeContainer) {
    			processNodes(((NodeContainer) node).getNodes(), result);
    		}
    	}
    }
    
    private class TaskDef {
    
    	private String taskName;
    	private Map<String, String> inputParams = new HashMap<String, String>();
    	private Map<String, String> outputParams = new HashMap<String, String>();
    	
    	public TaskDef(String taskName) {
    		this.taskName = taskName;
    	}

		public String getTaskName() {
			return taskName;
		}

		public Map<String, String> getInputParams() {
			return inputParams;
		}

		public Map<String, String> getOutputParams() {
			return outputParams;
		}
    	
    }

}
