/*
 * ====================================================================
 * 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.io.diff;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;


/**
 * The <b>SVNDeltaProcessor</b> is used to get a full text of a file 
 * in series applying diff windows being passed to a processor.  
 * 
 * @version 1.3
 * @author  TMate Software Ltd.
 * @since   1.2
 */
public class SVNDeltaProcessor {
    
    private SVNDiffWindowApplyBaton myApplyBaton;

    /**
     * Creates a processor. 
     */
    public SVNDeltaProcessor() {
    }
    
    /**
     * Starts processing deltas given a base file stream and an output stream 
     * to write resultant target bytes to.
     * 
     * <p>
     * If a target full text is a newly added file (text deltas would be vs. empty), 
     * then source bytes are not needed and <code>base</code> may be passed as 
     * <span class="javakeyword">null</span>.
     * 
     * <p>
     * If <code>computeChecksum</code> is <span class="javakeyword">true</span>, then 
     * an MD5 checksum will be calculated for target bytes. The calculated checksum is 
     * returned by {@link #textDeltaEnd()}.
     * 
     * @param base             an input stream to take base file contents 
     *                         from
     * @param target           an output stream to write the resultant target 
     *                         contents to
     * @param computeCheksum   <span class="javakeyword">true</span> to calculate
     *                         checksum
     */
    public void applyTextDelta(InputStream base, OutputStream target, boolean computeCheksum) {
        reset();
        MessageDigest digest = null;
        try {
            digest = computeCheksum ? MessageDigest.getInstance("MD5") : null;
        } catch (NoSuchAlgorithmException e1) {
        }
        base = base == null ? SVNFileUtil.DUMMY_IN : base;
        myApplyBaton = SVNDiffWindowApplyBaton.create(base, target, digest);
    }
    
    /**
     * Starts processing deltas given a base file and a one 
     * to write resultant target bytes to.
     * 
     * <p>
     * If a target full text is a newly added file (text deltas would be vs. empty), 
     * then source bytes are not needed and <code>baseFile</code> may be passed as 
     * <span class="javakeyword">null</span>.
     * 
     * <p>
     * If a file represented by <code>targetFile</code> does not exist 
     * yet, first tries to create an empty file.
     * 
     * <p>
     * If <code>computeChecksum</code> is <span class="javakeyword">true</span>, then 
     * an MD5 checksum will be calculated for target bytes. The calculated checksum is 
     * returned by {@link #textDeltaEnd()}. 
     * 
     * @param  baseFile          a base file to read base file contents 
     *                           from
     * @param  targetFile        a destination file where resultant 
     *                           target bytes will be written
     * @param  computeCheksum    <span class="javakeyword">true</span> to calculate
     *                           checksum
     * @throws SVNException
     */
    public void applyTextDelta(File baseFile, File targetFile, boolean computeCheksum) throws SVNException {
        if (!targetFile.exists()) {
            SVNFileUtil.createEmptyFile(targetFile);
        }
        InputStream base = baseFile != null && baseFile.exists() ? SVNFileUtil.openFileForReading(baseFile) : SVNFileUtil.DUMMY_IN;
        applyTextDelta(base, SVNFileUtil.openFileForWriting(targetFile), computeCheksum);
    }

    /**
     * Starts processing deltas given a base file and a one 
     * to write resultant target bytes to.
     * 
     * <p>
     * If a target full text is a newly added file (text deltas would be vs. empty), 
     * then source bytes are not needed and <code>baseIS</code> may be passed as 
     * <span class="javakeyword">null</span>.
     * 
     * <p>
     * If a file represented by <code>targetFile</code> does not exist 
     * yet, first tries to create an empty file.
     * 
     * <p>
     * If <code>computeTargetChecksum</code> is <span class="javakeyword">true</span>, then 
     * an MD5 checksum will be calculated for target bytes. The calculated checksum is 
     * returned by {@link #textDeltaEnd()}. 
     * 
     * @param  baseIS                an input stream to take base file contents 
     *                               from 
     * @param  targetFile            a destination file where resultant 
     *                               target bytes will be written
     * @param  computeTargetCheksum  <span class="javakeyword">true</span> to calculate
     *                               checksum of the target text
     * @throws SVNException
     * @since  1.3
     */
    public void applyTextDelta(InputStream baseIS, File targetFile, boolean computeTargetCheksum) throws SVNException {
        if (!targetFile.exists()) {
            SVNFileUtil.createEmptyFile(targetFile);
        }
        applyTextDelta(baseIS, SVNFileUtil.openFileForWriting(targetFile), computeTargetCheksum);
    }

    /**
     * Receives a next diff window to be applied. The return value is a 
     * dummy stream (left for backward compatibility) since new data should 
     * come within a diff window.
     * 
     * @param   window           a diff window
     * @return                   a dummy output stream
     * @throws  SVNException
     */
    public OutputStream textDeltaChunk(SVNDiffWindow window) throws SVNException {
        window.apply(myApplyBaton);
        return SVNFileUtil.DUMMY_OUT;
    }
    
    private void reset() {
        if (myApplyBaton != null) {
            myApplyBaton.close();
            myApplyBaton = null;
        }
    }
    
    /**
     * Performs delta processing finalizing steps. Applies the last 
     * window left (if any) and finalizes checksum calculation (if a 
     * checksum was required).  
     *  
     * @return  a string representing a hex form of the calculated
     *          MD5 checksum or <span class="javakeyword">null</span> 
     *          if checksum calculation was not required 
     */
    public String textDeltaEnd() {
        try {
            return myApplyBaton.close();
        } finally { 
            reset();
        }
    }
}
