/*******************************************************************************
 * Copyright (c) 2017 the TeXlipse team and others.
 * 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:
 *     The TeXlipse team - initial API and implementation
 *******************************************************************************/
package org.eclipse.texlipse.properties;

import java.io.File;
import java.util.Locale;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.fieldassist.AutoCompleteField;
import org.eclipse.jface.fieldassist.TextContentAdapter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.texlipse.TexlipsePlugin;
import org.eclipse.texlipse.builder.BuilderChooser;
import org.eclipse.ui.dialogs.PropertyPage;


/**
 * Project's properties page.
 * 
 * @author Kimmo Karlsson
 */
public class TexlipseProjectPropertyPage extends PropertyPage {

    // text field for project source file name
    private Text sourceFileField;
    
    // text field for output file name
    private Text outFileField;
    
    // text field for bib file
    private Text tempDirField;
    
    // text field for bib references file
    private Text bibRefDirField;

    // checkbox for marking derived temp files
    private Button derivedTempCheckbox;

    // checkbox for marking derived output files
    private Button derivedOutputCheckbox;
    
    // builder choosing component
    private BuilderChooser builderChooser;
    
    // makeindex style file
    private Text indexStyleField;
    
    // language code
    private Text languageField;
    
    /**
     * Constructor for the property page.
     */
    public TexlipseProjectPropertyPage() {
        super();
    }

    /**
     * Creates the layout of property page.
     * 
     * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite)
     */
    protected Control createContents(Composite parent) {

        Composite composite = new Composite(parent, SWT.NONE);
        composite.setLayout(new GridLayout());
        composite.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));

        TexlipsePreferencePage.addSeparator(1, composite);
        addMainSection(composite);
        TexlipsePreferencePage.addSeparator(1, composite);
        addOutSection(composite);
        TexlipsePreferencePage.addSeparator(1, composite);
        addTempDirSection(composite);
        TexlipsePreferencePage.addSeparator(1, composite);
        // TODO after 1.1.0 evaluate whether this is needed
        //addBibRefDirSection(composite);
        //TexlipsePreferencePage.addSeparator(1, composite);
        addDerivedSection(composite);
        TexlipsePreferencePage.addSeparator(1, composite);
        addFormatSection(composite);
        TexlipsePreferencePage.addSeparator(1, composite);
        addIndexStyleSection(composite);
        TexlipsePreferencePage.addSeparator(1, composite);
        addLangSection(composite);

        performDefaults();

        return composite;
    }

    /**
     * Create project main file section of the page.
     * @param parent parent component
     */
    private void addMainSection(Composite parent) {
        Composite composite = createDefaultComposite(parent, 2);

        //Label for path field
        Label pathLabel = new Label(composite, SWT.NONE);
        pathLabel.setText(TexlipsePlugin.getResourceString("propertiesMainFileLabel"));
        pathLabel.setLayoutData(new GridData());
        pathLabel.setToolTipText(TexlipsePlugin.getResourceString("propertiesMainFileTooltip"));
        
        // Path text field
        sourceFileField = new Text(composite, SWT.SINGLE | SWT.WRAP | SWT.BORDER);
        sourceFileField.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
        sourceFileField.setToolTipText(TexlipsePlugin.getResourceString("propertiesMainFileTooltip"));
        sourceFileField.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                validateSourceFileField();
            }});
    }

    /**
     * Check that:
     * - subdir exists
     * - subdir is direct subdir of project dir
     * - file exists
     * - file extension is tex or ltx
     */
    private void validateSourceFileField() {
        
        String text = sourceFileField.getText();
        if (text == null) {
            setValid(true);
            return;
        }
        
        text = text.trim();
        if (text.length() == 0) {
            setValid(true);
            return;
        }
        
        // if there is invalid characters
        if (text.indexOf(':') >= 0 || text.indexOf(';') >= 0) {
            setValid(false);
            return;
        }
        
        String dir = null;
        String file = "";
        
        int index = text.lastIndexOf('/');
        if (index < 0) {
            file = text;
        } else {
            dir = text.substring(0, index);
            file = text.substring(index+1);
        }
        
        if (dir != null && !projectFileExists(dir)) {
            setValid(false);
            return;
        }

        setValid(projectFileExists(text) && (file.endsWith(".tex") || file.endsWith(".ltx")));
    }

    /**
     * Create project main file section of the page.
     * @param parent parent component
     */
    private void addOutSection(Composite parent) {
        Composite composite = createDefaultComposite(parent, 2);

        //Label for path field
        Label pathLabel = new Label(composite, SWT.NONE);
        pathLabel.setText(TexlipsePlugin.getResourceString("propertiesOutFileLabel"));
        pathLabel.setLayoutData(new GridData());
        pathLabel.setToolTipText(TexlipsePlugin.getResourceString("propertiesOutFileTooltip"));
        
        // Path text field
        outFileField = new Text(composite, SWT.SINGLE | SWT.WRAP | SWT.BORDER);
        outFileField.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
        outFileField.setToolTipText(TexlipsePlugin.getResourceString("propertiesOutFileTooltip"));
        outFileField.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                validateOutputFileField();
            }});
    }

    /**
     * Check that:
     * - subdir exists
     * - subdir is direct subdir of project dir
     * - file has the extension of the output format
     */
    private void validateOutputFileField() {
        
        String text = outFileField.getText();
        if (text == null) {
            setValid(true);
            return;
        }
        
        text = text.trim();
        if (text.length() == 0) {
            setValid(true);
            return;
        }
        
        // if there is invalid characters
        if (text.indexOf(':') >= 0 || text.indexOf(';') >= 0) {
            setValid(false);
            return;
        }
        
        String dir = null;
        String file = "";
        
        int index = text.lastIndexOf('/');
        if (index < 0) {
            file = text;
        } else {
            dir = text.substring(0, index);
            file = text.substring(index+1);
        }
        
        if (dir != null && !projectFileExists(dir)) {
            setValid(false);
            return;
        }
        
        String format = builderChooser.getSelectedFormat();
        if (format != null) {
            setValid(file.endsWith(format));
        }
    }

    /**
     * Create the temp dir section of the page.
     * @param parent parent component
     */
    private void addTempDirSection(Composite parent) {
        Composite composite = createDefaultComposite(parent, 3);

        //Label for path field
        Label label = new Label(composite, SWT.NONE);
        label.setText(TexlipsePlugin.getResourceString("propertiesTempDirLabel"));
        label.setLayoutData(new GridData());
        label.setToolTipText(TexlipsePlugin.getResourceString("propertiesTempDirTooltip"));

        // Path text field
        tempDirField = new Text(composite, SWT.SINGLE | SWT.WRAP | SWT.BORDER);
        tempDirField.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
        tempDirField.setToolTipText(TexlipsePlugin.getResourceString("propertiesTempDirTooltip"));
        tempDirField.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                validateTempFileField();}});
    }

    /**
     * Check that the directory name in the temp dir text field is a valid
     * directory name.
     */
    private void validateTempFileField() {
        String text = tempDirField.getText();
        if (text != null && text.length() > 0) {
            if (text.indexOf(':') >= 0 || text.indexOf(';') >= 0) {
                setValid(false);
                //setMessage(TexlipsePlugin.getResourceString("propertiesInvalidDirChars"));
            } else {
                boolean exists = projectFileExists(text);
                setValid(exists);
                if (!exists) {
                    //setMessage(TexlipsePlugin.getResourceString("propertiesInvalidDirChars"));
                }
            }
        } else {
            setValid(true);
        }
    }
    
    /**
     * Create the bibRef dir section of the page.
     * @param parent parent component
     */
    private void addBibRefDirSection(Composite parent) {
        Composite composite = createDefaultComposite(parent, 3);

        //Label for path field
        Label label = new Label(composite, SWT.NONE);
        label.setText(TexlipsePlugin.getResourceString("propertiesBibRefDirLabel"));
        label.setLayoutData(new GridData());
        label.setToolTipText(TexlipsePlugin.getResourceString("propertiesBibRefDirTooltip"));

        // Path text field
        bibRefDirField = new Text(composite, SWT.SINGLE | SWT.WRAP | SWT.BORDER);
        bibRefDirField.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
        bibRefDirField.setToolTipText(TexlipsePlugin.getResourceString("propertiesBibRefDirTooltip"));
        bibRefDirField.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                validateBibRefFileField();}});
    }
    
    /**
     * Check that the directory name in the bibRef dir text field is a valid
     * directory name.
     */
    private void validateBibRefFileField() {
    	String text = bibRefDirField.getText();
    	if (text != null && text.length() > 0) {            
    		if (text.indexOf(';') >= 0) {
    			setValid(false);
    		} else {
    			boolean exists = projectFileExists(text);
    			if (!exists) { //also allow external paths
    				File f = new File(text);
    				setValid(f.exists());
    			} else {
    				setValid(exists);
    			}
    		}
    	} else {
    		setValid(true);
    	}
    }
    
    /**
     * Add the checkbox for controlling "derived"-flag.
     * @param parent parent component
     */
    private void addDerivedSection(Composite parent) {
        derivedTempCheckbox = new Button(parent, SWT.CHECK | SWT.LEFT);
        derivedTempCheckbox.setLayoutData(new GridData());
        derivedTempCheckbox.setText(TexlipsePlugin.getResourceString("propertiesDerivedTempFiles"));
        derivedTempCheckbox.setToolTipText(TexlipsePlugin.getResourceString("propertiesDerivedFilesTooltip"));
        derivedOutputCheckbox = new Button(parent, SWT.CHECK | SWT.LEFT);
        derivedOutputCheckbox.setLayoutData(new GridData());
        derivedOutputCheckbox.setText(TexlipsePlugin.getResourceString("propertiesDerivedOutputFiles"));
        derivedOutputCheckbox.setToolTipText(TexlipsePlugin.getResourceString("propertiesDerivedFilesTooltip"));
    }
    
    /**
     * Create output file format section of the page.
     * @param parent parent component
     */
    private void addFormatSection(Composite parent) {

        builderChooser = new BuilderChooser(parent);
        builderChooser.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent event) {
                String fmt = builderChooser.getSelectedFormat();
                if (fmt != null) {
                    setOutputExtension(fmt);
                }
            }});
    }

    /**
     * Create text field for makeindex style file parameter.
     * @param parent parent component
     */
    private void addIndexStyleSection(Composite parent) {
        Composite composite = createDefaultComposite(parent, 2);
        
        Label label = new Label(composite, SWT.LEFT | SWT.WRAP);
        label.setLayoutData(new GridData());
        label.setText(TexlipsePlugin.getResourceString("propertiesMakeindexStyle"));
        
        indexStyleField = new Text(composite, SWT.SINGLE | SWT.BORDER);
        indexStyleField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    }
    
    /**
     * Create a text field for language setting.
     * @param parent parent component
     */
    private void addLangSection(Composite parent) {
        
        Label descr = new Label(parent, SWT.LEFT | SWT.WRAP);
        descr.setLayoutData(new GridData());
        descr.setText(TexlipsePlugin.getResourceString("propertiesLanguageDescription"));
        
        Composite composite = createDefaultComposite(parent, 2);
        
        Label label = new Label(composite, SWT.LEFT);
        label.setLayoutData(new GridData());
        label.setText(TexlipsePlugin.getResourceString("propertiesLanguage"));
        
        languageField = new Text(composite, SWT.SINGLE | SWT.BORDER);
        languageField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        languageField.setTextLimit(2);
        new AutoCompleteField(languageField, new TextContentAdapter(), Locale.getISOLanguages());
    }
    
    /**
     * Change the output file's extension according to the parameter.
     * This method only changes the value in the textfield, 
     * not in the properties.
     * 
     * @param ext new file extension
     */
    private void setOutputExtension(String ext) {
        
        String text = outFileField.getText();
        if (text == null) {
            return;
        }
        
        text = text.trim();
        if (text.length() == 0) {
            text = sourceFileField.getText();
            if (text == null) {
                return;
            }
            text = text.trim();
            if (text.length() == 0) {
                return;
            }
        }

        int index = text.lastIndexOf('.');
        if (index < 0) {
            outFileField.setText(text + "." + ext);
        } else {
            String base = text.substring(0, index+1);
            outFileField.setText(base + ext);
        }
    }
    
    /**
     * Create a standard container for the text field sections of the page.
     * @param parent parent component
     * @param columns number of columns in the grid layout
     * @return the container
     */
    private Composite createDefaultComposite(Composite parent, int columns) {
        
        Composite composite = new Composite(parent, SWT.NULL);
        GridLayout layout = new GridLayout();
        layout.numColumns = columns;
        composite.setLayout(layout);
        composite.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL));

        return composite;
    }

    /**
     * Called when the defaults-button is pressed.
     */
    protected void performDefaults() {
        super.performDefaults();

        IResource project = (IResource) getElement();

        if (project.getType() == IResource.PROJECT) {
            //load settings, if changed on disk
            if (TexlipseProperties.isProjectPropertiesFileChanged((IProject) project)) {
                TexlipseProperties.loadProjectProperties((IProject) project);
            }
        }
        
        // derived flags
        String derivTmp = TexlipseProperties.getProjectProperty(project, TexlipseProperties.MARK_TEMP_DERIVED_PROPERTY);
        derivedTempCheckbox.setSelection("true".equals(derivTmp));
        String derivOut = TexlipseProperties.getProjectProperty(project, TexlipseProperties.MARK_OUTPUT_DERIVED_PROPERTY);
        derivedOutputCheckbox.setSelection("true".equals(derivOut));
        
        // language code
        String lang = TexlipseProperties.getProjectProperty(project, TexlipseProperties.LANGUAGE_PROPERTY);
        languageField.setText((lang != null) ? lang : "");
        
        // makeindex style file
        String sty = TexlipseProperties.getProjectProperty(project, TexlipseProperties.MAKEINDEX_STYLEFILE_PROPERTY);
        indexStyleField.setText((sty != null) ? sty : "");
        
        // read source file name
        String srcDir = TexlipseProperties.getProjectProperty(project,
                TexlipseProperties.SOURCE_DIR_PROPERTY);
        if (srcDir == null) {
            srcDir = "";
        } else if (srcDir.length() > 0 && !srcDir.endsWith("/")) {
            srcDir += '/';
        }
        
        String srcFile = TexlipseProperties.getProjectProperty(project,
                TexlipseProperties.MAINFILE_PROPERTY);
        sourceFileField.setText((srcFile != null) ? (srcDir+srcFile) : "");
        
        // read temp dir
        String temp = TexlipseProperties.getProjectProperty(project,
                TexlipseProperties.TEMP_DIR_PROPERTY);
        tempDirField.setText((temp != null) ? temp : "");
        
        // read bibRef dir
//        String bibRef = TexlipseProperties.getProjectProperty(project,
//                TexlipseProperties.BIBREF_DIR_PROPERTY);
//        bibRefDirField.setText((bibRef != null) ? bibRef : "");

        // find out the default builder
        String str = TexlipseProperties.getProjectProperty(project,
                TexlipseProperties.BUILDER_NUMBER);
        int num = 0;
        if (str == null) {
            str = TexlipsePlugin.getPreference(TexlipseProperties.BUILDER_NUMBER);
        }
        try {
            num = Integer.parseInt(str);
        } catch (NumberFormatException e) {
        }
        builderChooser.setSelectedBuilder(num);

        // read output file name
        String outDir = TexlipseProperties.getProjectProperty(project,
                TexlipseProperties.OUTPUT_DIR_PROPERTY);
        if (outDir == null) {
            outDir = "";
        } else if (outDir.length() > 0 && !outDir.endsWith("/")) {
            outDir += '/';
        }
        
        String outFile = TexlipseProperties.getProjectProperty(project,
                TexlipseProperties.OUTPUTFILE_PROPERTY);
        outFileField.setText((outFile != null) ? (outDir+outFile) : "");
        
        // set status of the page
        validateSourceFileField();
        validateOutputFileField();
        validateTempFileField();

    }
    
    /**
     * Called when the ok-button is pressed.
     * Stores the values in the text fields to persistent properties.
     * 
     * @return false, if properties dialog should NOT be closed
     */
    public boolean performOk() {

        IResource project = (IResource) getElement();
        String srcDir = "";
        String outDir = "";
        
        // check source file & source dir
        String srcFile = sourceFileField.getText();
        if (srcFile != null) {
            srcFile = srcFile.trim();
            int index = srcFile.lastIndexOf('/');
            if (index > 0) {
                srcDir = srcFile.substring(0, index+1);
                srcFile = srcFile.substring(index+1);
            }
        }
        
        // check temp dir
        String tmpDir = tempDirField.getText();
        if (tmpDir != null) {
            tmpDir = tmpDir.trim();
        }
        
        // check bibRef dir
//        String bibRefDir = bibRefDirField.getText();
//        if (bibRefDir != null) {
//            bibRefDir = bibRefDir.trim();
//        }
        
        // check the preferred output format for this project
        String format = builderChooser.getSelectedFormat();
        if (format == null) {
            format = TexlipseProperties.OUTPUT_FORMAT_DVI;
        }

        // check output file & output dir
        String outFile = outFileField.getText();
        if (outFile != null) {
            outFile = outFile.trim();
            int index = outFile.lastIndexOf('/');
            if (index > 0) {
                outDir = outFile.substring(0, index+1);
                outFile = outFile.substring(index+1);
            }
        }
        
        if ((outFile == null || outFile.length() == 0)
                && (srcFile != null && srcFile.length() > 0)) {
            // if no output given, make one up
            outFile = srcFile.substring(0, srcFile.lastIndexOf('.')+1) + format;
            outDir = srcDir;
        }

        // save values
        int num = builderChooser.getSelectedBuilder();
        if (num == -1) {
            num = 0;
        }
        TexlipseProperties.setProjectProperty(project,
                TexlipseProperties.BUILDER_NUMBER, num+"");
        
        TexlipseProperties.setProjectProperty(project,
                TexlipseProperties.MARK_TEMP_DERIVED_PROPERTY, derivedTempCheckbox.getSelection()+"");
        
        TexlipseProperties.setProjectProperty(project,
                TexlipseProperties.MARK_OUTPUT_DERIVED_PROPERTY, derivedOutputCheckbox.getSelection()+"");
        
        TexlipseProperties.setProjectProperty(project,
                TexlipseProperties.LANGUAGE_PROPERTY, languageField.getText());
        
        TexlipseProperties.setProjectProperty(project,
                TexlipseProperties.MAKEINDEX_STYLEFILE_PROPERTY, indexStyleField.getText());
        
        TexlipseProperties.setProjectProperty(project,
                TexlipseProperties.MAINFILE_PROPERTY, srcFile);

        TexlipseProperties.setProjectProperty(project,
                TexlipseProperties.SOURCE_DIR_PROPERTY, srcDir);
                
        TexlipseProperties.setProjectProperty(project,
                TexlipseProperties.OUTPUTFILE_PROPERTY, outFile);
                
        TexlipseProperties.setProjectProperty(project,
                TexlipseProperties.OUTPUT_DIR_PROPERTY, outDir);
                
        TexlipseProperties.setProjectProperty(project,
                TexlipseProperties.TEMP_DIR_PROPERTY, tmpDir);
        
//        TexlipseProperties.setProjectProperty(project,
//                TexlipseProperties.BIBREF_DIR_PROPERTY, bibRefDir);
        TexlipseProperties.setProjectProperty(project,
                TexlipseProperties.BIBREF_DIR_PROPERTY, "");
                
        TexlipseProperties.setProjectProperty(project,
                TexlipseProperties.OUTPUT_FORMAT, format);
        
        //save settings to file
        if (project.getType() == IResource.PROJECT) {
            // Check whether setting file is writable
            IFile settingsFile = ((IProject) project).getFile(TexlipseProperties.LATEX_PROJECT_SETTINGS_FILE);
            if (!settingsFile.isReadOnly())
                TexlipseProperties.saveProjectProperties((IProject) project);
            else{
                MessageDialog.openWarning(this.getShell(), "Warning", 
                        TexlipsePlugin.getResourceString("projectSettingsReadOnly"));
            }
        }
        
        return true;
    }
    
    /**
     * Find out if the given file exists in the project.
     * 
     * @param filename the file name assumed to be in the project's main directory
     * @return true, if the given file exists
     */
    private boolean projectFileExists(String filename) {

        IResource res = (IResource) getElement();
        IProject project = res.getProject();
        
        IResource file = project.findMember(filename);
        return file != null && file.exists();
    }
}