/*
 * ====================================================================
 * Copyright (c) 2004-2012 TMate Software Ltd.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://svnkit.com/license.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 * ====================================================================
 */
package org.tmatesoft.svn.core.wc;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNProperty;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.wc.SVNAdminUtil;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminArea;
import org.tmatesoft.svn.core.internal.wc.admin.SVNLog;


/**
 * The <b>SVNMergeFileSet</b> class holds information about the file that is to be merged.
 * This information includes references to <code>File</code> objects with working, base, repository contents;
 * file mimeType; labels to append to the file name to produce conflict files in case a merge fails with a
 * conflict, and so on.
 *
 * @version 1.3
 * @author  TMate Software Ltd.
 * @since   1.2
 */
public class SVNMergeFileSet {

    private String myLocalFilePath;
    private String myBaseFilePath;
    private String myRepositoryFilePath;
    private String myWCFilePath;
    private String myMergeResultFilePath;

    private String myMimeType;
    private SVNAdminArea myAdminArea;
    private SVNLog myLog;

    private String myLocalLabel;
    private String myBaseLabel;
    private String myRepositoryLabel;

    private File myLocalFile;
    private File myBaseFile;
    private File myRepositoryFile;
    private File myMergeResultFile;
    private File myCopyFromFile;

    private Collection myTmpPaths = new ArrayList();

    /**
     * Creates a new <code>SVNMergeFileSet</code> object given the data prepared for
     * merging a file.
     *
     * <p/>
     * Note: This is intended for internal use only, not for API users.
     *
     * @param adminArea     admin area the file is controlled under
     * @param log           log object
     * @param baseFile      file with pristine contents
     * @param localFile     file with translated working contents
     * @param wcPath        working copy path relative to the location of <code>adminArea</code>
     * @param reposFile     file contents from the repository
     * @param resultFile    file where the resultant merged contents will be written to
     * @param copyFromFile  contents of the copy source file (if any)
     * @param mimeType      file mime type
     */
    public SVNMergeFileSet(SVNAdminArea adminArea, SVNLog log,
            File baseFile,
            File localFile,
            String wcPath,
            File reposFile,
            File resultFile,
            File copyFromFile,
            String mimeType) {
        myAdminArea = adminArea;
        myLog = log;
        myLocalFile = localFile;
        myBaseFile = baseFile;
        myRepositoryFile = reposFile;
        myWCFilePath = wcPath;
        myMergeResultFile = resultFile;
        myCopyFromFile = copyFromFile;
        myMimeType = mimeType;

        if (myBaseFile != null) {
            if(myAdminArea!=null) {
                myBaseFilePath = SVNPathUtil.isAncestor(myAdminArea.getAdminDirectory().getAbsolutePath(),
                    myBaseFile.getAbsolutePath()) ? SVNFileUtil.getBasePath(myBaseFile) : null;
            } else {
                myBaseFilePath = myBaseFile.getAbsolutePath();
            }
        }
        if (myLocalFile != null) {
            myLocalFilePath = SVNFileUtil.getBasePath(myLocalFile);
        }
        if (myRepositoryFile != null) {
            if(myAdminArea!=null) {
                myRepositoryFilePath = SVNPathUtil.isAncestor(myAdminArea.getAdminDirectory().getAbsolutePath(),
                    myRepositoryFile.getAbsolutePath()) ? SVNFileUtil.getBasePath(myRepositoryFile) : null;
            } else {
                myRepositoryFilePath = myRepositoryFile.getAbsolutePath();
            }
        }
        if (myMergeResultFile != null) {
            myMergeResultFilePath = SVNFileUtil.getBasePath(myMergeResultFile);
        }
    }

    /**
     * Sets the labels for conflict files.
     *
     * <p/>
     * If <code>baseLabel</code> is <span class="javakeyword">null</span>,
     * <span class="javastring">".old"</span> will be set by default.
     * If <code>localLabel</code> is <span class="javakeyword">null</span>,
     * <span class="javastring">".working"</span> will be set by default.
     * If <code>repositoryLabel</code> is <span class="javakeyword">null</span>,
     * <span class="javastring">".new"</span> will be set by default.
     *
     * @param baseLabel          base file label
     * @param localLabel         working file label
     * @param repositoryLabel    repository file label
     */
    public void setMergeLabels(String baseLabel, String localLabel, String repositoryLabel) {
        myLocalLabel = localLabel == null ? ".working" : localLabel;
        myBaseLabel = baseLabel == null ? ".old" : baseLabel;
        myRepositoryLabel = repositoryLabel == null ? ".new" : repositoryLabel;
    }

    /**
     * Returns the log object.
     *
     * <p/>
     * Note: This is intended for internal use only, not for API users.
     *
     * @return wc modification commands logger
     */
    public SVNLog getLog() {
        return myLog;
    }

    /**
     * Returns the base file label.
     *
     * @return base label string
     */
    public String getBaseLabel() {
        return myBaseLabel;
    }

    /**
     * Returns the local file label.
     *
     * @return working file label
     */
    public String getLocalLabel() {
        return myLocalLabel;
    }

    /**
     * Returns the repository file label.
     *
     * @return label of the repository file version
     */
    public String getRepositoryLabel() {
        return myRepositoryLabel;
    }

    /**
     * Returns the base file path.
     *
     * <p/>
     * If the {@link #getBaseFile() base file} is located under the
     * {@link #getAdminArea() admin area}, then the return path will be just a relevant to the admin area path
     * of the base file. Otherwise (in case the repository file is located not under the admin area) this
     * method will create a temporary file in the <code>.svn/tmp</code> area of the admin area and copy the
     * contents of the base file into it; the return path will be again relative to the location of
     * the admin area.
     *
     * @return               path of the file with pristine contents
     * @throws SVNException
     */
    public String getBasePath() throws SVNException {
        if (myBaseFilePath == null && myBaseFile != null) {
            File tmp = SVNAdminUtil.createTmpFile(myAdminArea);
            SVNFileUtil.copyFile(myBaseFile, tmp, false);
            myBaseFilePath = SVNFileUtil.getBasePath(tmp);
            myTmpPaths.add(myBaseFilePath);
        }
        return myBaseFilePath;
    }

    /**
     * Returns the path of the detranslated version of the working copy file.
     * Detranslating of a working copy file takes place in case it's a symlink, or it has keywords or
     * eol-style properties set on it.
     *
     * @return path to the file with detranslated working contents; it's relevant to the
     *         {@link #getAdminArea() admin area} location
     */
    public String getLocalPath() {
        return myLocalFilePath;
    }

    /**
     * Returns the path of the working copy file.
     *
     * @return path of the working copy file; it's relevant to the {@link #getAdminArea() admin area} location
     */
    public String getWCPath() {
        return myWCFilePath;
    }

    /**
     * Returns the path to the file containing the contents of the repository version of the file.
     *
     * <p/>
     * If the {@link #getRepositoryFile() repository file} is located under the
     * {@link #getAdminArea() admin area}, then the return path will be just a relevant to the admin area path
     * of the repository file. Otherwise (in case the repository file is located not under the admin area) this
     * method will create a temporary file in the <code>.svn/tmp</code> area of the admin area and copy the
     * contents of the repository file into it; the return path will be again relative to the location of
     * the admin area.
     *
     * @return                path of the file containing file contents that come from the repository
     * @throws SVNException
     */
    public String getRepositoryPath() throws SVNException {
        if (myRepositoryFilePath == null && myRepositoryFile != null) {
            File tmp = SVNAdminUtil.createTmpFile(myAdminArea);
            SVNFileUtil.copyFile(myRepositoryFile, tmp, false);
            myRepositoryFilePath = SVNFileUtil.getBasePath(tmp);
            myTmpPaths.add(myRepositoryFilePath);
        }
        return myRepositoryFilePath;
    }

    /**
     * Returns the path of the file where the merged resultant text is written to.
     *
     * @return path of the result file; it's relevant to the {@link #getAdminArea() admin area} location
     */
    public String getResultPath() {
        return myMergeResultFilePath;
    }

    /**
     * Returns the file containing the pristine file contents.
     * @return base file
     */
    public File getBaseFile() {
        return myBaseFile;
    }

    /**
     * Returns the working copy file as it presents in the working copy.
     * @return working copy file
     */
    public File getWCFile() {
        if(myAdminArea!=null) {
            return myAdminArea.getFile(myWCFilePath);
        }
        return myWCFilePath!=null? new File(myWCFilePath) : null;
    }

    /**
     * Returns the detranslated working copy file.
     * Detranslating of a working copy file takes place in case it's a symlink, or it has keywords or
     * eol-style properties set on it.
     *
     * @return detranslated working copy file
     */
    public File getLocalFile() {
        return myLocalFile;
    }

    /**
     * Returns the repository version of the file.
     * @return repository file
     */
    public File getRepositoryFile() {
        return myRepositoryFile;
    }

    /**
     * Returns the file where the merged resultant text is written to.
     * @return merge result file
     */
    public File getResultFile() {
        return myMergeResultFile;
    }

    /**
     * Tells whether this file is binary or textual.
     * The result will depend on the value of the file {@link #getMimeType() mime type}.
     *
     * @return <span class="javakeyword">true</span> if binary
     */
    public boolean isBinary() {
        return SVNProperty.isBinaryMimeType(myMimeType);
    }

    /**
     * Returns the mime type of the file.
     * @return file mime type
     */
    public String getMimeType() {
        return myMimeType;
    }

    /**
     * Returns the admin area which controls the file.
     *
     * <p/>
     * Note: this method is not intended for API users.
     * @return admin area
     */
    public SVNAdminArea getAdminArea() {
        return myAdminArea;
    }

    /**
     * Disposes this object.
     *
     * <p/>
     * Note: this method is not intended for API users.
     * @throws SVNException
     */
    public void dispose() throws SVNException {
        // add deletion commands to the log file.
        SVNProperties command = new SVNProperties();
        for (Iterator paths = myTmpPaths.iterator(); paths.hasNext();) {
            String path = (String) paths.next();
            command.put(SVNLog.NAME_ATTR, path);
            myLog.addCommand(SVNLog.DELETE, command, false);
            command.clear();
        }
    }

    /**
     * Returns the file which is the copy source for the file being merged.
     * @return copy source file
     */
    public File getCopyFromFile() {
        return myCopyFromFile;
    }

    /**
     * Returns the copy source path.
     *
     * @return   path of the copy source file;
     *           it's relevant to the {@link #getAdminArea() admin area} location
     * @since    1.3
     */
    public String getCopyFromPath() {
        String root = myAdminArea.getRoot().getAbsolutePath().replace(File.separatorChar, '/');
        String copyFrom = getCopyFromFile().getAbsolutePath().replace(File.separatorChar, '/');
        String copyFromPath = copyFrom.substring(root.length());
        copyFromPath = copyFromPath.startsWith("/") ? copyFromPath.substring("/".length()) : copyFromPath;
        return copyFromPath;
    }
}