/*******************************************************************************
 * Copyright (C) 2003-2004, 2013, Guillaume Brocker
 * 
 * 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:
 *     Guillaume Brocker - Initial API and implementation
 *
 ******************************************************************************/ 

package eclox.ui.editor.advanced;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.resource.FontRegistry;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.forms.IDetailsPage;
import org.eclipse.ui.forms.IFormPart;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.events.IHyperlinkListener;
import org.eclipse.ui.forms.widgets.FormText;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;

import eclox.core.doxyfiles.Doxyfile;
import eclox.core.doxyfiles.Setting;
import eclox.ui.Plugin;
import eclox.ui.editor.editors.SettingEditor;


/**
 * Implements the generic details node page.
 * 
 * @author gbrocker
 */
public class DetailsPage implements IDetailsPage {
    
	/** symbolic name for emphasis font */
	private static final String EMPHASIS = "em";
	
    /** the static setting editor class register */
    private static EditorClassRegister editorClassRegister = new EditorClassRegister();
    
    /** the setting editor instance */
    private SettingEditor editor;

    /** the section that contains all our controls */
    private Section section;
    
    /** he editor content container widget */
    private Composite editorContainer;
    
    /** the control containing all controls of the section */
    private Composite sectionContent;
    
    /** the managed form the page is attached to */
    protected IManagedForm managedForm;
    
    /** the current selection */
    private NavigableSelection selection = new NavigableSelection();
    
    /** the control displaying the setting's note text */
    private FormText noteLabel;
    
    /** the font registry used for note text formatting */
    private FontRegistry fontRegistry;
    
    /**
     * Defines the listeners that will managed activation of hyper-links in the
     * setting's note.
     */
    private class MyHyperlinkListener implements IHyperlinkListener {

    	private DetailsPage owner;
    	
    	/**
    	 * Constructor
    	 * 
    	 * @param owner	the owner of the instance
    	 */
    	MyHyperlinkListener( DetailsPage owner ) {
    		this.owner = owner;
    	}
    	
		/**
		 * @see org.eclipse.ui.forms.events.IHyperlinkListener#linkActivated(org.eclipse.ui.forms.events.HyperlinkEvent)
		 */
		public void linkActivated(HyperlinkEvent e) {
			// Pre-condition
			assert editor != null;
			
			Doxyfile	doxyfile	= editor.getInput().getOwner();
			Setting		setting		= doxyfile.getSetting( e.getHref().toString() ); 
			
			if( setting != null ) {
				managedForm.fireSelectionChanged( owner, selection.select(setting) );
			}
		}

		/**
		 * @see org.eclipse.ui.forms.events.IHyperlinkListener#linkEntered(org.eclipse.ui.forms.events.HyperlinkEvent)
		 */
		public void linkEntered(HyperlinkEvent e) {}

		/**
		 * @see org.eclipse.ui.forms.events.IHyperlinkListener#linkExited(org.eclipse.ui.forms.events.HyperlinkEvent)
		 */
		public void linkExited(HyperlinkEvent e) {}
    	
    }

    /**
     * @see org.eclipse.ui.forms.IDetailsPage#createContents(org.eclipse.swt.widgets.Composite)
     */
    public void createContents(Composite parent) {
        FormToolkit toolkit = this.managedForm.getToolkit();
        
        fontRegistry = new FontRegistry(parent.getDisplay());
        
        // Initializes the parent control.
        parent.setLayout(new FillLayout());
        
        // Creates the section
        this.section = toolkit.createSection(parent, Section.TITLE_BAR);
        this.section.marginHeight = 5;
        this.section.marginWidth = 10;
        
        // Createst the section content and its layout
        this.sectionContent = toolkit.createComposite(section);
        this.section.setClient(this.sectionContent);
        GridLayout layout = new GridLayout(1, true);
        layout.marginWidth = 0;
        layout.marginHeight = 0;
        this.sectionContent.setLayout(layout);
        
        // Creates the editor content.
        this.editorContainer = managedForm.getToolkit().createComposite(sectionContent);
        this.editorContainer.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
                
        // Creates controls displaying the setting note.
        this.noteLabel = this.managedForm.getToolkit().createFormText( sectionContent, false );
        this.noteLabel.setLayoutData( new GridData(GridData.FILL_HORIZONTAL) );
        this.noteLabel.setFont( EMPHASIS, fontRegistry.getItalic("") );
        this.noteLabel.addHyperlinkListener( new MyHyperlinkListener(this) );
        
    }
    
    /**
     * @see org.eclipse.ui.forms.IFormPart#commit(boolean)
     */
    public void commit(boolean onSave) {
	    if( editor != null ) {
	    	editor.commit();
	    }
    }
    
    /**
     * @see org.eclipse.ui.forms.IFormPart#dispose()
     */
    public void dispose() {}
    
    /**
     * @see org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm)
     */
    public void initialize(IManagedForm form) {
        this.managedForm = form;
    }
    
    /**
     * @see org.eclipse.ui.forms.IFormPart#isDirty()
     */
    public boolean isDirty() {
    	return editor != null ? editor.isDirty() : false;
    }
    
    /**
     * @see org.eclipse.ui.forms.IFormPart#isStale()
     */
    public boolean isStale() {
    	return editor != null ? editor.isStale() : false;
    }
    
    /**
     * @see org.eclipse.ui.forms.IFormPart#refresh()
     */
    public void refresh() {
    	if( editor != null ) {
    		editor.refresh();
    	}
    }
    
    /**
     * @see org.eclipse.ui.forms.IFormPart#setFocus()
     */
    public void setFocus() {
    	// Pre-condition
    	assert this.editor != null;
    		
        this.editor.setFocus();
	}
    
    /**
     * @see org.eclipse.ui.forms.IFormPart#setFormInput(java.lang.Object)
     */
    public boolean setFormInput(Object input) {
        return false;
    }
    
    /**
     * @see org.eclipse.ui.forms.IPartSelectionListener#selectionChanged(org.eclipse.ui.forms.IFormPart, org.eclipse.jface.viewers.ISelection)
     */
    public void selectionChanged(IFormPart part, ISelection newSelection) {
    	// Pre-condition
    	assert (newSelection instanceof NavigableSelection);
    	
		// Retreieves the node that is provided by the selection.
    	/*if( part != this )*/ {
    		selection = (NavigableSelection) newSelection;
	    	Object	object	= selection.getFirstElement();
		    Setting	setting	= (object instanceof Setting) ? (Setting) object : null;
		    String	text	= setting.getProperty(Setting.TEXT);
		    
		    // Checks that the setting has a text property.
		    if( text == null ) {
		    	Plugin.log(setting.getIdentifier() + ": missing TEXT property.");
		    	text = new String(setting.getIdentifier());
		    }
	        
	        // Updates the form controls.
	        this.selectNote(setting);
	        this.selectEditor(setting);
	        this.section.setText( text );
	        this.section.layout(true, true);
    	}
    }
    
    /**
     * Disposes the current editor.
     */
    private void disposeEditor() {
        if(editor != null) {
        	editor.dispose();
            editor = null;
        }
    }
        
    /**
     * Selects the editor for the specified setting.
     * 
     * @param	input	the setting that is the new input
     */
    private void selectEditor(Setting input) {
        try {
	        // Retrieves the editor class for the input.
	        Class editorClass = editorClassRegister.find(input);
	        
	        // Perhaps should we remove the current editor.
	        if(editor != null && editor.getClass() != editorClass) {
	            disposeEditor();
	        }
	        
	        // Perhaps, we should create a new editor instance.
	        if(editor == null) {
		        editor = (SettingEditor) editorClass.newInstance();
		        editor.createContent(editorContainer, managedForm.getToolkit());
		        editorContainer.setLayoutData( new GridData(editor.grabVerticalSpace() ? GridData.FILL_BOTH : GridData.FILL_HORIZONTAL) );
	        }
	        
	        // Assigns the input to the editor.
	        editor.setInput(input);
	        editor.refresh();
        }
        catch(Throwable throwable) {
        	MessageDialog.openError(this.managedForm.getForm().getShell(), "Unexpected Error", throwable.toString());
        }
    }    

    /**
     * Updates the UI controls for the specified node.
     * 
     * @param	setting	a setting instance to use to refresh the UI controls.
     */
    private void selectNote(Setting setting) {
    	// Retrieves the setting's note text.
        String text = setting.getProperty( Setting.NOTE );
        
        // If there is none, build a default one.
        if(text == null) {
        	text = "Not available.";
        }
        // Else do some parsing and replacements for layout and style. 
        else {
        	Doxyfile	doxyfile = setting.getOwner();
        	
        	text = text.startsWith("<p>") ? text : "<p>"+text+"</p>";
        	Matcher			matcher = Pattern.compile("([A-Z_]{2,}|Warning:|Note:|@[a-z]+)").matcher(text);
        	StringBuffer	buffer = new StringBuffer();
        	while( matcher.find() ) {
        		String	match = matcher.group(1);
        		
        		if( match.equals("YES") || match.equals("NO") ) {
        			matcher.appendReplacement( buffer, "<span font=\""+EMPHASIS+"\">"+match+"</span>");
        		}
        		else if( match.equals("Note:") || match.equals("Warning:") ) {
        			matcher.appendReplacement( buffer, "<b>"+match+"</b>");
        		}
        		else if( match.startsWith("@") ) {
        			matcher.appendReplacement( buffer, "<span font=\""+EMPHASIS+"\">"+match+"</span>");
        		}
        		else {
        			Setting	matchSetting	= doxyfile.getSetting(match);
        			
        			if( matchSetting != null ) {
	        			String	settingText		= matchSetting.getProperty(Setting.TEXT);
	        			
	        			if( matchSetting == setting ) {
	        				matcher.appendReplacement( buffer, "<span font=\""+EMPHASIS+"\">"+settingText+"</span>");
	        			}
	        			else {
	        				matcher.appendReplacement( buffer, "<a href=\""+matchSetting.getIdentifier()+"\">"+settingText+"</a>");
	        			}
        			}
        		}
        	}
        	matcher.appendTail( buffer );
        	text = buffer.toString();
        }
        
        // Finally, assignes the text to the user interface control.
        this.noteLabel.setText("<form>"+text+"</form>", true, false);
    }
}
