/*
 * ====================================================================
 * Copyright (c) 2004-2011 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.cli.svnlook;

import java.io.File;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.tmatesoft.svn.cli.AbstractSVNCommand;
import org.tmatesoft.svn.cli.AbstractSVNCommandEnvironment;
import org.tmatesoft.svn.cli.AbstractSVNOption;
import org.tmatesoft.svn.cli.SVNCommandLine;
import org.tmatesoft.svn.cli.SVNOptionValue;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.fs.FSRepository;
import org.tmatesoft.svn.core.internal.io.fs.FSTransactionInfo;
import org.tmatesoft.svn.core.internal.util.SVNHashSet;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNPath;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNDiffOptions;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
import org.tmatesoft.svn.util.SVNLogType;

/**
 * @version 1.3
 * @author  TMate Software Ltd.
 */
public class SVNLookCommandEnvironment extends AbstractSVNCommandEnvironment {

    private long myRevision;
    private String myTransaction;
    private boolean myIsNonRecursive;
    private boolean myIsVerbose;
    private boolean myIsHelp;
    private boolean myIsRevProp;
    private boolean myIsVersion;
    private boolean myIsShowIDs;
    private long myLimit;
    private boolean myIsNoDiffDeleted;
    private boolean myIsNoDiffAdded;
    private boolean myIsDiffCopyFrom;
    private boolean myIsFullPaths;
    private boolean myIsCopyInfo;
    private Collection myExtensions;
    private boolean myIsRevision;
    private File myRepositoryFile;
    private FSRepository myRepository;
    private FSTransactionInfo myTransactionInfo;
    private String myArgument1;
    private String myArgument2;

    public SVNLookCommandEnvironment(String programName, PrintStream out, PrintStream err, InputStream in) {
        super(programName, out, err, in);
        myRevision = -1;
        myExtensions = new SVNHashSet();
    }
    
    public File getRepositoryFile() {
        return myRepositoryFile;
    }
    
    public long getRevision() {
        return myRevision;
    }
    
    public String getTransaction() {
        return myTransaction;
    }
    
    public boolean isNonRecursive() {
        return myIsNonRecursive;
    }
    
    public boolean isVerbose() {
        return myIsVerbose;
    }
    
    public boolean isHelp() {
        return myIsHelp;
    }
    
    public boolean isRevProp() {
        return myIsRevProp;
    }
    
    public boolean isVersion() {
        return myIsVersion;
    }
    
    public boolean isShowIDs() {
        return myIsShowIDs;
    }
    
    public long getLimit() {
        return myLimit;
    }
    
    public boolean isNoDiffDeleted() {
        return myIsNoDiffDeleted;
    }
    
    public boolean isNoDiffAdded() {
        return myIsNoDiffAdded;
    }

    public boolean isDiffCopyFrom() {
        return myIsDiffCopyFrom;
    }

    public boolean isFullPaths() {
        return myIsFullPaths;
    }
    
    public boolean isCopyInfo() {
        return myIsCopyInfo;
    }
    
    public Collection getExtensions() {
        return myExtensions;
    }
    
    public boolean isRevision() {
        return myIsRevision;
    }
    
    public FSTransactionInfo getTransactionInfo() {
        return myTransactionInfo;
    }
    
    public FSRepository getRepository() {
        return myRepository;
    }
    
    public String getFirstArgument() {
        return myArgument1;
    }

    public String getSecondArgument() {
        return myArgument2;
    }

    protected ISVNAuthenticationManager createClientAuthenticationManager() {
        return SVNWCUtil.createDefaultAuthenticationManager();
    }

    protected DefaultSVNOptions createClientOptions() {
        return SVNWCUtil.createDefaultOptions(true);
    }

    protected void validateOptions(SVNCommandLine commandLine) throws SVNException {
        super.validateOptions(commandLine);
        if (myRevision >= 0 && myTransaction != null) {
            SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_MUTUALLY_EXCLUSIVE_ARGS, 
                    "The '--transaction' (-t) and '--revision' (-r) arguments can not co-exist");
            SVNErrorManager.error(err, SVNLogType.CLIENT);
        }
        myIsRevision = myTransaction == null;
        
        if (!(myIsHelp || myIsVersion || "help".equals(commandLine.getCommandName()))) {
            if (getArguments().isEmpty()) {
                SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_INSUFFICIENT_ARGS, "Repository argument required");
                SVNErrorManager.error(err, SVNLogType.CLIENT);
            }
            SVNPath path = new SVNPath((String) getArguments().get(0), false);
            if (path.isURL()) {
                SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_ARG_PARSING_ERROR, "''{0}'' is URL when it should be a path", path.getTarget());
                SVNErrorManager.error(err, SVNLogType.CLIENT);
            }
            myRepositoryFile = path.getFile();
            myRepository = (FSRepository) SVNRepositoryFactory.create(SVNURL.fromFile(myRepositoryFile));
            myRepository.setCanceller(this);
            myRepository.testConnection();
            if (getTransaction() != null) {
                myTransactionInfo = myRepository.getFSFS().openTxn(getTransaction());
            } else {
                if (myRevision < 0) {
                    myRevision = myRepository.getLatestRevision();
                }
            }
            List updatedArguments = new LinkedList(getArguments());
            updatedArguments.remove(0);
            if (!updatedArguments.isEmpty()) {
                myArgument1 = (String) updatedArguments.remove(0);
            }
            if (!updatedArguments.isEmpty()) {
                myArgument2 = (String) updatedArguments.remove(0);
            }
            setArguments(updatedArguments);
        }
    }

    protected void initOption(SVNOptionValue optionValue) throws SVNException {
        AbstractSVNOption option = optionValue.getOption();
        if (option == SVNLookOption.REVISION) {
            long revision = -1; 
            if (optionValue.getValue() != null) {
                try {
                    revision = Long.parseLong(optionValue.getValue());
                } catch (NumberFormatException nfe) {
                }
            }
            if (revision < 0) {
                SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_ARG_PARSING_ERROR, "Invalid revision number supplied");
                SVNErrorManager.error(err, SVNLogType.CLIENT);
            }
            myRevision = revision;
        } else if (option == SVNLookOption.TRANSACTION) {
            myTransaction = optionValue.getValue();
        } else if (option == SVNLookOption.NON_RECURSIVE) {
            myIsNonRecursive = true;
        } else if (option == SVNLookOption.VERBOSE) {
            myIsVerbose = true;
        } else if (option == SVNLookOption.HELP || option == SVNLookOption.QUESTION) {
            myIsHelp = true;
        } else if (option == SVNLookOption.REVPROP) {
            myIsRevProp = true;
        } else if (option == SVNLookOption.VERSION) {
            myIsVersion = true;
        } else if (option == SVNLookOption.SHOW_IDS) {
            myIsShowIDs = true;
        } else if (option == SVNLookOption.LIMIT) {
            long limit = -1; 
            if (optionValue.getValue() != null) {
                try {
                    limit = Long.parseLong(optionValue.getValue());
                } catch (NumberFormatException nfe) {
                    SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_ARG_PARSING_ERROR, "Non-numeric limit argument given");
                    SVNErrorManager.error(err, SVNLogType.CLIENT);
                }
            } else {
                SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_ARG_PARSING_ERROR, "Non-numeric limit argument given");
                SVNErrorManager.error(err, SVNLogType.CLIENT);
            }
            if (limit <= 0) {
                SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_ARG_PARSING_ERROR, "Argument to --limit must be positive");
                SVNErrorManager.error(err, SVNLogType.CLIENT);
            }
            myLimit = limit;
        } else if (option == SVNLookOption.NO_DIFF_DELETED) {
            myIsNoDiffDeleted = true;
        } else if (option == SVNLookOption.NO_DIFF_ADDED) {
            myIsNoDiffAdded = true;
        } else if (option == SVNLookOption.DIFF_COPY_FROM) {
            myIsDiffCopyFrom = true;
        } else if (option == SVNLookOption.FULL_PATHS) {
            myIsFullPaths = true;
        } else if (option == SVNLookOption.COPY_INFO) {
            myIsCopyInfo = true;
        } else if (option == SVNLookOption.EXTENSIONS) {
            myExtensions.add(optionValue.getValue());
        }
    }

    public SVNDiffOptions getDiffOptions() throws SVNException {
        LinkedList extensions = new LinkedList(myExtensions);
        boolean ignoreAllWS = myExtensions.contains("-w") || myExtensions.contains("--ignore-all-space");
        if (ignoreAllWS) {
            extensions.remove("-w");
            extensions.remove("--ignore-all-space");
        }
        boolean ignoreAmountOfWS = myExtensions.contains("-b") || myExtensions.contains("--ignore-space-change");
        if (ignoreAmountOfWS) {
            extensions.remove("-b");
            extensions.remove("--ignore-space-change");
        }
        boolean ignoreEOLStyle = myExtensions.contains("--ignore-eol-style");
        if (ignoreEOLStyle) {
            extensions.remove("--ignore-eol-style");
        }
        if (!extensions.isEmpty()) {
            SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.INVALID_DIFF_OPTION, 
                    "Invalid argument ''{0}'' in diff options", extensions.get(0));
            SVNErrorManager.error(err, SVNLogType.CLIENT);
        }
        return new SVNDiffOptions(ignoreAllWS, ignoreAmountOfWS, ignoreEOLStyle);
    }

    protected String refineCommandName(String commandName, SVNCommandLine commandLine) throws SVNException {
        for (Iterator options = commandLine.optionValues(); options.hasNext();) {
            SVNOptionValue optionValue = (SVNOptionValue) options.next();
            AbstractSVNOption option = optionValue.getOption();
            if (option == SVNLookOption.HELP || option == SVNLookOption.QUESTION) {
                myIsHelp = true;                
            } else if (option == SVNLookOption.VERSION) {
                myIsVersion = true;
            }
        }
        
        if (myIsHelp) {
            List newArguments = commandName != null ? Collections.singletonList(commandName) : Collections.EMPTY_LIST;
            setArguments(newArguments);
            return "help";
        } 
        if (commandName == null) {
            if (myIsVersion) {
                SVNLookCommand versionCommand = new SVNLookCommand("--version", null) {
                    protected Collection createSupportedOptions() {
                        LinkedList options = new LinkedList();
                        options.add(SVNLookOption.VERSION);
                        return options;
                    }
                    
                    public void run() throws SVNException {
                        AbstractSVNCommand helpCommand = AbstractSVNCommand.getCommand("help");
                        helpCommand.init(SVNLookCommandEnvironment.this);
                        helpCommand.run();
                    }
                };
                AbstractSVNCommand.registerCommand(versionCommand);
                return "--version";
            }
            SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_INSUFFICIENT_ARGS, "Subcommand argument required");
            SVNErrorManager.error(err, SVNLogType.CLIENT);
        }
        return commandName;
    }

    protected String getCommandLineClientName() {
        return "svnlook";
    }

}
