package org.tmatesoft.svn.core.internal.wc2.remote;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
import org.tmatesoft.svn.core.internal.util.SVNHashSet;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.util.SVNURLUtil;
import org.tmatesoft.svn.core.internal.wc.ISVNCommitPathHandler;
import org.tmatesoft.svn.core.internal.wc.SVNCommitUtil;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNEventFactory;
import org.tmatesoft.svn.core.internal.wc.SVNPropertiesManager;
import org.tmatesoft.svn.core.internal.wc2.SvnRemoteOperationRunner;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNEvent;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.core.wc2.SvnCommitItem;
import org.tmatesoft.svn.core.wc2.SvnRemoteMkDir;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import org.tmatesoft.svn.util.SVNLogType;

public class SvnRemoteRemoteMkDir extends SvnRemoteOperationRunner<SVNCommitInfo, SvnRemoteMkDir>  {

    @Override
    protected SVNCommitInfo run() throws SVNException {
        SVNCommitInfo info = doRun();
        if (info != null) {
            Collection<SvnTarget> targets = getOperation().getTargets();
            if (targets != null && targets.size() != 0) {
                SvnTarget firstTarget = targets.iterator().next();

                SVNRepository repository = getRepositoryAccess().createRepository(firstTarget.getURL(), null, true);
                SVNURL repositoryRoot = repository.getRepositoryRoot(true);

                getOperation().receive(SvnTarget.fromURL(repositoryRoot), info);
            }
        }
        return info;
    }
    
    protected SVNCommitInfo doRun() throws SVNException {
    	int i = 0;
        SVNURL[] urls = new SVNURL[getOperation().getTargets().size()];
        for (SvnTarget target : getOperation().getTargets()) {
            urls[i++] = target.getURL();
        }
        
    	if (getOperation().isMakeParents()) {
            List<SVNURL> allURLs = new LinkedList<SVNURL>();
            for (i = 0; i < urls.length; i++) {
                SVNURL url = urls[i];
                addURLParents(allURLs, url);
            }
            urls = (SVNURL[]) allURLs.toArray(new SVNURL[allURLs.size()]);
        }
        if (urls == null || urls.length == 0) {
            return SVNCommitInfo.NULL;
        }
        Collection<String> paths = new SVNHashSet();
        SVNURL rootURL = SVNURLUtil.condenceURLs(urls, paths, false);
        if (rootURL == null) {
            SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.RA_ILLEGAL_URL, "Can not compute common root URL for specified URLs");
            SVNErrorManager.error(err, SVNLogType.DEFAULT);
        }
        if (paths.isEmpty()) {
            paths.add(SVNPathUtil.tail(rootURL.getURIEncodedPath()));
            rootURL = rootURL.removePathTail();
        }
        if (paths.contains("")) {
            List<String> convertedPaths = new ArrayList<String>();
            String tail = SVNPathUtil.tail(rootURL.getURIEncodedPath());
            rootURL = rootURL.removePathTail();
            for (Iterator<String> commitPaths = paths.iterator(); commitPaths.hasNext();) {
                String path = (String) commitPaths.next();
                if ("".equals(path)) {
                    convertedPaths.add(tail);
                } else {
                    convertedPaths.add(SVNPathUtil.append(tail, path));
                }
            }
            paths = convertedPaths;
        }
        List<String> sortedPaths = new ArrayList<String>(paths);
        Collections.sort(sortedPaths, SVNPathUtil.PATH_COMPARATOR);
        String commitMessage;
        if (getOperation().getCommitHandler() != null) {
	        SvnCommitItem[] commitItems = new SvnCommitItem[sortedPaths.size()];
	        for (i = 0; i < commitItems.length; i++) {
	            String path = (String) sortedPaths.get(i);
	            SvnCommitItem item = new SvnCommitItem();
	            item.setKind(SVNNodeKind.DIR);
	            item.setUrl(rootURL.appendPath(path, true));
	            item.setFlags(SvnCommitItem.ADD);
	            commitItems[i] = item; 
	        }
	        commitMessage = getOperation().getCommitHandler().getCommitMessage(getOperation().getCommitMessage(), commitItems);
	        if (commitMessage == null) {
	            return SVNCommitInfo.NULL;
	        }
        }
        else {
        	commitMessage = getOperation().getCommitMessage();
        }
        commitMessage = commitMessage == null ? "" : SVNCommitUtil.validateCommitMessage(commitMessage);
        List<String> decodedPaths = new ArrayList<String>();
        for (Iterator<String> commitPaths = sortedPaths.iterator(); commitPaths.hasNext();) {
            String path = commitPaths.next();
            decodedPaths.add(SVNEncodingUtil.uriDecode(path));
        }
        paths = decodedPaths;
        SVNRepository repository = getRepositoryAccess().createRepository(rootURL, null, true);
        commitMessage = SVNCommitUtil.validateCommitMessage(commitMessage);
        SVNPropertiesManager.validateRevisionProperties(getOperation().getRevisionProperties());
        ISVNEditor commitEditor = repository.getCommitEditor(commitMessage, null, false, getOperation().getRevisionProperties(), null);
        ISVNCommitPathHandler creater = new ISVNCommitPathHandler() {

            public boolean handleCommitPath(String commitPath, ISVNEditor commitEditor) throws SVNException {
                SVNPathUtil.checkPathIsValid(commitPath);
                commitEditor.addDir(commitPath, null, -1);
                return true;
            }
        };
        SVNCommitInfo info;
        try {
            SVNCommitUtil.driveCommitEditor(creater, paths, commitEditor, -1);
            SVNEvent event = SVNEventFactory.createSVNEvent(null, SVNNodeKind.UNKNOWN, null, SVNRepository.INVALID_REVISION, SVNEventAction.COMMIT_FINALIZING, SVNEventAction.COMMIT_FINALIZING, null, null);
            event.setURL(rootURL);
            handleEvent(event);
            info = commitEditor.closeEdit();
        } catch (SVNException e) {
            try {
                commitEditor.abortEdit();
            } catch (SVNException inner) {
            }
            throw e;
        }
        if (info != null && info.getNewRevision() >= 0) {
        	handleEvent(SVNEventFactory.createSVNEvent(null, SVNNodeKind.NONE, null, info.getNewRevision(), SVNEventAction.COMMIT_COMPLETED, null, null, null), ISVNEventHandler.UNKNOWN);
        }
        return info != null ? info : SVNCommitInfo.NULL;
    }
    
    private void addURLParents(List<SVNURL> targets, SVNURL url) throws SVNException {
        SVNURL parentURL = url.removePathTail();
        SVNRepository repository = getRepositoryAccess().createRepository(parentURL, null, true);
        SVNNodeKind kind = repository.checkPath("", SVNRepository.INVALID_REVISION);
        if (kind == SVNNodeKind.NONE) {
            addURLParents(targets, parentURL);
        }
        targets.add(url);
    }
}
