/*
 * ====================================================================
 * 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.internal.wc;

import java.io.File;
import java.io.OutputStream;
import java.util.Collection;

import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNProperty;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.SVNPropertyValue;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.diff.SVNDiffWindow;
import org.tmatesoft.svn.core.wc.ISVNDiffStatusHandler;
import org.tmatesoft.svn.core.wc.SVNDiffStatus;
import org.tmatesoft.svn.core.wc.SVNStatusType;



/**
 * @version 1.3
 * @author  TMate Software Ltd.
 */
public class SVNDiffStatusEditor implements ISVNEditor {
    
    private SVNSummarize myCurrentDirSummarize;
    private SVNSummarize myCurrentFileSummarize;

    private SVNRepository myRepository;
    private ISVNDiffStatusHandler myHandler;
    private long myRevision;
    private SVNURL myRootURL;
    private File myAnchor;
    private String myTarget;
    
    public SVNDiffStatusEditor(File anchor, String target, SVNRepository repos, long revision, ISVNDiffStatusHandler handler) {
        myRepository = repos;
        myHandler = handler;
        myRevision = revision;
        myRootURL = repos.getLocation();
        myAnchor = anchor;
        myTarget = target;
    }

    public void openRoot(long revision) throws SVNException {
        myCurrentDirSummarize = new SVNSummarize(null, "", SVNNodeKind.DIR);
    }

    public void targetRevision(long revision) throws SVNException {
    }

    public void deleteEntry(String path, long revision) throws SVNException {
        SVNNodeKind kind = myRepository.checkPath(path, myRevision);
        String statusPath = getStatusPath(path);
        SVNDiffStatus status = new SVNDiffStatus(myAnchor != null ? new File(myAnchor, path) : null, myRootURL.appendPath(path, false), statusPath, SVNStatusType.STATUS_DELETED, false, kind);
        myHandler.handleDiffStatus(status);
        if (kind == SVNNodeKind.DIR) {
            diffDeletedDir(path);
        }
    }

    private void diffDeletedDir(String path) throws SVNException {
        Collection<SVNDirEntry> entries = myRepository.getDir(path, myRevision, null, SVNDirEntry.DIRENT_KIND, (Collection<SVNDirEntry>) null);
        for (SVNDirEntry entry : entries) {
            String name = entry.getName();
            if ("".equals(name)) {
                continue;
            }
            String entryPath = SVNPathUtil.append(path, name);
            String statusPath = getStatusPath(entryPath);
            SVNDiffStatus status = new SVNDiffStatus(
                    myAnchor != null ? new File(myAnchor, entryPath) : null, 
                    myRootURL.appendPath(entryPath, false), 
                    statusPath, 
                    SVNStatusType.STATUS_DELETED, 
                    false, 
                    entry.getKind());
            myHandler.handleDiffStatus(status);
            if (entry.getKind() == SVNNodeKind.DIR) {
                diffDeletedDir(entryPath);
            }
        }
    }

    private String getStatusPath(String path) {
        String statusPath = path;
        if (myTarget != null && (path.equals(myTarget) || path.startsWith(myTarget + "/"))) {
            statusPath = SVNPathUtil.removeHead(path);
        }
        if (statusPath.startsWith("/")) {
            statusPath = statusPath.substring(1);
        }
        return statusPath;
    }

    public void openDir(String path, long revision) throws SVNException {
        myCurrentDirSummarize = new SVNSummarize(myCurrentDirSummarize, path, SVNNodeKind.DIR);
    }

    public void addDir(String path, String copyFromPath, long copyFromRevision) throws SVNException {
        myCurrentDirSummarize = new SVNSummarize(myCurrentDirSummarize, path, SVNNodeKind.DIR);
        myCurrentDirSummarize.myType = SVNStatusType.STATUS_ADDED;
    }

    public void changeDirProperty(String name, SVNPropertyValue value) throws SVNException {
        if (SVNProperty.isRegularProperty(name)) {
            if (myCurrentDirSummarize.myType != SVNStatusType.STATUS_ADDED) {
                myCurrentDirSummarize.myPropChanged = true;
            }
        }
    }

    public void closeDir() throws SVNException {
        myHandler.handleDiffStatus(myCurrentDirSummarize.toStatus());
        myCurrentDirSummarize = myCurrentDirSummarize.myParent;
    }

    public void openFile(String path, long revision) throws SVNException {
        myCurrentFileSummarize = new SVNSummarize(myCurrentDirSummarize, path, SVNNodeKind.FILE);
    }

    public void addFile(String path, String copyFromPath, long copyFromRevision) throws SVNException {
        myCurrentFileSummarize = new SVNSummarize(myCurrentDirSummarize, path, SVNNodeKind.FILE);
        myCurrentFileSummarize.myType = SVNStatusType.STATUS_ADDED;
    }

    public void changeFileProperty(String path, String name, SVNPropertyValue value) throws SVNException {
        if (SVNProperty.isRegularProperty(name)) {
            if (myCurrentFileSummarize.myType != SVNStatusType.STATUS_ADDED) {
                myCurrentFileSummarize.myPropChanged = true;
            }
        }
    }

    public void applyTextDelta(String path, String baseChecksum) throws SVNException {
        if (myCurrentFileSummarize.myType != SVNStatusType.STATUS_ADDED) {
            myCurrentFileSummarize.myType = SVNStatusType.STATUS_MODIFIED;
        }
    }

    public OutputStream textDeltaChunk(String path, SVNDiffWindow diffWindow) throws SVNException {
        return null;
    }

    public void textDeltaEnd(String path) throws SVNException {
    }

    public void closeFile(String path, String textChecksum) throws SVNException {
        myHandler.handleDiffStatus(myCurrentFileSummarize.toStatus());
        myCurrentFileSummarize = null;
    }

    public SVNCommitInfo closeEdit() throws SVNException {
        if (myCurrentDirSummarize != null) {
            myHandler.handleDiffStatus(myCurrentDirSummarize.toStatus());
        }
        return null;
    }

    public void abortEdit() throws SVNException {
    }

    public void absentDir(String path) throws SVNException {
    }

    public void absentFile(String path) throws SVNException {
    }

    private class SVNSummarize {
        
        public SVNSummarize(SVNSummarize parent, String path, SVNNodeKind kind) {
            myKind = kind;
            myType = SVNStatusType.STATUS_NONE;
            myParent = parent;
            myFile = myAnchor != null ? new File(myAnchor, path) : null;
            myPath = getStatusPath(path);
        }
        
        public SVNDiffStatus toStatus() throws SVNException {
            return new SVNDiffStatus(myFile, myRootURL.appendPath(myPath, false), myPath, myType, myPropChanged, myKind);
        }
        
        private File myFile;
        private String myPath;
        private SVNNodeKind myKind;
        private SVNStatusType myType;
        private boolean myPropChanged; 
        private SVNSummarize myParent;
    }

}
