/*******************************************************************************
 * Copyright (c) 2005-2011
 * University of Augsburg, Germany <www.ds-lab.org>
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Christian Seitz, Programming distributed Systems Lab, University of Augsburg - initial API and implementation
 *    Christian Saad, Programming distributed Systems Lab, University of Augsburg - smaller changes
 *******************************************************************************/

package org.eclipse.jwt.we.plugins.doc.actions;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jwt.meta.model.application.Application;
import org.eclipse.jwt.meta.model.core.Model;
import org.eclipse.jwt.meta.model.data.Data;
import org.eclipse.jwt.meta.model.organisations.Role;
import org.eclipse.jwt.meta.model.processes.Activity;
import org.eclipse.jwt.we.PluginProperties;
import org.eclipse.jwt.we.editors.WEEditor;
import org.eclipse.jwt.we.editors.actions.external.WEExternalAction;
import org.eclipse.jwt.we.editors.pages.activityEditor.WEEditorSheet;
import org.eclipse.jwt.we.misc.util.GeneralHelper;
import org.eclipse.jwt.we.misc.util.SaveImage;
import org.eclipse.jwt.we.plugins.doc.Activator;
import org.eclipse.jwt.we.plugins.doc.HtmlDocumentation;
import org.eclipse.jwt.we.plugins.doc.Messages;
import org.eclipse.jwt.we.plugins.doc.PreferenceConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.FileDialog;
import org.osgi.framework.Bundle;



/**
 * The action that starts the generation of the HTML documentation. A file dialog is
 * opened to let the user specify the location where the documentation should be stored.
 * Then the generation is done and the complete documentation saved to disk.
 * 
 * @author Christian Seitz (chr.seitz@gmx.de), Programming distributed Systems Lab,
 *         University of Augsburg, Germany, www.ds-lab.org.
 */
public class DocAction
		extends WEExternalAction
{

	/**
	 * The model the documentation has to be generated
	 */
	protected Model model = null;

	/**
	 * List of packages
	 */
	protected List packages = new ArrayList();

	/**
	 * List of roles
	 */
	protected List roles = new ArrayList();
	
	
	/**
	 * List of data
	 */
	protected List data = new ArrayList();
	
	/**
	 * List of applications
	 */
	protected List applications = new ArrayList();

	/**
	 * List of Processes
	 */
	protected List processes = new ArrayList();
	
	

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jwt.we.editors.actions.external.WEExternalAction#getImage()
	 */
	@Override
	public ImageDescriptor getImage()
	{

		return Activator.getImageDescriptor("icons/documentation.gif");
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jwt.we.editors.actions.external.WEExternalAction#run()
	 */
	@Override
	public void run()
	{

		packages.clear();
		roles.clear();
		applications.clear();
		data.clear();
		processes.clear();
		
		
		//get the location where to store the documentation
		String filepath = "";  //$NON-NLS-1$
		URL stylefullpath = null;
		String result = null;
		String[] filterExtensions =
		{ "*.html" }; //$NON-NLS-1$
		boolean fileDoesExist = false;
		FileDialog filedialog = new FileDialog(GeneralHelper.getActiveShell(), SWT.SAVE);
		filedialog.setFilterExtensions(filterExtensions);
		// ask until filepath == null or existing file should be overwritten
		do
		{
			filepath = filedialog.open();

			if (filepath != null && (new File(filepath)).exists())
			{
				fileDoesExist = !MessageDialog.openQuestion(GeneralHelper.getActiveShell(),
						PluginProperties.dialog_overwrite_title,
						PluginProperties.dialog_overwrite_message);
			}

		}
		while (filepath != null && fileDoesExist);
		
		
		if (filepath != null) {
			
		
		IPath path = new Path(filepath);
		if (path.getFileExtension() == null) path = path.addFileExtension("html"); //$NON-NLS-1$
		
		//check if there is a stylesheet
    	String finalpath = null;
    	IPreferenceStore preferenceStore = Activator.getDefault().getPreferenceStore();
    	if (preferenceStore.contains(PreferenceConstants.P_CSSPATH)) {
    		finalpath = Activator.getDefault().getPreferenceStore().getString(PreferenceConstants.P_CSSPATH);
    	}
    	else {
    		try {
    		Activator defaultplugin = Activator.getDefault();
			
			Bundle bundle = defaultplugin.getBundle();
			URL stylepath = bundle.getEntry(""); //$NON-NLS-1$
			
			if (stylepath != null)
			{
				stylefullpath = FileLocator.resolve(stylepath);
				//normal case
				if (stylefullpath.getProtocol().equals("jar")) { //$NON-NLS-1$
					finalpath = stylefullpath.getFile(); 
					finalpath = finalpath.substring(0,finalpath.length() - 2);
					if (finalpath.startsWith("file")) finalpath = finalpath.substring(5); //$NON-NLS-1$
				} else {
				//development case
				finalpath = stylefullpath.getPath() + "styles/style.css"; //$NON-NLS-1$
				}
				
				
			}
			if (finalpath.startsWith("/")) finalpath = finalpath.substring(1); //$NON-NLS-1$
			File testfile = new File(finalpath);
			
    		if (!testfile.exists()) finalpath = null;
    		}
    		
    		catch (Exception ex)
    		{
    			
    		}
    	}
    	
    	//stylesheet exists
    	if (finalpath != null) {
		
    		/*
    		 * use the HtmlDocumentation class generated by the JET template to generate the documentation 
    		 * and store it at the selected location 
    		 */
    		Object resource = getActiveResource().getContents().get(0);
		
	    	if (resource instanceof Model ) {
	    		model = (Model)resource;
	    		List firstlevel = model.getElements();
	    	
	    	
	    		//build sorted lists of all elements
	    		for (Iterator i = firstlevel.iterator(); i.hasNext();) {
	    			Object obj = i.next();
	    			if (obj instanceof Role) roles.add((Role)obj);
	    			else if (obj instanceof Application) applications.add((Application)obj);
	    			else if (obj instanceof Data) data.add((Data)obj);
	    			else if (obj instanceof Activity) processes.add((Activity)obj);
	    		}
	    		List firstpackages = model.getSubpackages();    	
	    		for (Iterator i = firstpackages.iterator(); i.hasNext();) {
	    			searchPackage((org.eclipse.jwt.meta.model.core.Package)i.next());
	    		}
	    	
	    		CaseInsensitiveComparator comparator = new CaseInsensitiveComparator();
	    		Collections.sort(roles, comparator);
	    		Collections.sort(applications, comparator);
	    		Collections.sort(data, comparator);
	    		Collections.sort(processes, comparator);

	    		
	    		// Generate the documentation
				HtmlDocumentation template = new HtmlDocumentation();
				result = template.generate(new Object[]
				{ resource, roles, applications, data, processes });

				File htmlfile = path.toFile();
				try
				{

					htmlfile.createNewFile();
					FileOutputStream ostream = new FileOutputStream(htmlfile);
					ostream.write(result.getBytes());
					ostream.close();
				}
				catch (Exception e)
				{

				}
				
	    	
	    	
				//Copy the stylesheet
				File oldstylefile = null;
				IPath newstylepath = new Path(filepath);
				newstylepath = newstylepath.removeLastSegments(1);
    			newstylepath = newstylepath.append("style.css"); //$NON-NLS-1$
    			File newstylefile = newstylepath.toFile();
    			
    			//development case
				if (stylefullpath.getProtocol().equals("file")) { //$NON-NLS-1$
					IPath originalstylepath = new Path(finalpath);
					oldstylefile = originalstylepath.toFile();
					try {	
		    			FileReader filereader = new FileReader(oldstylefile);
		    			FileWriter filewriter = new FileWriter(newstylefile);
		    			newstylefile.createNewFile();
		    			int c;
		    			while ((c = filereader.read()) != -1) {
		    				filewriter.write(c);
		    			}
		    			filereader.close();
		    			filewriter.close();
		    		} catch (Exception e){
		    			
		    		}
				} 
				
				//normal case		
				else if (stylefullpath.getProtocol().equals("jar")) { //$NON-NLS-1$
						try {
							JarFile jarfile = new JarFile(finalpath);
							JarEntry jarentry = jarfile.getJarEntry("styles/style.css"); //$NON-NLS-1$
							InputStream inputstream = jarfile.getInputStream(jarentry);
							InputStreamReader isreader = new InputStreamReader(inputstream);
							FileWriter filewriter = new FileWriter(newstylefile);
							newstylefile.createNewFile();
							int c;
							while ((c = isreader.read()) != -1) {
								filewriter.write(c);
							}
							inputstream.close();
							isreader.close();
							filewriter.close();
						}
						catch (Exception e) {
							
						}
					}
		
	    		
	    		//Create and copy images
	    		try {
	    		int counter = 0;
	    			
	    		WEEditor editor = GeneralHelper.getActiveInstance();

	    		IPath imagehelppath = new Path(filepath);
	    		imagehelppath = imagehelppath.removeLastSegments(1);
	    		imagehelppath = imagehelppath.append("process"); //$NON-NLS-1$
	    		String imagepath = imagehelppath.toPortableString();	    		
	    		
	    		
	    		int oldPageID = editor.getActivePage();
	    		for (Iterator i = processes.iterator(); i.hasNext();){
	    			counter++;
	    			Activity activity = (Activity)i.next();
	    			
	    			
	    			/**
	    			 * If an activity is already open in a tab, the snapshot has to be taken from
	    			 * exactly this tab or the graphical representation will be destroyed.
	    			 * Activities that aren't already opened, have to be openend in a new editorSheet.
	    			 * 
	    			 */
	    			if (editor.activateIfAlreadyOpen(activity)) {
	    				GraphicalViewer viewer = editor.getCurrentActivitySheet().getGraphicalViewer();
	    				SaveImage.save(viewer, imagepath + counter + ".jpg", SWT.IMAGE_JPEG); //$NON-NLS-1$
	    			}
	    			else {
	    				WEEditorSheet editorSheet = new WEEditorSheet(editor);
	    				int pageID = editor.addPage(editorSheet, editor.getEditorInput());
	    				GraphicalViewer viewer = editorSheet.getGraphicalViewer();
	    				editorSheet.loadActivityModel(activity);
	    				editor.activatePage(pageID);
	    				SaveImage.save(viewer, imagepath + counter + ".jpg", SWT.IMAGE_JPEG); //$NON-NLS-1$
	    				editor.removeActivityFromPage(activity);
	    			}
	    		}
	    		//go back to the tab that was open before the generation started
	    		editor.activatePage(oldPageID);
	    		
	    		} catch (Exception e) {
	    			
	    		}
	    		
	    		
	    	}
	    	
    	}	
    	
	    //no stylesheet found
	    else {
	    	MessageDialog dialog = new MessageDialog(Activator.getDefault().getWorkbench().getDisplay().getActiveShell(),
	    			Messages.doc_dialog_title, null ,
	    			Messages.doc_dialog_message, MessageDialog.INFORMATION,
	    			new String[] {"OK"}, 0); //$NON-NLS-1$
	    	dialog.create();
	    	dialog.open();
	    }
    	
		
		}
	}


	/**
	 * Searches all subpackages of a package for roles, applications, data and processes
	 * and adds them to the corresponding list
	 * 
	 * @param pack
	 *            The package to be searched
	 */
	private void searchPackage(org.eclipse.jwt.meta.model.core.Package pack)
	{
		List elements = pack.getElements();
		for (Iterator i = elements.iterator(); i.hasNext();)
		{
			Object obj = i.next();
			if (obj instanceof Role)
				roles.add((Role) obj);
			else if (obj instanceof Application)
				applications.add((Application) obj);
			else if (obj instanceof Data)
				data.add((Data) obj);
			else if (obj instanceof Activity)
				processes.add((Activity) obj);
		}
		List packages = pack.getSubpackages();
		for (Iterator i = packages.iterator(); i.hasNext();)
		{
			searchPackage((org.eclipse.jwt.meta.model.core.Package) i.next());
		}

	}

}


/**
 * 
 * Comparator which ignores casesensitivity and is specialized for the model elements
 * 
 * @author Christian Seitz (chr.seitz@gmx.de), Programming distributed Systems Lab,
 *         University of Augsburg, Germany, www.ds-lab.org.
 */
class CaseInsensitiveComparator
		implements java.util.Comparator
{

	public int compare(Object o1, Object o2)
	{
		String s1 = ""; //$NON-NLS-1$
		String s2 = ""; //$NON-NLS-1$
		if (o1 instanceof Role)
		{
			if (((Role) o1).getName()!=null)
				s1 = ((Role) o1).getName().toUpperCase();
			if (((Role) o2).getName()!=null)
				s2 = ((Role) o2).getName().toUpperCase();
		}
		else if (o1 instanceof Application)
		{
			if (((Application) o1).getName()!=null)
				s1 = ((Application) o1).getName().toUpperCase();
			if (((Application) o2).getName()!=null)
				s2 = ((Application) o2).getName().toUpperCase();
		}
		else if (o1 instanceof Data)
		{
			if (((Data) o1).getName()!=null)
				s1 = ((Data) o1).getName().toUpperCase();
			if (((Data) o2).getName()!=null)
				s2 = ((Data) o2).getName().toUpperCase();
		}
		else if (o1 instanceof Activity)
		{
			if (((Activity) o1).getName()!=null)
				s1 = ((Activity) o1).getName().toUpperCase();
			if (((Activity) o2).getName()!=null)
				s2 = ((Activity) o2).getName().toUpperCase();
		}
		else
		{
			s1 = o1.toString().toUpperCase();
			s2 = o2.toString().toUpperCase();
		}
		return s1.compareTo(s2);
	}
}
