/*
 * Created on 2004-12-06
 *
 * Part of the Symbian development plug-in for Eclipse
 * Copyright (C) Mikołaj Zalewski, 2004
 */
package pl.zalewski.mikolaj.symbdev.ui.mmpedit;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.forms.widgets.FormText;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.TableWrapData;

import pl.zalewski.mikolaj.symbdev.SymbdevPlugin;
import pl.zalewski.mikolaj.symbdev.SymbianSDKEntry;
import pl.zalewski.mikolaj.symbdev.core.mmpmodel.ICompilerOptions;
import pl.zalewski.mikolaj.symbdev.core.mmpmodel.IMMPModel;
import pl.zalewski.mikolaj.symbdev.ui.SymbdevImages;
import pl.zalewski.mikolaj.symbdev.ui.internal.widgets.CompilerOptionsDialog;
import pl.zalewski.mikolaj.symbdev.ui.internal.widgets.DirectoryDialog;
import pl.zalewski.mikolaj.symbdev.utils.FileStringType;
import pl.zalewski.mikolaj.symbdev.utils.StringDialog;

/**
 * This is the Compiler section in the Options page.
 * 
 * @author mikolaj
 */
public class CompilerOptionsSection extends MMPSectionPart {

    final static String MACRO_HINT="<form><p>Here are the symbols that will be #defined during the compilation</p></form>";
    final static String OPTION_HINT="<form><p>Here are the options that will be appended to the compiler's command line</p></form>";
    final static String USER_INC_HINT="<form><p>Here are the directories with the header file that should be included with #include \"file\"</p></form>";
    final static String SYSTEM_INC_HINT="<form><p>Here are the directories with the header file that should be included with #include &lt;file&gt;</p></form>";
    final static String FORCE_THREE_LINES="<form><p>x<br/>x<br/>x<br/></p></form>";
    
    public CompilerOptionsSection(Composite parent, FormToolkit toolkit, IMMPModel model, SymbianSDKEntry sdk, IPath dirPath) {
        super(parent, toolkit, "Compiler", "Choose compilation options", 2);
        fModel=model;
        fSDK=sdk;
        fDirPath=dirPath;
        fItemsTree=new Tree(fContainer, SWT.NONE);
        toolkit.adapt(fItemsTree);
        TableWrapData td=new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.FILL_GRAB);
        td.heightHint=250;
        fItemsTree.setLayoutData(td);
        fMacroRoot=new TreeItem(fItemsTree, SWT.NONE);
        fMacroRoot.setText("Defined symbols");
        fMacroRoot.setImage(SymbdevImages.DESC_DEFINE.createImage());
        fOptionsRoot=new TreeItem(fItemsTree, SWT.NONE);
        fOptionsRoot.setText("Compiler command line options");
        fOptionsRoot.setImage(SymbdevImages.DESC_PROP_EDIT.createImage());
        fUserIncRoot=new TreeItem(fItemsTree, SWT.NONE);
        fUserIncRoot.setText("User include directories");
        fUserIncRoot.setImage(SymbdevImages.DESC_INCLUDES_CONTAINER.createImage());
        fSysIncRoot=new TreeItem(fItemsTree, SWT.NONE);
        fSysIncRoot.setText("System include directories");
        fSysIncRoot.setImage(SymbdevImages.DESC_INCLUDES_CONTAINER.createImage());
        
        Composite buttons=toolkit.createComposite(fContainer);
        td=new TableWrapData(TableWrapData.LEFT, TableWrapData.MIDDLE);
        buttons.setLayoutData(td);
        GridLayout gl=new GridLayout(1, true);
        buttons.setLayout(gl);
        fAdd=toolkit.createButton(buttons, "Add...", SWT.PUSH);
        fAdd.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        fRemove=toolkit.createButton(buttons, "Remove", SWT.PUSH);
        fRemove.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        fEdit=toolkit.createButton(buttons, "Edit...", SWT.PUSH);
        fEdit.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        
        fHintText=toolkit.createFormText(fContainer, true);
        td=new TableWrapData();
        td.colspan=2;
        td.align=TableWrapData.FILL;
        td.valign=TableWrapData.FILL_GRAB;
        fHintText.setLayoutData(td);
        fHintText.setText(FORCE_THREE_LINES, true, false);
        
        fAdd.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event e) {
                addPushed();
            }			
        });
        fRemove.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event e) {
                removePushed();
            }			
        });
        fEdit.addListener(SWT.Selection, new Listener() {
            public void handleEvent(org.eclipse.swt.widgets.Event e) {
                editPushed();
            }			
        });
        fItemsTree.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event e) {
                treeSelected();
            }			
        });
        
        fMacroListener=new Listener() {
            public void handleEvent(Event e) {
                macrosChanged();
            }			
        };
        fModel.addChangeListener(IMMPModel.CHANGE_MACRO, fMacroListener);
        fOptionsListener=new Listener() {
            public void handleEvent(Event e) {
                optionsChanged();
            }			
        };
        fModel.addChangeListener(IMMPModel.CHANGE_OPTION, fOptionsListener);
        fUserIncListener=new Listener() {
            public void handleEvent(org.eclipse.swt.widgets.Event e) {
                userIncludeDirsChanged();
            }
        };
        fModel.addChangeListener(IMMPModel.CHANGE_USER_INCLUDES, fUserIncListener);
        fSystemIncListener=new Listener() {
            public void handleEvent(org.eclipse.swt.widgets.Event e) {
                systemIncludeDirsChanged();
            }
        };
        fModel.addChangeListener(IMMPModel.CHANGE_SYSTEM_INCLUDES, fSystemIncListener);
        
        // load data
        macrosChanged();
        optionsChanged();
        userIncludeDirsChanged();
        systemIncludeDirsChanged();
        
        contentCreated();
    }
    
    protected void enableButtons()
    {
        TreeItem[] sel=fItemsTree.getSelection();
        if (sel.length==0)
        {
            fAdd.setEnabled(false);
            fRemove.setEnabled(false);
            fEdit.setEnabled(false);
            return;
        }
        fAdd.setEnabled(true);
        
        if (sel[0].getParentItem()==null)
        {
            fRemove.setEnabled(false);
            fEdit.setEnabled(false);
            
            if (sel[0]==fMacroRoot) {
                fHintText.setText(MACRO_HINT, true, false);
            } else if (sel[0]==fOptionsRoot) {
                fHintText.setText(OPTION_HINT, true, false);                         
            } else if (sel[0]==fUserIncRoot) {
                fHintText.setText(USER_INC_HINT, true, false);
            } else if (sel[0]==fSysIncRoot) {
                fHintText.setText(SYSTEM_INC_HINT, true, false);
            } else fHintText.setText("", false, false);
        } else
        {
            fRemove.setEnabled(true);
            fEdit.setEnabled(true);
            fHintText.setText("", false, false);
        }
        fHintText.redraw();
    }
    
    int getItemPos(TreeItem item)
    {
        TreeItem[] items=item.getParentItem().getItems();
        int pos;
        for (pos=0; pos<items.length; pos++)
            if (items[pos]==item)
                return pos;
        return -1;        
    }
    
    void addPushed()
    {
        TreeItem[] sel=fItemsTree.getSelection();        
        if (sel.length==0)
            return;
        TreeItem it=sel[0];
        if (it.getParentItem()!=null)
            it=it.getParentItem();
        if (it==fMacroRoot)
        {
            String define=StringDialog.queryString(getSection().getShell(),
                    SymbdevPlugin.MESSAGE_TITLE, "Enter symbol to define", null);
            if (define==null || define.equals(""))
                return;
            try {
                fModel.addMacro(define);
            } catch (CoreException e) {
                SymbdevPlugin.showError(e, "Couldn't store define", getSection().getShell());
            }
        } else if (it==fOptionsRoot)
        {
            CompilerOptionsDialog dlg=new CompilerOptionsDialog(getSection().getShell());
            if (!dlg.execute(null))
                return;
            try {
                fModel.addCompilerOptions(dlg.getId(), dlg.getOptions());
            } catch (CoreException e) {
                SymbdevPlugin.showError(e, null, getSection().getShell());
            }
        } else if (it==fUserIncRoot || it==fSysIncRoot)
        {
            DirectoryDialog dlg=new DirectoryDialog(getSection().getShell(), fSDK, fDirPath);
            dlg.execute();
            if (dlg.getResult()!=null)
                try {
	                if (it==fUserIncRoot) fModel.addUserInclude(dlg.getResult());
	                	else fModel.addSystemInclude(dlg.getResult());
                }
            	catch (CoreException e)
            	{
                    SymbdevPlugin.showError(e, null, getSection().getShell());            	    
            	}
        }
    }
    
    void removePushed()
    {
        TreeItem[] sel=fItemsTree.getSelection();        
        if (sel.length==0)
            return;
        for (int i=0; i<sel.length; i++)
        {
	        TreeItem item=sel[i];
            if (item.getParentItem()==null)
                continue;
	        int pos=getItemPos(item);
	        if (pos<0)
	            continue;
	        
	        try {
		        if (item.getParentItem()==fMacroRoot)
		        {
	                fModel.removeMacro(pos);
		        } else if (item.getParentItem()==fOptionsRoot)
		        {
		            fModel.removeCompilerOptions(pos);
		        } else if (item.getParentItem()==fSysIncRoot)
		        {
		            fModel.removeSystemInclude(pos);
		        } else if (item.getParentItem()==fUserIncRoot)
		        {
		            fModel.removeUserInclude(pos);
		        }
	        } catch (CoreException e) {
	            SymbdevPlugin.showError(e, null, getSection().getShell());
	        }
        }
    }
    
    void editPushed()
    {
        TreeItem[] sel=fItemsTree.getSelection();        
        if (sel.length==0 || sel[0].getParentItem()==null)
            return;
        TreeItem item=sel[0];
        int pos=getItemPos(item);
        if (pos<0)
            return;

        try {
	        if (item.getParentItem()==fMacroRoot)
	        {
	            String newText=StringDialog.queryString(getSection().getShell(),
	                    SymbdevPlugin.MESSAGE_TITLE, "Edit symbol to define",
	                    item.getText());
	            if (newText==null)
	                return;
	            if (newText.length()==0)
	            {
	                fModel.removeMacro(pos);
	                return;
	            }
	            fModel.setMacro(pos, newText);
	        } else if (item.getParentItem()==fOptionsRoot)
	        {
	            CompilerOptionsDialog dlg=new CompilerOptionsDialog(getSection().getShell());
	            if (!dlg.execute(fModel.getCompilerOptions()[pos]))
	                return;
	            String id=dlg.getId();
	            String opt=dlg.getOptions();
	            if (opt==null)
	                fModel.removeCompilerOptions(pos);
	                else fModel.setCompilerOptions(pos, id, opt);
	        } else if (item.getParentItem()==fUserIncRoot ||
	                   item.getParentItem()==fSysIncRoot)
	        {
	            DirectoryDialog dlg=new DirectoryDialog(getSection().getShell(), fSDK, fDirPath);
	            if (item.getParentItem()==fUserIncRoot)
	                dlg.setResult(fModel.getUserIncludes()[pos]);
	            	else dlg.setResult(fModel.getSystemIncludes()[pos]);
	            if (dlg.execute())
	            {
	                if (item.getParentItem()==fUserIncRoot)
	                    fModel.setUserInclude(pos, dlg.getResult());
	                	else fModel.setSystemInclude(pos, dlg.getResult());
	            }
	        }
        }
        catch (CoreException e)
        {
            SymbdevPlugin.showError(e, "Error storing new value",
                    getSection().getShell());
        }
    }
    
    void treeSelected()
    {
        enableButtons();
    }
    
    void macrosChanged()
    {
        if (fItemsTree.isDisposed())
            return;

        TreeItem[] tab=fMacroRoot.getItems();
        for (int i=0; i<tab.length; i++)
            tab[i].dispose();
        String[] macros=fModel.getMacros();
        for (int i=0; i<macros.length; i++)
        {
            TreeItem ti=new TreeItem(fMacroRoot, SWT.NONE);
            ti.setText(macros[i]);
            ti.setImage(SymbdevImages.DESC_DEFINE.createImage());
        }
        fMacroRoot.setExpanded(true);
    }
    
    void optionsChanged()
    {
        if (fItemsTree.isDisposed())
            return;
        
        TreeItem[] tab=fOptionsRoot.getItems();
        for (int i=0; i<tab.length; i++)
            tab[i].dispose();
        ICompilerOptions[] opts=fModel.getCompilerOptions();
        for (int i=0; i<opts.length; i++)
        {
            TreeItem ti=new TreeItem(fOptionsRoot, SWT.NONE);
            ti.setText(opts[i].getCompiler()+": '"+opts[i].getOptions()+"'");
            ti.setImage(SymbdevImages.DESC_PROP_EDIT.createImage());
        }
        fOptionsRoot.setExpanded(true);
    }
    
    void includeDirsChaned(String dirs[], TreeItem root)
    {
        if (fItemsTree.isDisposed())
            return;

        TreeItem[] tab=root.getItems();
        for (int i=0; i<tab.length; i++)
            tab[i].dispose();
        for (int i=0; i<dirs.length; i++)
        {
            TreeItem ti=new TreeItem(root, SWT.NONE);
            FileStringType str=new FileStringType(
                    dirs[i], fSDK.epocRoot, fDirPath.toString());
            ti.setText(str.getStringName("SDK root"));
            ti.setImage(SymbdevImages.DESC_HFOLDER_OBJ.createImage());
        }
        root.setExpanded(true);        
    }
    
    void userIncludeDirsChanged()
    {
        includeDirsChaned(fModel.getUserIncludes(), fUserIncRoot);
    }

    void systemIncludeDirsChanged()
    {
        includeDirsChaned(fModel.getSystemIncludes(), fSysIncRoot);
    }
    
    public void dispose() {
        fModel.removeChangeListener(IMMPModel.CHANGE_MACRO, fMacroListener);
        fModel.removeChangeListener(IMMPModel.CHANGE_OPTION, fOptionsListener);
        fModel.removeChangeListener(IMMPModel.CHANGE_USER_INCLUDES, fUserIncListener);
        fModel.removeChangeListener(IMMPModel.CHANGE_SYSTEM_INCLUDES, fSystemIncListener);        
        super.dispose();
    }
    
    Tree fItemsTree;
    IMMPModel fModel;
    Button fAdd, fRemove, fEdit;
    TreeItem fMacroRoot, fOptionsRoot, fUserIncRoot, fSysIncRoot;
    FormText fHintText;
    IPath fDirPath;
    
    SymbianSDKEntry fSDK;
    Listener fMacroListener;
    Listener fOptionsListener;
    Listener fUserIncListener;
    Listener fSystemIncListener;
}
