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

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import org.tmatesoft.svn.core.ISVNLogEntryHandler;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.util.SVNDate;
import org.tmatesoft.svn.core.internal.util.SVNHashMap;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.util.SVNURLUtil;
import org.tmatesoft.svn.core.internal.wc17.SVNWCConflictDescription17;
import org.tmatesoft.svn.core.internal.wc17.SVNWCContext;
import org.tmatesoft.svn.core.internal.wc17.SVNWCUtils;
import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb;
import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.SVNWCDbStatus;
import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.WCDbBaseInfo.BaseInfoField;
import org.tmatesoft.svn.core.internal.wc17.db.Structure;
import org.tmatesoft.svn.core.internal.wc17.db.StructureFields.NodeInfo;
import org.tmatesoft.svn.core.internal.wc2.ISvnCommitRunner;
import org.tmatesoft.svn.core.wc.ISVNAddParameters;
import org.tmatesoft.svn.core.wc.ISVNAnnotateHandler;
import org.tmatesoft.svn.core.wc.ISVNChangelistHandler;
import org.tmatesoft.svn.core.wc.ISVNCommitHandler;
import org.tmatesoft.svn.core.wc.ISVNCommitParameters;
import org.tmatesoft.svn.core.wc.ISVNDiffStatusHandler;
import org.tmatesoft.svn.core.wc.ISVNExternalsHandler;
import org.tmatesoft.svn.core.wc.ISVNFileFilter;
import org.tmatesoft.svn.core.wc.ISVNPropertyHandler;
import org.tmatesoft.svn.core.wc.ISVNPropertyValueProvider;
import org.tmatesoft.svn.core.wc.ISVNStatusFileProvider;
import org.tmatesoft.svn.core.wc.SVNCommitItem;
import org.tmatesoft.svn.core.wc.SVNCommitPacket;
import org.tmatesoft.svn.core.wc.SVNConflictDescription;
import org.tmatesoft.svn.core.wc.SVNCopySource;
import org.tmatesoft.svn.core.wc.SVNDiffStatus;
import org.tmatesoft.svn.core.wc.SVNInfo;
import org.tmatesoft.svn.core.wc.SVNPropertyData;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNRevisionRange;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNStatusType;
import org.tmatesoft.svn.core.wc.SVNTreeConflictDescription;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
import org.tmatesoft.svn.core.wc.admin.ISVNChangeEntryHandler;
import org.tmatesoft.svn.core.wc.admin.ISVNChangedDirectoriesHandler;
import org.tmatesoft.svn.core.wc.admin.ISVNHistoryHandler;
import org.tmatesoft.svn.core.wc.admin.ISVNTreeHandler;
import org.tmatesoft.svn.core.wc.admin.SVNAdminPath;
import org.tmatesoft.svn.core.wc.admin.SVNChangeEntry;
import org.tmatesoft.svn.core.wc2.ISvnAddParameters;
import org.tmatesoft.svn.core.wc2.ISvnCommitParameters;
import org.tmatesoft.svn.core.wc2.ISvnObjectReceiver;
import org.tmatesoft.svn.core.wc2.SvnAnnotateItem;
import org.tmatesoft.svn.core.wc2.SvnChecksum;
import org.tmatesoft.svn.core.wc2.SvnCommit;
import org.tmatesoft.svn.core.wc2.SvnCommitItem;
import org.tmatesoft.svn.core.wc2.SvnCommitPacket;
import org.tmatesoft.svn.core.wc2.SvnCopySource;
import org.tmatesoft.svn.core.wc2.SvnDiffStatus;
import org.tmatesoft.svn.core.wc2.SvnInfo;
import org.tmatesoft.svn.core.wc2.SvnRevisionRange;
import org.tmatesoft.svn.core.wc2.SvnSchedule;
import org.tmatesoft.svn.core.wc2.SvnStatus;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import org.tmatesoft.svn.core.wc2.SvnWorkingCopyInfo;
import org.tmatesoft.svn.core.wc2.hooks.ISvnCommitHandler;
import org.tmatesoft.svn.core.wc2.hooks.ISvnExternalsHandler;
import org.tmatesoft.svn.core.wc2.hooks.ISvnFileFilter;
import org.tmatesoft.svn.core.wc2.hooks.ISvnFileListHook;
import org.tmatesoft.svn.core.wc2.hooks.ISvnPropertyValueProvider;

public class SvnCodec {
    
    public static SVNDiffStatus diffStatus(SvnDiffStatus diffStatus) {
        return new SVNDiffStatus(diffStatus.getFile(), diffStatus.getUrl(), diffStatus.getPath(), diffStatus.getModificationType(), diffStatus.isPropertiesModified(), diffStatus.getKind());
    }
    
    public static SvnDiffStatus diffStatus(SVNDiffStatus diffStatus) {
        SvnDiffStatus result = new SvnDiffStatus();
        result.setFile(diffStatus.getFile());
        result.setUrl(diffStatus.getURL());
        result.setPath(diffStatus.getPath());
        result.setModificationType(diffStatus.getModificationType());
        result.setPropertiesModified(diffStatus.isPropertiesModified());
        result.setKind(diffStatus.getKind());
        result.setUserData(diffStatus);
        return result;
    }
    
    public static ISvnObjectReceiver<SVNAdminPath> treeReceiver(final ISVNTreeHandler handler) {
        return new ISvnObjectReceiver<SVNAdminPath>() {
            public void receive(SvnTarget target, SVNAdminPath path) throws SVNException {
                if (handler != null) {
                    handler.handlePath(path);
                }
            }
        };
    }
    
    public static ISvnObjectReceiver<SVNAdminPath> changedHistoryReceiver(final ISVNHistoryHandler handler) {
        return new ISvnObjectReceiver<SVNAdminPath>() {
            public void receive(SvnTarget target, SVNAdminPath path) throws SVNException {
                if (handler != null) {
                    handler.handlePath(path);
                }
            }
        };
    }
    
    public static ISvnObjectReceiver<String> changedDirectoriesReceiver(final ISVNChangedDirectoriesHandler handler) {
        return new ISvnObjectReceiver<String>() {
            public void receive(SvnTarget target, String path) throws SVNException {
                if (handler != null) {
                    handler.handleDir(path);
                }
            }
        };
    }
    
    public static ISvnObjectReceiver<SVNChangeEntry> changeEntryReceiver(final ISVNChangeEntryHandler handler) {
        return new ISvnObjectReceiver<SVNChangeEntry>() {
            public void receive(SvnTarget target, SVNChangeEntry entry) throws SVNException {
                if (handler != null) {
                    handler.handleEntry(entry);
                }
            }
        };
    }
    
    
    
    public static ISvnObjectReceiver<String> changelistReceiver(final ISVNChangelistHandler handler) {
        return new ISvnObjectReceiver<String>() {
            public void receive(SvnTarget target, String object) throws SVNException {
                if (handler != null) {
                    handler.handle(target.getFile(), object);
                }
            }
        };
    }
    
    public static ISvnObjectReceiver<SVNPropertyData> propertyReceiver(final ISVNPropertyHandler handler) {
        return new ISvnObjectReceiver<SVNPropertyData>() {
            public void receive(SvnTarget target, SVNPropertyData object) throws SVNException {
                if (handler != null) {
                    handler.handleProperty(target.getFile(), object);
                }
            }
        };
    }
    
    public static ISvnObjectReceiver<SVNLogEntry> logReceiver(final ISVNLogEntryHandler handler) {
        return new ISvnObjectReceiver<SVNLogEntry>() {
            public void receive(SvnTarget target, SVNLogEntry object) throws SVNException {
                if (handler != null) {
                    handler.handleLogEntry(object);
                }
            }
        };
    }
    
    public static ISvnObjectReceiver<SvnAnnotateItem> annotateReceiver(final ISVNAnnotateHandler handler) {
        return new ISvnObjectReceiver<SvnAnnotateItem>() {
            public void receive(SvnTarget target, SvnAnnotateItem item) throws SVNException {
                if (handler != null) {
                	if (item.isEof())
                		handler.handleEOF();
                	else if (item.isLine()) 
                		handler.handleLine(item.getDate(), item.getRevision(), item.getAuthor(), item.getLine(), item.getMergedDate(), 
                				item.getMergedRevision(), item.getMergedAuthor(), item.getMergedPath(), item.getLineNumber());
                	else if (item.isRevision())
                		item.setReturnResult(handler.handleRevision(item.getDate(), item.getRevision(), item.getAuthor(), item.getContents()));

                }
            }
        };
    }

    public static ISvnObjectReceiver<SvnDiffStatus> diffStatusReceiver(final ISVNDiffStatusHandler handler) {
        return new ISvnObjectReceiver<SvnDiffStatus>() {
            public void receive(SvnTarget target, SvnDiffStatus svnDiffStatus) throws SVNException {
                if (handler != null) {
                    handler.handleDiffStatus(diffStatus(svnDiffStatus));
                }
            }
        };
    }
    
    public static SvnStatus status(SVNStatus status) {
        SvnStatus result = new SvnStatus();
        result.setUserData(status);
        
        result.setPath(status.getFile());
        result.setChangedAuthor(status.getAuthor());
        result.setChangedDate(SVNDate.fromDate(status.getCommittedDate()));
        result.setChangedRevision(revisionNumber(status.getCommittedRevision()));
        result.setChangelist(status.getChangelistName());
        result.setConflicted(status.isConflicted());
        result.setCopied(status.isCopied());
        result.setDepth(status.getDepth());
        result.setFileExternal(status.isFileExternal());
        // TODO
        //result.setFileSize()
        result.setKind(status.getKind());
        result.setLock(status.getLocalLock());
        
        // combine node and contents?
        result.setNodeStatus(status.getNodeStatus());
        result.setTextStatus(status.getContentsStatus());
        result.setPropertiesStatus(status.getPropertiesStatus());
        
        result.setRepositoryChangedAuthor(status.getRemoteAuthor());
        result.setRepositoryChangedDate(SVNDate.fromDate(status.getRemoteDate()));
        result.setRepositoryChangedRevision(revisionNumber(status.getRemoteRevision()));
        result.setRepositoryKind(status.getRemoteKind());
        result.setRepositoryLock(status.getRemoteLock());
        
        // combine node and contents?
        result.setRepositoryNodeStatus(status.getRemoteNodeStatus());
        result.setRepositoryTextStatus(status.getRemoteContentsStatus());
        result.setRepositoryPropertiesStatus(status.getRemotePropertiesStatus());
        
        result.setRepositoryRelativePath(status.getRepositoryRelativePath());
        result.setRepositoryRootUrl(status.getRepositoryRootURL());
        result.setRepositoryUuid(status.getRepositoryUUID());
        
        result.setRevision(revisionNumber(status.getRevision()));
        result.setSwitched(status.isSwitched());
        result.setVersioned(status.isVersioned());
        result.setWcLocked(status.isLocked());

        result.setWorkingCopyFormat(status.getWorkingCopyFormat());
        
        result.setMovedFromPath(status.getMovedFromPath());
        result.setMovedToPath(status.getMovedToPath());
        
        try {
            result.setCopyFromUrl(status.getCopyFromURL() != null ? SVNURL.parseURIEncoded(status.getCopyFromURL()) : null);
            result.setCopyFromRevision(status.getCopyFromRevision() != null ? status.getCopyFromRevision().getNumber() : -1);
        } catch (SVNException e) {
            result.setCopyFromUrl(null);
        }
        return result;
    }
    
    public static long revisionNumber(SVNRevision revision) {
        if (revision == null || revision == SVNRevision.UNDEFINED) {
            return SVNWCContext.INVALID_REVNUM;
        }
        return revision.getNumber();
    } 
    
    public static SVNStatus status(SVNWCContext context, SvnStatus status) throws SVNException {
        if (status.getUserData() instanceof SVNStatus) {
            return (SVNStatus) status.getUserData();
        }
        
        SVNStatus result = new SVNStatus();
        result.setFile(status.getPath());
        result.setKind(status.getKind());
        // TODO filesize
        result.setIsVersioned(status.isVersioned());
        result.setIsConflicted(status.isConflicted());
        
        result.setNodeStatus(status.getNodeStatus());
        result.setContentsStatus(status.getTextStatus());
        result.setPropertiesStatus(status.getPropertiesStatus());


        if (status.getKind() == SVNNodeKind.DIR) {
            result.setIsLocked(status.isWcLocked());
        }
        result.setIsFileExternal(status.isFileExternal());
        result.setIsCopied(status.isCopied());
        result.setRevision(SVNRevision.create(status.getRevision()));
        
        result.setCommittedRevision(SVNRevision.create(status.getChangedRevision()));
        result.setAuthor(status.getChangedAuthor());
        result.setCommittedDate(status.getChangedDate());
        
        result.setRepositoryRootURL(status.getRepositoryRootUrl());
        result.setRepositoryRelativePath(status.getRepositoryRelativePath());
        result.setRepositoryUUID(status.getRepositoryUuid());
        
        result.setIsSwitched(status.isSwitched());
        if (status.isVersioned() && status.isSwitched() && status.getKind() == SVNNodeKind.FILE) {
           // TODO fileExternal
        }
        result.setLocalLock(status.getLock());
        result.setChangelistName(status.getChangelist());
        result.setDepth(status.getDepth());
        
        result.setRemoteKind(status.getRepositoryKind());
        result.setRemoteNodeStatus(status.getRepositoryNodeStatus());
        result.setRemoteContentsStatus(status.getRepositoryTextStatus());
        result.setRemotePropertiesStatus(status.getRepositoryPropertiesStatus());
        result.setRemoteLock(status.getRepositoryLock());
        
        result.setRemoteAuthor(status.getRepositoryChangedAuthor());
        result.setRemoteRevision(SVNRevision.create(status.getRepositoryChangedRevision()));
        result.setRemoteDate(status.getRepositoryChangedDate());
        
        // do all that on demand in SVNStatus class later.
        // compose URL on demand in SVNStatus
        
        if (status.isVersioned() && status.isConflicted()) {
            SVNWCContext.ConflictInfo info = context.getConflicted(status.getPath(), true, true, true);
            if (info.textConflicted) {
                result.setContentsStatus(SVNStatusType.STATUS_CONFLICTED);
            }
            if (info.propConflicted) {
                result.setPropertiesStatus(SVNStatusType.STATUS_CONFLICTED);
            }
            if (info.textConflicted || info.propConflicted) {
                result.setNodeStatus(SVNStatusType.STATUS_CONFLICTED);
            }
        }
        
        if (status.getRepositoryRootUrl() != null && status.getRepositoryRelativePath() != null) {
            SVNURL url = status.getRepositoryRootUrl().appendPath(status.getRepositoryRelativePath(), false);
            if (status.isVersioned()) {
                result.setURL(url);
            }
            result.setRemoteURL(url);
        }
        
        if (context != null && status.isVersioned() && status.getRevision() == SVNWCContext.INVALID_REVNUM && !status.isCopied()) {
            if (status.getNodeStatus() == SVNStatusType.STATUS_REPLACED) {
                fetchStatusRevision(context, status, result);
            } else if (status.getNodeStatus() == SVNStatusType.STATUS_DELETED ) {
                fetchStatusRevision(context, status, result);
            }
        }
        
        if (context != null && status.isConflicted()) {
            boolean hasTreeConflict = false;
            SVNWCContext.ConflictInfo conflictedInfo = null;
            if (status.isVersioned()) {
                try {
                    conflictedInfo = context.getConflicted(status.getPath(), true, true, true);
                } catch (SVNException e) {
                    if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_UPGRADE_REQUIRED) {
                    } else {
                        throw e;
                    }
                }
                hasTreeConflict = conflictedInfo != null && conflictedInfo.treeConflicted;
            } else {
                hasTreeConflict = true;
            }
        
            if (hasTreeConflict) {
                SVNTreeConflictDescription treeConflictDescription = context.getTreeConflict(status.getPath());
                result.setTreeConflict(treeConflictDescription);
            }
            
            if (conflictedInfo != null) {
                result.setConflictWrkFile(conflictedInfo.localFile);
                result.setConflictOldFile(conflictedInfo.baseFile);
                result.setConflictNewFile(conflictedInfo.repositoryFile);                    
                result.setPropRejectFile(conflictedInfo.propRejectFile);                    
            }
        }
        
        if (result.getNodeStatus() == SVNStatusType.STATUS_ADDED) {
            result.setPropertiesStatus(SVNStatusType.STATUS_NONE);
        }
        result.setWorkingCopyFormat(status.getWorkingCopyFormat());
        
        result.setCopyFromRevision(status.getCopyFromRevision() >= 0 ? SVNRevision.create(status.getCopyFromRevision()) : SVNRevision.UNDEFINED);
        result.setCopyFromURL(status.getCopyFromUrl() != null ? status.getCopyFromUrl().toString() : null);
        
        result.setMovedFromPath(status.getMovedFromPath());
        result.setMovedToPath(status.getMovedToPath());
        
        return result;
    }
    
    private static void fetchStatusRevision(SVNWCContext context, SvnStatus source, SVNStatus result) throws SVNException {
        Structure<NodeInfo> info = context.getDb().readInfo(source.getPath(), NodeInfo.revision, NodeInfo.changedAuthor, NodeInfo.changedDate, NodeInfo.changedRev, 
                NodeInfo.haveBase, NodeInfo.haveWork, NodeInfo.haveMoreWork, NodeInfo.status);
        
        if (source.getNodeStatus() == SVNStatusType.STATUS_DELETED) {
            result.setAuthor(info.text(NodeInfo.changedAuthor));
            result.setCommittedDate(info.<SVNDate>get(NodeInfo.changedDate));
            result.setCommittedRevision(SVNRevision.create(info.lng(NodeInfo.changedRev)));
        }
        result.setRevision(SVNRevision.create(info.lng(NodeInfo.revision)));
        SVNWCDbStatus st = info.<SVNWCDbStatus>get(NodeInfo.status); 
        if (info.is(NodeInfo.haveWork) || info.lng(NodeInfo.revision) == SVNWCContext.INVALID_REVNUM || 
                (source.getNodeStatus() == SVNStatusType.STATUS_DELETED && info.lng(NodeInfo.changedRev) == SVNWCContext.INVALID_REVNUM) || 
                (st != SVNWCDbStatus.Added && st != SVNWCDbStatus.Deleted)) {
            info.release();
            
            try {
                ISVNWCDb.WCDbBaseInfo binfo = context.getDb().getBaseInfo(source.getPath(), BaseInfoField.revision, BaseInfoField.changedRev, BaseInfoField.changedAuthor, BaseInfoField.changedDate);            
                if (source.getNodeStatus() == SVNStatusType.STATUS_DELETED) {
                    result.setAuthor(binfo.changedAuthor);
                    result.setCommittedDate(binfo.changedDate);
                    result.setCommittedRevision(SVNRevision.create(binfo.changedRev));
                }
                result.setRevision(SVNRevision.create(binfo.revision));
            } catch (SVNException e) {
                if (e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_PATH_NOT_FOUND) {
                    throw e;
                }
            }
        } else {
            info.release();
        }
    }
    
    public static SvnInfo info(SVNInfo info) {
        SvnInfo result = new SvnInfo();
        result.setUserData(info);
        result.setKind(info.getKind());
        result.setLastChangedAuthor(info.getAuthor());
        result.setLastChangedDate(SVNDate.fromDate(info.getCommittedDate()));
        result.setLastChangedRevision(info.getCommittedRevision().getNumber());
        result.setLock(info.getLock());
        result.setRepositoryRootURL(info.getRepositoryRootURL());
        result.setRepositoryUuid(info.getRepositoryUUID());
        result.setRevision(info.getRevision().getNumber());
        result.setSize(-1);
        result.setUrl(info.getURL());
        
        SvnWorkingCopyInfo wcInfo = new SvnWorkingCopyInfo();
        
        result.setWcInfo(wcInfo);
        wcInfo.setChangelist(info.getChangelistName());
        if (info.getChecksum() != null) {
            SvnChecksum checksum = new SvnChecksum(SvnChecksum.Kind.md5, info.getChecksum());
            wcInfo.setChecksum(checksum);
        }
        
        if (info.getTreeConflict() != null || 
                info.getConflictWrkFile() != null || info.getConflictNewFile() != null || info.getConflictOldFile() != null || 
                info.getPropConflictFile() != null) {
            Collection<SVNConflictDescription> conflicts = new ArrayList<SVNConflictDescription>();
            if (info.getTreeConflict() != null) {
                conflicts.add(info.getTreeConflict());
            }
            if (info.getConflictWrkFile() != null || info.getConflictNewFile() != null || info.getConflictOldFile() != null) {
                SVNWCConflictDescription17 cd = SVNWCConflictDescription17.createText(info.getFile());
                cd.setTheirFile(info.getConflictNewFile());
                cd.setBaseFile(info.getConflictOldFile());
                cd.setMyFile(info.getConflictWrkFile());
                conflicts.add(cd.toConflictDescription());
            }
            if (info.getPropConflictFile() != null) {
                SVNWCConflictDescription17 cd = SVNWCConflictDescription17.createProp(info.getFile(), info.getKind(), null);
                cd.setTheirFile(info.getPropConflictFile());
                conflicts.add(cd.toConflictDescription());
            }
            wcInfo.setConflicts(conflicts);
        }
        
        wcInfo.setCopyFromRevision(info.getCommittedRevision().getNumber());
        wcInfo.setCopyFromUrl(info.getCopyFromURL());
        wcInfo.setDepth(info.getDepth());
        wcInfo.setPath(info.getFile());
        wcInfo.setRecordedSize(info.getWorkingSize());
        if (info.getTextTime() != null) {
            wcInfo.setRecordedTime(info.getTextTime().getTime());
        }
        wcInfo.setSchedule(SvnSchedule.fromString(info.getSchedule()));
        
        File wcRoot = null;
        try {
            wcRoot = SVNWCUtil.getWorkingCopyRoot(info.getFile(), true);
        } catch (SVNException e) {
        }
        wcInfo.setWcRoot(wcRoot);
        wcInfo.setMovedFrom(info.getMovedFromPath());
        wcInfo.setMovedTo(info.getMovedToPath());
        
        return result;
    }
    
    public static SVNInfo info(SvnInfo info) {
        if (info.getUserData() instanceof SVNInfo) {
            return ((SVNInfo) info.getUserData());
        }
        if (info.getWcInfo() == null) {
            String rootPath = info.getRepositoryRootUrl().getPath();
            String itemPath = info.getUrl().getPath();
            itemPath = SVNPathUtil.getPathAsChild(rootPath, itemPath);
            if (itemPath == null) {
                itemPath = "";
            }
            return new SVNInfo(itemPath, info.getUrl(), SVNRevision.create(info.getRevision()), info.getKind(), info.getRepositoryUuid(), info.getRepositoryRootUrl(), 
                    info.getLastChangedRevision(), info.getLastChangedDate(), info.getLastChangedAuthor(), info.getLock(), SVNDepth.UNKNOWN, info.getSize());
        }
        SvnWorkingCopyInfo wcInfo = info.getWcInfo();
        
        String conflictOld = null;
        String conflictNew = null;
        String conflictWorking = null;
        String propRejectFile = null;
        SVNTreeConflictDescription treeConflict = null;
        
        Collection<SVNConflictDescription> conflicts = wcInfo.getConflicts();
        if (conflicts != null) {
            for (SVNConflictDescription conflictDescription : conflicts) {
                if (conflictDescription.isTreeConflict() && conflictDescription instanceof SVNTreeConflictDescription) {
                    treeConflict = (SVNTreeConflictDescription) conflictDescription;
                } else if (conflictDescription.isTextConflict()) {
                    if (conflictDescription.getMergeFiles() != null) {
                        if (conflictDescription.getMergeFiles().getBaseFile() != null) {
                            conflictOld = conflictDescription.getMergeFiles().getBaseFile().getName();
                        }
                        if (conflictDescription.getMergeFiles().getRepositoryFile() != null) {
                            conflictNew = conflictDescription.getMergeFiles().getRepositoryFile().getName();
                        }
                        if (conflictDescription.getMergeFiles().getLocalFile() != null) {
                            conflictWorking = conflictDescription.getMergeFiles().getLocalFile().getName();
                        }
                    }
                } else if (conflictDescription.isPropertyConflict()) {
                    if (conflictDescription.getMergeFiles() != null) {
                        propRejectFile = conflictDescription.getMergeFiles().getRepositoryFile().getName();
                    }
                }
            }
        }
        
        String schedule = wcInfo.getSchedule() != null ? wcInfo.getSchedule().asString() : null;
        SVNInfo i = new SVNInfo(wcInfo.getPath(), 
                info.getUrl(), 
                info.getRepositoryRootUrl(), 
                info.getRevision(), 
                info.getKind(), 
                info.getRepositoryUuid(), 
                info.getLastChangedRevision(),
                info.getLastChangedDate() != null ? info.getLastChangedDate().format() : null, 
                info.getLastChangedAuthor(), 
                schedule, 
                wcInfo.getCopyFromUrl(), 
                wcInfo.getCopyFromRevision(), 
                wcInfo.getRecordedTime() > 0 ? SVNWCUtils.readDate(wcInfo.getRecordedTime()).format() : null, 
                null,
                wcInfo.getChecksum() != null ? wcInfo.getChecksum().getDigest() : null, 
                conflictOld, 
                conflictNew, 
                conflictWorking, 
                propRejectFile, 
                info.getLock(), 
                wcInfo.getDepth(), 
                wcInfo.getChangelist(), 
                wcInfo.getRecordedSize(), 
                treeConflict);
        i.setWorkingCopyRoot(wcInfo.getWcRoot());
        i.setMovedFromPath(wcInfo.getMovedFrom());
        i.setMovedToPath(wcInfo.getMovedTo());
        return i;
    }
    
    public static ISvnFileListHook fileListHook(final ISVNStatusFileProvider provider) {
        if (provider == null) {
            return null;
        }
        return new ISvnFileListHook() {
            public Map<String, File> listFiles(File parent) {
                return provider.getChildrenFiles(parent);
            }
        };
    }
    
    public static ISVNStatusFileProvider fileListProvider(final ISvnFileListHook hook) {
        if (hook == null) {
            return null;
        }
        return new ISVNStatusFileProvider() {
            public Map<String, File> getChildrenFiles(File parent) {
                return hook.listFiles(parent);
            }
        };
    }
    
    public static SVNCommitItem commitItem(SvnCommitItem item) {
        return new SVNCommitItem(item.getPath(), 
                item.getUrl(), item.getCopyFromUrl(), 
                item.getKind(), 
                SVNRevision.create(item.getRevision()), 
                SVNRevision.create(item.getCopyFromRevision()), 
                item.hasFlag(SvnCommitItem.ADD), 
                item.hasFlag(SvnCommitItem.DELETE), 
                item.hasFlag(SvnCommitItem.PROPS_MODIFIED), 
                item.hasFlag(SvnCommitItem.TEXT_MODIFIED), 
                item.hasFlag(SvnCommitItem.COPY), 
                item.hasFlag(SvnCommitItem.LOCK));
    }

    public static SvnCommitItem commitItem(SVNCommitItem item) {
        SvnCommitItem i = new SvnCommitItem();
        i.setPath(item.getFile());
        i.setUrl(item.getURL());
        i.setKind(item.getKind());
        if (item.getCopyFromRevision() != null) {
            i.setCopyFromRevision(item.getCopyFromRevision().getNumber());
        } else {
            i.setCopyFromRevision(-1);
        }        
        i.setCopyFromUrl(i.getCopyFromUrl());
        i.setRevision(item.getRevision() != null ? item.getRevision().getNumber() : -1);
        int flags = 0;
        if (item.isAdded()) {
            flags |= SvnCommitItem.ADD;
        }
        if (item.isContentsModified()) {
            flags |= SvnCommitItem.TEXT_MODIFIED;
        }
        if (item.isPropertiesModified()) {
            flags |= SvnCommitItem.PROPS_MODIFIED;
        }
        if (item.isCopied()) {
            flags |= SvnCommitItem.COPY;
        }
        if (item.isDeleted()) {
            flags |= SvnCommitItem.DELETE;
        }
        if (item.isLocked()) {
            flags |= SvnCommitItem.LOCK;
        }
        i.setFlags(flags);
        return i;
    }
    
    public static class SVNCommitPacketWrapper extends SVNCommitPacket {

        private SvnCommitPacket packet;
        private SvnCommit operation;

        public SVNCommitPacketWrapper(SvnCommit operation, SvnCommitPacket packet, SVNCommitItem[] items, Map<String, String> lockTokens) {
            super(null, items, lockTokens);
            this.operation = operation;
            this.packet = packet;
        }

        @Override
        public void dispose() throws SVNException {
            packet.dispose();
        }
        
        public SvnCommitPacket getPacket() {
            return this.packet;
        }
        
        public SvnCommit getOperation() {
            return operation;
        }

        @Override
        public void setCommitItemSkipped(SVNCommitItem item, boolean skipped) {
            super.setCommitItemSkipped(item, skipped);
            packet.setItemSkipped(item.getFile(), skipped);
        }

        @Override
        public SVNCommitPacket removeSkippedItems() {
            packet.removeSkippedItems();

            if (this == EMPTY) {
                return EMPTY;
            }
            Collection<SVNCommitItem> items = new ArrayList<SVNCommitItem>();
            @SuppressWarnings("unchecked")
            Map<String, String> lockTokens = getLockTokens() == null ? null : new SVNHashMap(getLockTokens());
            SVNCommitItem[] filteredItems = filterSkippedItemsAndLockTokens(items, lockTokens);
            return new SVNCommitPacketWrapper(getOperation(), packet, filteredItems, lockTokens);
        }
    }
    
    public static SVNCommitPacket commitPacket(final SvnCommit operation, final SvnCommitPacket packet) {
        Collection<SVNCommitItem> skippedItems = new ArrayList<SVNCommitItem>();
        Collection<SVNCommitItem> oldItems = new ArrayList<SVNCommitItem>();
        for (SVNURL reposRoot : packet.getRepositoryRoots()) {
            for (SvnCommitItem item : packet.getItems(reposRoot)) {
                SVNCommitItem oldItem = commitItem(item);
                oldItems.add(oldItem);

                if (packet.isItemSkipped(item.getPath())) {
                    skippedItems.add(oldItem);
                }
            }
        }
        final SVNCommitItem[] allItems = oldItems.toArray(new SVNCommitItem[oldItems.size()]);
        
        Map<String, String> oldLockTokens = new HashMap<String, String>();
        if (packet.getLockTokens() != null) {
            for (SVNURL url : packet.getLockTokens().keySet()) {
                String token = packet.getLockTokens().get(url);
                oldLockTokens.put(url.toString(), token);
            }
        }

        final SVNCommitPacketWrapper packetWrapper = new SVNCommitPacketWrapper(operation, packet, allItems, oldLockTokens);
        for (SVNCommitItem skippedItem : skippedItems) {
            packetWrapper.setCommitItemSkipped(skippedItem, true);
        }
        return packetWrapper;
    }

    public static SvnCommitPacket commitPacket(ISvnCommitRunner runner, SVNCommitPacket oldPacket) {
        SvnCommitPacket packet = new SvnCommitPacket();
        packet.setLockingContext(runner, oldPacket);
        Map<SVNURL, String> lockTokens = new HashMap<SVNURL, String>();
        SVNCommitItem[] items = oldPacket.getCommitItems();
        @SuppressWarnings("unchecked")
        Map<String, String> locks = oldPacket.getLockTokens();
        if (locks != null) {
            for (String url : locks.keySet()) {
                try {
                    lockTokens.put(SVNURL.parseURIEncoded(url), locks.get(url));
                } catch (SVNException e) {
                    //
                }
            }
        }
        Collection<SVNURL> allUrl = new HashSet<SVNURL>();
        SVNURL rootUrl = null;
        for (int j = 0; j < items.length; j++) {
            SVNCommitItem item = items[j];
            allUrl.add(item.getURL());
            if (item.getCopyFromURL() != null) {
                allUrl.add(item.getCopyFromURL());
            }
        }
        for (SVNURL svnurl : allUrl) {
            if (rootUrl == null) {
                rootUrl = svnurl;
            } else {
                rootUrl = SVNURLUtil.getCommonURLAncestor(rootUrl, svnurl);
            }
        }
        for (int j = 0; j < items.length; j++) {
            SVNCommitItem item = items[j];
            int flags = 0;
            if (item.isAdded()) {
                flags |= SvnCommitItem.ADD;
            }
            if (item.isContentsModified()) {
                flags |= SvnCommitItem.TEXT_MODIFIED;
            }
            if (item.isCopied()) {
                flags |= SvnCommitItem.COPY;
            }
            if (item.isDeleted()) {
                flags |= SvnCommitItem.DELETE;
            }
            if (item.isLocked()) {
                flags |= SvnCommitItem.LOCK;
            }
            if (item.isPropertiesModified()) {
                flags |= SvnCommitItem.PROPS_MODIFIED;
            }
            try {
                SvnCommitItem newItem = packet.addItem(item.getFile(),
                        rootUrl,
                        item.getKind(),
                        item.getURL(), item.getRevision() != null ? item.getRevision().getNumber() : -1,
                        item.getCopyFromURL(),
                        item.getCopyFromRevision() != null ? item.getCopyFromRevision().getNumber() : -1,
                        flags);
                if (oldPacket.isCommitItemSkipped(item)) {
                    packet.setItemSkipped(newItem.getPath(), true);
                }
            } catch (SVNException e) {
                //
            }
        }
        packet.setLockTokens(lockTokens);
        return packet;
    }
    
    public static SVNRevisionRange revisionRange(SvnRevisionRange range) {
        return new SVNRevisionRange(range.getStart(), range.getEnd());
    }
    
    public static SvnRevisionRange revisionRange(SVNRevisionRange range) {
        return SvnRevisionRange.create(range.getStartRevision(), range.getEndRevision());
    }

    public static Collection<SvnRevisionRange> revisionRanges(Collection<SVNRevisionRange> ranges) {
        Collection<SvnRevisionRange> result = new ArrayList<SvnRevisionRange>();
        if (ranges != null) {
            for (SVNRevisionRange range : ranges) {
                result.add(revisionRange(range));
            }
        }
        return result;
    }

    public static Collection<SVNRevisionRange> oldRevisionRanges(Collection<SvnRevisionRange> ranges) {
        Collection<SVNRevisionRange> result = new ArrayList<SVNRevisionRange>();
        if (ranges != null) {
            for (SvnRevisionRange range : ranges) {
                result.add(revisionRange(range));
            }
        }
        return result;
    }

    public static SVNCopySource copySource(SvnCopySource newSource) {
        if (newSource.getSource().getURL() != null) {
            final SVNCopySource copySource = new SVNCopySource(newSource.getSource().getResolvedPegRevision(), newSource.getRevision(), newSource.getSource().getURL());
            copySource.setCopyContents(newSource.isCopyContents());
            return copySource;
        }
        final SVNCopySource copySource = new SVNCopySource(newSource.getSource().getResolvedPegRevision(), newSource.getRevision(), newSource.getSource().getFile());
        copySource.setCopyContents(newSource.isCopyContents());
        return copySource;
    }

    public static SvnCopySource copySource(SVNCopySource oldSource) {
        SvnTarget target;
        if (oldSource.isURL()) {
            target = SvnTarget.fromURL(oldSource.getURL(), oldSource.getPegRevision());
        } else {
            target = SvnTarget.fromFile(oldSource.getFile(), oldSource.getPegRevision());
        }
        final SvnCopySource copySource = SvnCopySource.create(target, oldSource.getRevision());
        copySource.setCopyContents(oldSource.isCopyContents());
        return copySource;
    }
    
    public static ISvnCommitHandler commitHandler(final ISVNCommitHandler target) {
        if (target == null) {
            return null;
        }
        return new SvnCommitHandlerWithFilter(target);
    }

    public static ISVNCommitHandler commitHandler(final ISvnCommitHandler target) {
        if (target == null) {
            return null;
        }
        return new SVNCommitHandler(target);
    }
    
    private static class SVNCommitHandler implements ISVNCommitHandler, ISVNFileFilter {
        
        private final ISvnCommitHandler targetHandler;
        
        public SVNCommitHandler(ISvnCommitHandler target) {
            targetHandler = target;
        }

        public boolean accept(File file) throws SVNException {
            return targetHandler instanceof ISvnFileFilter ? ((ISvnFileFilter) targetHandler).accept(file) : true;
        }

        public SVNProperties getRevisionProperties(String message, SVNCommitItem[] commitables, SVNProperties revisionProperties) throws SVNException {
            SvnCommitItem[] targetItems = new SvnCommitItem[commitables.length];
            for (int i = 0; i < targetItems.length; i++) {
                targetItems[i] = commitItem(commitables[i]);
            }
            return targetHandler.getRevisionProperties(message, targetItems, revisionProperties);
        }
        
        public String getCommitMessage(String message, SVNCommitItem[] commitables) throws SVNException {
            SvnCommitItem[] targetItems = new SvnCommitItem[commitables.length];
            for (int i = 0; i < targetItems.length; i++) {
                targetItems[i] = commitItem(commitables[i]);
            }
            return targetHandler.getCommitMessage(message, targetItems);
        }   
    }

    private static class SvnCommitHandlerWithFilter implements ISvnCommitHandler, ISvnFileFilter {
        
        private final ISVNCommitHandler targetHandler;
        
        public SvnCommitHandlerWithFilter(ISVNCommitHandler target) {
            targetHandler = target;
        }

        public boolean accept(File file) throws SVNException {
            return targetHandler instanceof ISVNFileFilter ? ((ISVNFileFilter) targetHandler).accept(file) : true;
        }

        public SVNProperties getRevisionProperties(String message, SvnCommitItem[] commitables, SVNProperties revisionProperties) throws SVNException {
            SVNCommitItem[] targetItems = new SVNCommitItem[commitables.length];
            for (int i = 0; i < targetItems.length; i++) {
                targetItems[i] = commitItem(commitables[i]);
            }
            return targetHandler.getRevisionProperties(message, targetItems, revisionProperties);
        }
        
        public String getCommitMessage(String message, SvnCommitItem[] commitables) throws SVNException {
            SVNCommitItem[] targetItems = new SVNCommitItem[commitables.length];
            for (int i = 0; i < targetItems.length; i++) {
                targetItems[i] = commitItem(commitables[i]);
            }
            return targetHandler.getCommitMessage(message, targetItems);
        }
    }
    
    public static ISVNExternalsHandler externalsHandler(final ISvnExternalsHandler target) {
        if (target == null) {
            return ISVNExternalsHandler.DEFAULT;
        }
        return new ISVNExternalsHandler() {
            public SVNRevision[] handleExternal(File externalPath, SVNURL externalURL,
                    SVNRevision externalRevision, SVNRevision externalPegRevision,
                    String externalsDefinition, SVNRevision externalsWorkingRevision) {
                return target.handleExternal(externalPath, externalURL, externalRevision, externalPegRevision, externalsDefinition, externalsWorkingRevision);
            }
        };
    }

    public static ISvnExternalsHandler externalsHandler(final ISVNExternalsHandler target) {
        if (target == null) {
            return new ISvnExternalsHandler() {
                public SVNRevision[] handleExternal(File externalPath, SVNURL externalURL, SVNRevision externalRevision, SVNRevision externalPegRevision, String externalsDefinition, SVNRevision externalsWorkingRevision) {
                    return new SVNRevision[] {externalRevision, externalPegRevision};
                }
            };
        }
        return new ISvnExternalsHandler() {
            public SVNRevision[] handleExternal(File externalPath, SVNURL externalURL,
                    SVNRevision externalRevision, SVNRevision externalPegRevision,
                    String externalsDefinition, SVNRevision externalsWorkingRevision) {
                return target.handleExternal(externalPath, externalURL, externalRevision, externalPegRevision, externalsDefinition, externalsWorkingRevision);
            }
        };
    }
    
    public static ISvnCommitParameters commitParameters(final ISVNCommitParameters old) {
        if (old == null) {
            return null;
        }
        return new ISvnCommitParameters() {

            public Action onMissingFile(File file) {
                ISVNCommitParameters.Action a = old.onMissingFile(file);
                return action(a);
            }

            public Action onMissingDirectory(File file) {
                ISVNCommitParameters.Action a = old.onMissingDirectory(file);
                return action(a);
            }

            private Action action(ISVNCommitParameters.Action a) {
                if (a == ISVNCommitParameters.DELETE) {
                    return Action.DELETE;
                } else if (a == ISVNCommitParameters.ERROR) {
                    return Action.ERROR;
                }
                return Action.SKIP;
            }

            public boolean onDirectoryDeletion(File directory) {
                return old.onDirectoryDeletion(directory);
            }

            public boolean onFileDeletion(File file) {
                return old.onFileDeletion(file);
            }
        };
    }
    
    public static ISVNCommitParameters commitParameters(final ISvnCommitParameters old) {
        if (old == null) {
            return null;
        }
        return new ISVNCommitParameters() {

            public ISVNCommitParameters.Action onMissingFile(File file) {
                ISvnCommitParameters.Action a = old.onMissingFile(file);
                return action(a);
            }

            public Action onMissingDirectory(File file) {
                ISvnCommitParameters.Action a = old.onMissingDirectory(file);
                return action(a);
            }

            private ISVNCommitParameters.Action action(ISvnCommitParameters.Action a) {
                if (a == ISvnCommitParameters.Action.DELETE) {
                    return ISVNCommitParameters.DELETE;
                } else if (a == ISvnCommitParameters.Action.ERROR) {
                    return ISVNCommitParameters.ERROR;
                }
                return ISVNCommitParameters.SKIP;
            }

            public boolean onDirectoryDeletion(File directory) {
                return old.onDirectoryDeletion(directory);
            }

            public boolean onFileDeletion(File file) {
                return old.onFileDeletion(file);
            }
        };
    }
    
    public static ISvnAddParameters addParameters(final ISVNAddParameters old) {
        if (old == null) {
            return null;
        }
        return new ISvnAddParameters() {

            public Action onInconsistentEOLs(File file) {
                ISVNAddParameters.Action a = old.onInconsistentEOLs(file);
                if (a == ISVNAddParameters.ADD_AS_BINARY) {
                    return Action.ADD_AS_BINARY;
                } else if (a == ISVNAddParameters.ADD_AS_IS) {
                    return Action.ADD_AS_IS;
                }
                return Action.REPORT_ERROR;
            }
        };
    }
    
    public static ISVNAddParameters addParameters(final ISvnAddParameters old) {
        if (old == null) {
            return null;
        }
        return new ISVNAddParameters() {
            public Action onInconsistentEOLs(File file) {
                ISvnAddParameters.Action a = old.onInconsistentEOLs(file);
                if (a == ISvnAddParameters.Action.ADD_AS_BINARY) {
                    return ISVNAddParameters.ADD_AS_BINARY;
                } else if (a == ISvnAddParameters.Action.ADD_AS_IS) {
                    return ISVNAddParameters.ADD_AS_IS;
                }
                return ISVNAddParameters.REPORT_ERROR;
            }
        };
    }

    public static ISvnPropertyValueProvider propertyValueProvider(final ISVNPropertyValueProvider propertyValueProvider) {
        if (propertyValueProvider == null) {
            return null;
        }
        return new ISvnPropertyValueProvider() {
            public SVNProperties providePropertyValues(File path, SVNProperties properties) throws SVNException {
                return propertyValueProvider.providePropertyValues(path, properties);
            }
        };
    }

    public static ISVNPropertyValueProvider propertyValueProvider(final ISvnPropertyValueProvider propertyValueProvider) {
        if (propertyValueProvider == null) {
            return null;
        }
        return new ISVNPropertyValueProvider() {
            public SVNProperties providePropertyValues(File path, SVNProperties properties) throws SVNException {
                return propertyValueProvider.providePropertyValues(path, properties);
            }
        };
    }
}
