/*******************************************************************************
 * Copyright (c) 2003, 2006 Subclipse project 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:
 *     Subclipse project committers - initial API and implementation
 ******************************************************************************/
package org.tigris.subversion.subclipse.ui.comments;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.osgi.util.NLS;
import org.eclipse.team.core.TeamException;
import org.tigris.subversion.subclipse.core.SVNException;
import org.tigris.subversion.subclipse.ui.Policy;
import org.tigris.subversion.subclipse.ui.SVNUIPlugin;
import org.tigris.subversion.subclipse.ui.internal.XMLWriter;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * the comments manager : holds previous comments 
 */
public class CommentsManager {
    // The previously remembered comment
    static String[] previousComments = new String[0];
    static String[] commentTemplates = new String[0];
    static final int MAX_COMMENTS = 10;
    private static final String COMMENT_HIST_FILE = "commitCommentHistory.xml"; //$NON-NLS-1$
    private static final String COMMENT_TEMPLATES_FILE = "commentTemplates.xml"; //$NON-NLS-1$
	static final String ELEMENT_COMMIT_COMMENT = "CommitComment"; //$NON-NLS-1$
	static final String ELEMENT_COMMIT_HISTORY = "CommitComments"; //$NON-NLS-1$
    static final String ELEMENT_COMMENT_TEMPLATES = "CommitCommentTemplates"; //$NON-NLS-1$


    /**
     * Answer the list of comments that were previously used when committing.
     * @return String[]
     */
    public String[] getPreviousComments() {
        return previousComments;
    }

    /**
     * Method addComment.
     * @param string
     */
	public void addComment(String comment) {
		// Make comment first element if it's already there
		int index = getCommentIndex(comment);
		if (index != -1) {
			makeFirstElement(index);
			return;
		}
		if (containsCommentTemplate(comment))
			return;
		
		// Insert the comment as the first element
		String[] newComments = new String[Math.min(previousComments.length + 1, MAX_COMMENTS)];
		newComments[0] = comment;
		for (int i = 1; i < newComments.length; i++) {
			newComments[i] = previousComments[i-1];
		}
		previousComments = newComments;
	}
	
	private int getCommentIndex(String comment) {
		for (int i = 0; i < previousComments.length; i++) {
			if (previousComments[i].equals(comment)) {
				return i;
			}
		}
		return -1;
	}
	
	private void makeFirstElement(int index) {
		String[] newComments = new String[previousComments.length];
		newComments[0] = previousComments[index];
		System.arraycopy(previousComments, 0, newComments, 1, index);
		int maxIndex = previousComments.length - 1;
		if (index != maxIndex) {
			int nextIndex = (index + 1);
			System.arraycopy(previousComments, nextIndex, newComments,
					nextIndex, (maxIndex - index));
		}
		previousComments = newComments;
	}

    /**
     * load the comment history 
     */
    public void loadCommentHistory() {
        IPath pluginStateLocation = SVNUIPlugin.getPlugin().getStateLocation().append(COMMENT_HIST_FILE);
        File file = pluginStateLocation.toFile();
        if (!file.exists()) return;
        try {
            BufferedInputStream is = new BufferedInputStream(new FileInputStream(file));
            try {
            	SAXParserFactory factory = SAXParserFactory.newInstance();

                try {
                	SAXParser parser = factory.newSAXParser();                	
                    parser.parse(new InputSource(is), new CommentHistoryContentHandler());
                } catch (SAXException ex) {
                    throw new SVNException(Policy.bind("RepositoryManager.parsingProblem", COMMENT_HIST_FILE), ex); //$NON-NLS-1$
                } catch (ParserConfigurationException e) {
                    throw new SVNException(Policy.bind("RepositoryManager.parsingProblem", COMMENT_HIST_FILE), e); //$NON-NLS-1$				
                }
            } finally {
                is.close();
            }
        } catch (IOException e) {
            SVNUIPlugin.log(new Status(IStatus.ERROR, SVNUIPlugin.ID, TeamException.UNABLE, Policy.bind("RepositoryManager.ioException"), e)); //$NON-NLS-1$
        } catch (TeamException e) {
            SVNUIPlugin.log(e.getStatus());
        }
    }

    /**
     * save the comments history
     */ 
    public void saveCommentHistory() throws TeamException {
        IPath pluginStateLocation = SVNUIPlugin.getPlugin().getStateLocation();
        File tempFile = pluginStateLocation.append(COMMENT_HIST_FILE + ".tmp").toFile(); //$NON-NLS-1$
        File histFile = pluginStateLocation.append(COMMENT_HIST_FILE).toFile();
        try {
                 XMLWriter writer = new XMLWriter(new BufferedOutputStream(new FileOutputStream(tempFile)));
                 try {
                     writer.startTag(ELEMENT_COMMIT_HISTORY, null, false);
                     for (int i=0; i<previousComments.length && i<MAX_COMMENTS; i++)
                         writer.printSimpleTag(ELEMENT_COMMIT_COMMENT, previousComments[i]);
                     writer.endTag(ELEMENT_COMMIT_HISTORY);
                 } finally {
                         writer.close();
                 }
                 if (histFile.exists()) {
                         histFile.delete();
                 }
                 boolean renamed = tempFile.renameTo(histFile);
                 if (!renamed) {
                         throw new TeamException(new Status(IStatus.ERROR, SVNUIPlugin.ID, TeamException.UNABLE, Policy.bind("RepositoryManager.rename", tempFile.getAbsolutePath()), null)); //$NON-NLS-1$
                 }
         } catch (IOException e) {
                 throw new TeamException(new Status(IStatus.ERROR, SVNUIPlugin.ID, TeamException.UNABLE, Policy.bind("RepositoryManager.save",histFile.getAbsolutePath()), e)); //$NON-NLS-1$
         }
    }

    public void loadCommentTemplates() {
		IPath pluginStateLocation = SVNUIPlugin.getPlugin().getStateLocation();
		File histFile = pluginStateLocation.append(COMMENT_TEMPLATES_FILE).toFile();
        if (!histFile.exists()) return;
        try {
            BufferedInputStream is = new BufferedInputStream(new FileInputStream(histFile));
            try {
                readCommentTemplates(is);
            } finally {
                is.close();
            }
		} catch (IOException e) {
            SVNUIPlugin.log(new Status(IStatus.ERROR, SVNUIPlugin.ID, TeamException.UNABLE, Policy.bind("RepositoryManager.ioException"), e)); //$NON-NLS-1$
        } catch (TeamException e) {
            SVNUIPlugin.log(e.getStatus());
        }
    }
    
    private void readCommentTemplates(InputStream stream) throws IOException, TeamException {
		try {
			SAXParserFactory factory = SAXParserFactory.newInstance();
			SAXParser parser = factory.newSAXParser();
			parser.parse(new InputSource(stream),
					new CommentTemplatesContentHandler());
		} catch (SAXException ex) {
			throw new SVNException(NLS.bind(
					Policy.bind("RepositoryManager.parsingProblem"),
					new String[] { COMMENT_TEMPLATES_FILE }), ex);
		} catch (ParserConfigurationException ex) {
			throw new SVNException(NLS.bind(
					Policy.bind("RepositoryManager.parsingProblem"),
					new String[] { COMMENT_TEMPLATES_FILE }), ex);
		}
	}
	
	public void saveCommentTemplates() throws TeamException {
		IPath pluginStateLocation = SVNUIPlugin.getPlugin().getStateLocation();
		File tempFile = pluginStateLocation.append(
				COMMENT_TEMPLATES_FILE + ".tmp").toFile(); //$NON-NLS-1$
		File histFile = pluginStateLocation.append(COMMENT_TEMPLATES_FILE)
				.toFile();
		try {
			XMLWriter writer = new XMLWriter(new BufferedOutputStream(
					new FileOutputStream(tempFile)));
			try {
				writeCommentTemplates(writer);
			} finally {
				writer.close();
			}
			if (histFile.exists()) {
				histFile.delete();
			}
			boolean renamed = tempFile.renameTo(histFile);
			if (!renamed) {
				throw new TeamException(new Status(IStatus.ERROR,
						SVNUIPlugin.ID, TeamException.UNABLE, NLS.bind(
								Policy.bind("RepositoryManager.rename"),
								new String[] { tempFile.getAbsolutePath() }),
						null));
			}
		} catch (IOException e) {
			throw new TeamException(new Status(IStatus.ERROR, SVNUIPlugin.ID,
					TeamException.UNABLE, NLS.bind(
							Policy.bind("RepositoryManager.save"),
							new String[] { histFile.getAbsolutePath() }), e));
		}
	}
	
	private void writeCommentTemplates(XMLWriter writer) {
		writer.startTag(ELEMENT_COMMENT_TEMPLATES, null, false);
		for (int i = 0; i < commentTemplates.length; i++)
			writer.printSimpleTag(ELEMENT_COMMIT_COMMENT, commentTemplates[i]);
		writer.endTag(ELEMENT_COMMENT_TEMPLATES);
	}
	
	private boolean containsCommentTemplate(String comment) {
		for (int i = 0; i < commentTemplates.length; i++) {
			if (commentTemplates[i].equals(comment)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Get list of comment templates.
	 */
	public String[] getCommentTemplates() {
		return commentTemplates;
	}
	
	public void replaceAndSaveCommentTemplates(String[] templates)
			throws TeamException {
		commentTemplates = templates;
		saveCommentTemplates();
	}
}
