package org.ntlab.traceanalyzer;

import java.io.File;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
import org.ntlab.trace.ClassInfo;
import org.ntlab.trace.Trace;

public class CallTreeView extends ViewPart {
	private TreeViewer viewer;
	private IAction openAction;
	private IAction markAction;
	private IAction highlightAction;
	private MarkedTrace markedTrace;
	private Shell shell;

	@Override
	public void createPartControl(Composite parent) {
		shell = parent.getShell();
		viewer = new TreeViewer(parent);
		viewer.setContentProvider(new CallTreeContentProvider());
		viewer.setLabelProvider(new CallTreeLabelProvider());
		viewer.setInput(null);
		viewer.addSelectionChangedListener(new ISelectionChangedListener() {
			@Override
			public void selectionChanged(SelectionChangedEvent event) {
				IStructuredSelection sel = (IStructuredSelection)event.getSelection();
				Object element = sel.getFirstElement();
				if (element instanceof MarkedMethodExecution) {
					String className = ((MarkedMethodExecution)element).getDeclaringClassName();
					String methodSignature = ((MarkedMethodExecution)element).getSignature();
					ClassInfo classInfo = markedTrace.getClassInfo(className);
					if (classInfo == null) {
						className = ((MarkedMethodExecution)element).getReceiverClassName();
						classInfo = markedTrace.getClassInfo(className);
					}
					if (classInfo != null) {
						String projectPath = classInfo.getLoaderPath();
						IJavaProject javaProject = findJavaProject(projectPath);
						if (javaProject != null) {
							openInJavaEditor(javaProject, className, methodSignature);					
						}
					}
				}
			}
		});
		getSite().setSelectionProvider(viewer);
		createActions();
		createToolBar();
		createMenuBar();
	}
	
	/** アクションを作成 */
	private void createActions(){
		// トレースファイルを開くアクションを作成
		ImageDescriptor openIcon = PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_OBJ_FOLDER);        	
		openAction = new Action("Open Trace File...", openIcon){
			public void run(){
				// トレース出力先参照ウィザード
				FileDialog dialog = new FileDialog(shell, SWT.OPEN);
				dialog.setText("Open Trace File");
				dialog.setFilterExtensions(new String[]{"*.*"});
				String path = dialog.open();
				if (path == null) {
					return;
				}
				markedTrace = new MarkedTrace(path);
				viewer.setInput(markedTrace);
				viewer.refresh();
				highlightAction.setChecked(false);
			}
		};
		
		// トレースにマークするアクションを作成
		ImageDescriptor markIcon = PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_OPEN_MARKER);        	
		markAction = new Action("Mark Trace...", markIcon){
			public void run(){
				final Shell traceMarkShell = new Shell(SWT.TITLE | SWT.ON_TOP | SWT.CLOSE);  // マーク指定画面
				traceMarkShell.setText("Mark Trace");
				traceMarkShell.setSize(250, 150);
				traceMarkShell.setLayout(new FormLayout());
				
			    // 開始時間(ラベル)
			    Label startTimeLabel = new Label(traceMarkShell, SWT.NULL);
			    startTimeLabel.setText("Start Time:");
				FormData startTimeLabelForm = new FormData();
				startTimeLabelForm.top = new FormAttachment(10, 5);
				startTimeLabelForm.left = new FormAttachment(5, 5);
				startTimeLabel.setLayoutData(startTimeLabelForm);				
			    
			    // 開始時間(テキストボックス)
				final Text startTimeText = new Text(traceMarkShell, SWT.BORDER | SWT.SINGLE | SWT.WRAP);
				startTimeText.setText(Long.toString(markedTrace.getMarkStart()));
				FormData startTimeTextForm = new FormData();
				startTimeTextForm.top = new FormAttachment(10, 5);
				startTimeTextForm.left = new FormAttachment(35, 10);
				startTimeTextForm.right = new FormAttachment(95, -5);
				startTimeText.setLayoutData(startTimeTextForm);				
			    
			    // 終了時間(ラベル)
			    Label endTimeLabel = new Label(traceMarkShell, SWT.NULL);
			    endTimeLabel.setText("End Time:");
				FormData endTimeLabelForm = new FormData();
				endTimeLabelForm.top = new FormAttachment(30, 5);
				endTimeLabelForm.left = new FormAttachment(5, 5);
				endTimeLabel.setLayoutData(endTimeLabelForm);		
				
			    // 終了時間入力(テキストボックス)
			    final Text endTimeText = new Text(traceMarkShell, SWT.BORDER | SWT.SINGLE | SWT.WRAP);
			    endTimeText.setText(Long.toString(markedTrace.getMarkEnd()));
				FormData endTimeTextForm = new FormData();
				endTimeTextForm.top = new FormAttachment(30, 5);
				endTimeTextForm.left = new FormAttachment(35, 10);
				endTimeTextForm.right = new FormAttachment(95, -5);
				endTimeText.setLayoutData(endTimeTextForm);				
				
				//OKボタン
				Button okButton = new Button(traceMarkShell, SWT.PUSH);
				okButton.setText("OK");
				FormData okButtonForm = new FormData();
				okButtonForm.bottom = new FormAttachment(100, -5);
				okButtonForm.right = new FormAttachment(95, -5);
				okButton.setLayoutData(okButtonForm);				
				okButton.addSelectionListener(new SelectionAdapter() {
					public void widgetSelected(SelectionEvent e){
						if (markedTrace != null) {
							String startText = startTimeText.getText();
							String endText = endTimeText.getText();
							try {
								long start = Long.parseLong(startText);
								long end = Long.parseLong(endText);
								traceMarkShell.close();							
								markedTrace.mark(start, end);
								viewer.setInput(markedTrace);
								viewer.refresh();
								highlightAction.setChecked(false);
							} catch (NumberFormatException ex) {
							}
						} else {
							traceMarkShell.close();							
						}
					}
				});	
								
				traceMarkShell.open();
			}
		};
		
		// マーク固有のメソッドを強調するアクションを作成
		ImageRegistry registry = Activator.getDefault().getImageRegistry();
		ImageDescriptor highlightIcon = registry.getDescriptor("TraceAnalyzer.HIGHLIGHT_UNIQUE_IMAGE");
		highlightAction = new Action("Highlight Unique Methods", Action.AS_CHECK_BOX){
			public void run(){
				if (markedTrace != null) {
					if (isChecked()) {
						markedTrace.setHighlightUniqueMethods(true);
						viewer.setInput(markedTrace);
						viewer.refresh();
					} else {
						markedTrace.setHighlightUniqueMethods(false);					
						viewer.setInput(markedTrace);
						viewer.refresh();
					}
				}
			}
		};
		highlightAction.setImageDescriptor(highlightIcon);
	}
	
	/** ツールバーにアクションを追加 */
	private void createToolBar(){
		IToolBarManager mgr = getViewSite().getActionBars().getToolBarManager();
		mgr.add(openAction);
		mgr.add(markAction);
		mgr.add(highlightAction);
	}

	/** メニューバーにアクションを追加 */
	private void createMenuBar(){
		IMenuManager mgr = getViewSite().getActionBars().getMenuManager();
		mgr.add(openAction);
		mgr.add(markAction);
	}
	
	public void setFocus() {
		  viewer.getTree().setFocus();
	}

	private IJavaProject findJavaProject(String projectPath) {
		IJavaProject javaProject = null;
		IWorkspaceRoot root =  ResourcesPlugin.getWorkspace().getRoot();
		IProject[] projects = root.getProjects();
		for (IProject project: projects) {
			if (projectPath.startsWith(project.getLocation().toString())) {
				javaProject = JavaCore.create(project);
				break;
			}
		}
		return javaProject;
	}

	private void openInJavaEditor(IJavaProject javaProject, String className, String methodSignature) {
		try {
			IType type = javaProject.findType(className);
			if (type != null) {
				IEditorPart editor = JavaUI.openInEditor(type);
				if (!type.isLocal() && !type.isMember()) {
					for (IMethod method: type.getMethods()) {
						if (methodSignature.contains(method.getElementName())) {
							JavaUI.revealInEditor(editor, (IJavaElement)method);										
						}
					}
				}
			}
		} catch (PartInitException | JavaModelException e) {
			e.printStackTrace();
		}
	}
}
