/*
 * ====================================================================
 * 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.IOException;
import java.io.OutputStream;

import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNRevisionProperty;
import org.tmatesoft.svn.core.internal.io.fs.CountingOutputStream;
import org.tmatesoft.svn.core.internal.io.fs.FSFS;
import org.tmatesoft.svn.core.internal.io.fs.FSRevisionRoot;
import org.tmatesoft.svn.core.internal.io.fs.FSRoot;
import org.tmatesoft.svn.core.internal.io.fs.FSTransactionRoot;
import org.tmatesoft.svn.core.wc.DefaultSVNDiffGenerator;
import org.tmatesoft.svn.core.wc.admin.ISVNGNUDiffGenerator;
import org.tmatesoft.svn.util.SVNLogType;


/**
 * @version 1.3
 * @author  TMate Software Ltd.
 */
public class DefaultSVNGNUDiffGenerator extends DefaultSVNDiffGenerator implements ISVNGNUDiffGenerator {

    private String myHeader;
    private boolean myIsHeaderWritten;
    private FSRoot myOriginalRoot;
    private String myOriginalPath;
    private FSRoot myNewRoot;
    private String myNewPath;
    private boolean myIsDiffWritten;

    public void displayHeader(int type, String path, String copyFromPath, long copyFromRevision, OutputStream result) throws SVNException {
        switch (type) {
            case ADDED:
                if (!myIsHeaderWritten) {
                    path = path.startsWith("/") ? path.substring(1) : path;
                    myHeader = "Added: " + path;
                }
                break;
            case DELETED:
                if (!myIsHeaderWritten) {
                    path = path.startsWith("/") ? path.substring(1) : path;
                    myHeader = "Deleted: " + path;
                }
                break;
            case MODIFIED:
                if (!myIsHeaderWritten) {
                    path = path.startsWith("/") ? path.substring(1) : path;
                    myHeader = "Modified: " + path;
                }
                break;
            case COPIED:
                if (!myIsHeaderWritten) {
                    path = path.startsWith("/") ? path.substring(1) : path;
                    copyFromPath = copyFromPath.startsWith("/") ? copyFromPath.substring(1) : copyFromPath;
                    myHeader = "Copied: " + path + " (from rev " + copyFromRevision + ", " + copyFromPath + ")";
                }
                break;
            case NO_DIFF:
                try {
                    result.write(getEOL());
                } catch (IOException e) {
                    SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e.getLocalizedMessage());
                    SVNErrorManager.error(err, e, SVNLogType.DEFAULT);
                }
                break;
        }
    }

    public void displayFileDiff(String path, File file1, File file2,
            String rev1, String rev2, String mimeType1, String mimeType2, OutputStream result) throws SVNException {
        CountingOutputStream counitngStream = new CountingOutputStream(result, 0);
        super.displayFileDiff(path, file1, file2, rev1, rev2, mimeType1, mimeType2, counitngStream);
        if (counitngStream.getPosition() > 0) {
            try {
                result.write(getEOL());
            } catch (IOException e) {
                SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e.getLocalizedMessage());
                SVNErrorManager.error(err, e, SVNLogType.DEFAULT);
            }
        }
        setDiffWritten(counitngStream.getPosition() > 0);
    }

    public void setHeaderWritten(boolean written) {
        myIsHeaderWritten = written;
    }
    
    protected boolean displayHeader(OutputStream os, String path, boolean deleted) throws IOException {
        if (myHeader != null) {
            os.write(myHeader.getBytes(getEncoding())); 
            os.write(getEOL());
            myHeader = null;
            myIsHeaderWritten = true;
        } else if (!myIsHeaderWritten) {
            path = path.startsWith("/") ? path.substring(1) : path;
            String header = "Index: " + path;
            os.write(header.getBytes(getEncoding())); 
            os.write(getEOL());
            myIsHeaderWritten = true;
        }
        os.write(HEADER_SEPARATOR.getBytes(getEncoding()));
        os.write(getEOL());
        return false;
    }
    
    protected void displayBinary(OutputStream os, String mimeType1, String mimeType2) throws IOException {
        os.write("(Binary files differ)".getBytes(getEncoding()));
        os.write(getEOL());
    }

    protected void displayHeaderFields(OutputStream os, String label1, String label2) throws IOException {
        os.write("--- ".getBytes(getEncoding()));
        String originalLabel = null;
        String newLabel = null;
        try {
            originalLabel = generateLabel(myOriginalRoot, myOriginalPath); 
            newLabel = generateLabel(myNewRoot, myNewPath);
        } catch (SVNException svne) {
            throw new IOException(svne.getLocalizedMessage());
        }
        os.write(originalLabel.getBytes(getEncoding()));
        os.write(getEOL());
        os.write("+++ ".getBytes(getEncoding()));
        os.write(newLabel.getBytes(getEncoding()));
        os.write(getEOL());
    }

    protected void setOriginalFile(FSRoot originalRoot, String originalPath) {
        myOriginalRoot = originalRoot;
        myOriginalPath = originalPath;
    }

    protected void setNewFile(FSRoot newRoot, String newPath) {
        myNewRoot = newRoot;
        myNewPath = newPath;
    }

    private String generateLabel(FSRoot root, String path) throws SVNException {
        String date = null;
        String txnName = null;
        long rev = 0;
        if (root != null) {
            FSFS fsfs = root.getOwner();
            SVNProperties props = null;
            if (root instanceof FSRevisionRoot) {
                FSRevisionRoot revisionRoot = (FSRevisionRoot) root;
                rev = revisionRoot.getRevision();
                props = fsfs.getRevisionProperties(rev);
            } else {
                FSTransactionRoot txnRoot = (FSTransactionRoot) root;
                txnName = txnRoot.getTxnID();
                props = fsfs.getTransactionProperties(txnName);
            }
            date = props.getStringValue(SVNRevisionProperty.DATE);
        } 
        
        String dateString = null;
        if (date != null) {
            int tInd = date.indexOf('T');
            dateString = date.substring(0, tInd) + " " + date.substring(tInd + 1, tInd + 9) + " UTC";
            
        } else {
            dateString = "                       ";
        }
        
        if (txnName != null) {
            return path + '\t' + dateString + " (txn " + txnName + ")";
        }
        return path + '\t' + dateString + " (rev " + rev + ")";
    }
    
    protected boolean useLocalFileSeparatorChar() {
        return false;
    }

    public void setDiffWritten(boolean b) {
        myIsDiffWritten = b;
    }
    
    public boolean isDiffWritten() {
        return myIsDiffWritten;
    }
    
    public void printHeader(OutputStream os) throws SVNException {
        if (myHeader != null) {
            try {
                displayHeader(os, null, false);
            } catch (IOException e) {
                SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e.getLocalizedMessage());
                SVNErrorManager.error(err, e, SVNLogType.DEFAULT);
            }
        }
    }

}
