package org.eclipse.photran.internal.ui.old_editor;

import org.eclipse.cdt.core.model.IWorkingCopy;
import org.eclipse.cdt.internal.ui.editor.CContentOutlinePage;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.IWorkingCopyManager;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.DefaultLineTracker;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentPartitioner;
import org.eclipse.jface.text.ITextViewerExtension2;
import org.eclipse.jface.text.MarginPainter;
import org.eclipse.jface.text.rules.DefaultPartitioner;
import org.eclipse.jface.text.rules.RuleBasedPartitionScanner;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.photran.internal.ui.preferences.EditorPreferencePage;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.part.IShowInTargetList;
import org.eclipse.ui.texteditor.DefaultRangeIndicator;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;

/**
 * The Fortran text editor, including an inner class containing a coloring
 * scheme.
 * 
 * @author joverbey
 * @author cheahcf
 * @author nchen
 * @author crasmussen
 */
public class FortranFreeFormEditor extends AbstractFortranEditor {
	public static final String EDITOR_ID = "org.eclipse.photran.internal.ui.Editor.FortranEditor";

	private TabConverter tabConverter;

	private Composite fMainComposite;

	static final int MAX_LINES_FOR_LEXER_BASED_SCANNER = -1;
	
	private static final int COLUMN_132_WIDTH = 132;
	
	private static final RGB light_gray = new RGB(176, 180, 185);


	public FortranFreeFormEditor() {
		super();
		setSourceViewerConfiguration(new FortranSourceViewerConfiguration(this));
		setRangeIndicator(new DefaultRangeIndicator());
		// We must use the CUIPlugin's document provider in order for the
		// working copy manager in setOutlinePageInput (below) to
		// function correctly.
		// setDocumentProvider(new EbnfDocumentProvider());
		setDocumentProvider(CUIPlugin.getDefault().getDocumentProvider());
        
        // So our Refactoring menu will appear
        setEditorContextMenuId("#FortranEditorContext");
	}

	// Since we don't have our "own" document provider, we can't set up
	// partitioning there... so we do it here instead
	// TODO: dynamic change to
	protected void doSetInput(IEditorInput input) throws CoreException {
		super.doSetInput(input);
		IDocument document = this.getDocumentProvider().getDocument(input);
	
		if (document != null) {
			linesOfCode = document.getNumberOfLines();
			if (document.getNumberOfLines() > MAX_LINES_FOR_LEXER_BASED_SCANNER) {
				RuleBasedPartitionScanner sScanner = new RuleBasedPartitionScanner();
				IDocumentPartitioner partitioner = new DefaultPartitioner(
						sScanner,
						new String[] { FortranPartitionScanner.F90_STRING_CONSTANTS_PARTITION });
				partitioner.connect(document);
				document.setDocumentPartitioner(partitioner);
			} else {
				IDocumentPartitioner partitioner = new FortranPartitionScanner(
						input.getName(), false);
				partitioner.connect(document);
				document.setDocumentPartitioner(partitioner);
			}
			configureTabConverter(); // prepare a new TabConverter for the
			// document
		}
	}

	// --- RESPOND TO PREFERENCE CHANGES ---//

	/**
	 * Handles preference changes. Usually it is sufficient to use the
	 * superclass implementation. This time, we need to perform conversion and
	 * also change the tab width.
	 */
	protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
		try {
			if (getSourceViewer() == null
					|| getSourceViewer().getTextWidget() == null)
				return;
			AdaptedSourceViewer asv = (AdaptedSourceViewer) getSourceViewer();
			if (asv != null) {
				String property = event.getProperty();

				if (FortranEditorPreferencePage.SPACES_FOR_TABS_PREF.equals(property)) {
					asv.updateIndentationPrefixes();
					if (FortranEditorPreferencePage.isTabConversionEnabled())
						doTabConversion();
					else
						endTabConversion();
					return;
				}

				if (FortranEditorPreferencePage.TAB_WIDTH_PREF.equals(property)) {
					asv.updateIndentationPrefixes();
					if (tabConverter != null)
						tabConverter
								.setNumberOfSpacesPerTab(FortranEditorPreferencePage
										.getTabSize());
					Object value = event.getNewValue();
					if (value instanceof Integer) {
						asv.getTextWidget().setTabs(
								((Integer) value).intValue());
					} else if (value instanceof String) {
						asv.getTextWidget().setTabs(
								Integer.parseInt((String) value));
					}
					return;
				}
			}
		} finally {
			super.handlePreferenceStoreChanged(event);
		}
	}

	/**
	 * Creates a new tabCoverter for this editor that is currently opened in the
	 * workspace
	 * 
	 */
	private void configureTabConverter() {
		if (tabConverter != null) {
			tabConverter.setLineTracker(new DefaultLineTracker());
		}
	}

	/**
	 * Converts any new tabs that are entered into spaces
	 * 
	 */
	private void doTabConversion() {
		if (tabConverter == null) {
			tabConverter = new TabConverter();
			configureTabConverter();
			tabConverter.setNumberOfSpacesPerTab(FortranEditorPreferencePage
					.getTabSize());
			AdaptedSourceViewer asv = (AdaptedSourceViewer) getSourceViewer();
			asv.addTextConverter(tabConverter);
			asv.updateIndentationPrefixes();
		}
	}

	/**
	 * Stops converting tabs that the user enters. Any previous tabs that were
	 * converted to spaces are left as they are.
	 */
	private void endTabConversion() {
		if (tabConverter != null) {
			AdaptedSourceViewer asv = (AdaptedSourceViewer) getSourceViewer();
			asv.removeTextConverter(tabConverter);
			asv.updateIndentationPrefixes();
			tabConverter = null;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
	 */
	public void createPartControl(Composite parent) {
		super.createPartControl(parent);
		if (FortranEditorPreferencePage.isTabConversionEnabled())
			doTabConversion();

		Composite childComp = (Composite) ((Composite) parent.getChildren()[0])
				.getChildren()[0];
		GridLayout layout = new GridLayout();
		layout.marginHeight = 0;
		layout.marginWidth = 0;
		layout.verticalSpacing = 2;
		childComp.setLayout(layout);

		GridData data = new GridData(GridData.FILL_BOTH);
		childComp.getChildren()[0].setLayoutData(data);

		fMainComposite = childComp;

		createHorizontalRuler(fMainComposite);
		createLightGrayLines();
	}

	// --- EVERYTHING BELOW IS FOR OUTLINING ---//

	public Object getAdapter(Class required) {
		if (IContentOutlinePage.class.equals(required)) {
			return getOutlinePage();
		}
		if (required == IShowInTargetList.class) {
			return new IShowInTargetList() {
				public String[] getShowInTargetIds() {
					return new String[] { CUIPlugin.CVIEW_ID,
							IPageLayout.ID_OUTLINE, IPageLayout.ID_RES_NAV };
				}

			};
		}
		return super.getAdapter(required);
	}

	protected CContentOutlinePage fOutlinePage;

	private AbstractHorizontalRuler fHRuler;
    
	/**
	 * Gets the outline page of the c-editor.
	 * 
	 * @return Outline page.
	 */
	public CContentOutlinePage getOutlinePage() {
		if (fOutlinePage == null) {
			// CContentOutlinePage currently does nothing with its editor
			// parameter,
			// so we can pass in null rather than trying to convince it to use
			// our
			// editor (e.g., by subclassing CEditor).
			fOutlinePage = new CContentOutlinePage(null);
			fOutlinePage.addSelectionChangedListener(this);
		}
		setOutlinePageInput(fOutlinePage, getEditorInput());
		return fOutlinePage;
	}

	/**
	 * Sets an input for the outline page.
	 * 
	 * @param page
	 *            Page to set the input.
	 * @param input
	 *            Input to set.
	 */
	public static void setOutlinePageInput(CContentOutlinePage page,
			IEditorInput input) {
		if (page != null) {
			IWorkingCopyManager manager = CUIPlugin.getDefault().getWorkingCopyManager();
			IWorkingCopy workingCopy = manager.getWorkingCopy(input);
			page.setInput(workingCopy);
		}
	}

	// --- TOGGLE COMMENTING SUPPORT
	// ---------------------------------------------- //
	/**
	 * cheahcf Required to intialize key bindings specified in plugin.xml
	 */
	protected void initializeKeyBindingScopes() {
		setKeyBindingScopes(new String[] { "org.eclipse.photran.ui.FEditorScope" }); //$NON-NLS-1$
	}

	/**
	 * Create actions that will be registered with the editor.
	 */
	protected void createActions() {
		super.createActions();
//		Action action = new FortranBlockCommentAction(CEditorMessages
//				.getResourceBundle(), "AddBlockComment.", this); //$NON-NLS-1$
//		action
//				.setActionDefinitionId(ICEditorActionDefinitionIds.ADD_BLOCK_COMMENT);
//		setAction("AddBlockComment", action); //$NON-NLS-1$
//		markAsStateDependentAction("AddBlockComment", true); //$NON-NLS-1$
//		markAsSelectionDependentAction("AddBlockComment", true); //$NON-NLS-1$      
	}

	// --FOLDING
	// SUPPORT----------------------------------------------------------------------------

	// protected ProjectionSupport projectionSupport;
	//
	// protected ProjectionAnnotationModel annotationModel;
	//
	// public ProjectionAnnotationModel getAnnotationModel() {
	// return annotationModel;
	// }
	//
	// public void createPartControl(Composite parent) {
	// super.createPartControl(parent);
	//
	// ProjectionViewer viewer = (ProjectionViewer) getSourceViewer();
	//
	// projectionSupport = new ProjectionSupport(viewer,
	// getAnnotationAccess(), getSharedColors());
	// projectionSupport.install();
	//
	// // turn projection mode on
	// viewer.doOperation(ProjectionViewer.TOGGLE);
	//
	// annotationModel = viewer.getProjectionAnnotationModel();
	// }
	//
	// protected ISourceViewer createSourceViewer(Composite parent,
	// IVerticalRuler ruler, int styles) {
	// ISourceViewer viewer = new ProjectionViewer(parent, ruler,
	// getOverviewRuler(), isOverviewRulerVisible(), styles);
	//
	// // ensure decoration support has been created and configured.
	// getSourceViewerDecorationSupport(viewer);
	//
	// return viewer;
	// }

	// -- ISelectionChangedListener implementation -----------------------------

	/**
	 * @param mainComposite
	 * This creates the horizontal ruler and adds it to the top of the editor
	 */
	private void createHorizontalRuler(Composite mainComposite) {

		GC gc = new GC(getSourceViewer().getTextWidget());
		GridData data = new GridData(GridData.FILL_HORIZONTAL);
		data.heightHint = gc.getFontMetrics().getHeight();
		gc.dispose();

		fHRuler = new FortranFreeFormHorizontalRuler(getVerticalRuler(), mainComposite);
		fHRuler.setFont(getSourceViewer().getTextWidget().getFont());
		fHRuler.setSourceViewer(getSourceViewer());
		fHRuler.setLayoutData(data);
		fHRuler.moveAbove(null);

	}

	/**
	 *  Display a light gray line between columns 132/133
	 */
	private void createLightGrayLines() {
		ISourceViewer mySourceViewer = getSourceViewer();
		if(mySourceViewer instanceof ITextViewerExtension2) {
			ITextViewerExtension2 painter = (ITextViewerExtension2) mySourceViewer;
			MarginPainter column132_133margin = new MarginPainter(getSourceViewer());
			column132_133margin.setMarginRulerColumn(COLUMN_132_WIDTH);
			column132_133margin.setMarginRulerColor(new Color(null, light_gray));
			painter.addPainter(column132_133margin);
		}
	}

}
