/*
 * @(#)FileConvert.java
 *
 * Copyright (c) 2002-2003 JSCAPE
 * 1147 S. 53rd Pl., Mesa, Arizona, 85206, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JSCAPE. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with JSCAPE.
 */

package com.jscape.file;

import com.jscape.util.IOTools;
import com.jscape.util.LineReader;
import com.jscape.util.Parameters;

import java.io.*;
import java.nio.charset.Charset;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;


/**
 * @author vglass
 */
public class FileConvert {

    private static final String MSDOS = "\r\n";
    private static final String UNIX = "\n";
    private static final String MAC = "\r";

    private String characterEncoding;
    private Set<FileConvertListener> listeners;

    /**
     * Creates a new <code>FileConvert</code> instance.
     *
     * @param characterEncoding the character encoding used in the files to convert
     */
    public FileConvert(String characterEncoding) {
        this.characterEncoding = characterEncoding;
        this.listeners = new CopyOnWriteArraySet<FileConvertListener>();
    }

    public FileConvert() {
        this(null);
    }

    /**
     * Gets character encoding used when converting files.
     *
     * @return character encoding
     */
    public String getCharacterEncoding() {
        return this.characterEncoding;
    }

    /**
     * Sets character encoding used when converting files.
     *
     * @param characterEncoding the character encoding
     */
    public void setCharacterEncoding(String characterEncoding) {
        this.characterEncoding = characterEncoding;
    }

    /**
     * Add listener.
     *
     * @param listener listener
     */
    public void addFileConvertListener(FileConvertListener listener) {
        Parameters.assertNotNull(listener);
        this.listeners.add(listener);
    }

    /**
     * Remove listener.
     *
     * @param listener of type FileConvertListener.
     */
    public void removeFileConvertListener(FileConvertListener listener) {
        this.listeners.remove(listener);
    }

    /**
     * Converts file from MSDOS format to UNIX format.
     *
     * @param input  the input file to convert
     * @param output the output file to save to
     *
     * @throws IOException if an I/O related error occurs
     */
    public void dosToUnix(File input, File output)
            throws IOException {
        convert(input, MSDOS, output, UNIX);
    }

    /**
     * Converts file from MSDOS format to UNIX format.
     *
     * @param input the input file to convert
     *
     * @throws IOException if an I/O related error occurs
     */
    public void dosToUnix(File input)
            throws IOException {
        convert(input, MSDOS, input, UNIX);
    }


    /**
     * Converts file from MSDOS format to Mac format.
     *
     * @param input  the input file to convert
     * @param output the output file to save to
     *
     * @throws IOException if an I/O related error occurs
     */
    public void dosToMac(File input, File output)
            throws IOException {
        convert(input, MSDOS, output, MAC);
    }

    /**
     * Converts file from MSDOS format to Mac format.
     *
     * @param input the input file to convert
     *
     * @throws IOException if an I/O related error occurs
     */
    public void dosToMac(File input)
            throws IOException {
        convert(input, MSDOS, input, MAC);
    }


    /**
     * Converts file from UNIX format to MSDOS format.
     *
     * @param input  the input file to convert
     * @param output the output file to save to
     *
     * @throws IOException if an I/O related error occurs
     */
    public void unixToDos(File input, File output)
            throws IOException {
        convert(input, UNIX, output, MSDOS);
    }

    /**
     * Converts file from UNIX format to MSDOS format.
     *
     * @param input the input file to convert
     *
     * @throws IOException if an I/O related error occurs
     */
    public void unixToDos(File input)
            throws IOException {
        convert(input, UNIX, input, MSDOS);
    }


    /**
     * Converts file from UNIX format to Mac format.
     *
     * @param input  the input file to convert
     * @param output the output file to save to
     *
     * @throws IOException if an I/O related error occurs
     */
    public void unixToMac(File input, File output)
            throws IOException {
        convert(input, UNIX, output, MAC);
    }

    /**
     * Converts file from UNIX format to Mac format.
     *
     * @param input the input file to convert
     *
     * @throws IOException if an I/O related error occurs
     */
    public void unixToMac(File input)
            throws IOException {
        convert(input, UNIX, input, MAC);
    }


    /**
     * Converts file from Mac format to MSDOS format.
     *
     * @param input  the input file to convert
     * @param output the output file to save to
     *
     * @throws IOException if an I/O related error occurs
     */
    public void macToDos(File input, File output)
            throws IOException {
        convert(input, MAC, output, MSDOS);
    }

    /**
     * Converts file from Mac format to MSDOS format.
     *
     * @param input the input file to convert
     *
     * @throws IOException if an I/O related error occurs
     */
    public void macToDos(File input)
            throws IOException {
        convert(input, MAC, input, MSDOS);
    }

    /**
     * Converts file from Mac format to UNIX format.
     *
     * @param input  the input file to convert
     * @param output the output file to save to
     *
     * @throws IOException if an I/O related error occurs
     */
    public void macToUnix(File input, File output)
            throws IOException {
        convert(input, MAC, output, UNIX);
    }

    /**
     * Converts file from Mac format to UNIX format.
     *
     * @param input the input file to convert
     *
     * @throws IOException if an I/O related error occurs
     */
    public void macToUnix(File input)
            throws IOException {
        convert(input, MAC, input, UNIX);
    }

    /**
     * Converts file from one format to another.
     *
     * @param input            the input file to convert
     * @param inputTerminator  the line terminator used for input
     * @param output           the output file to write to
     * @param outputTerminator the line terminator used for output
     *
     * @throws IOException if an I/O related error occurs
     */
    public void convert(File input,
                        String inputTerminator,
                        File output,
                        String outputTerminator)
            throws IOException {
        File zout = null;
        boolean inputEqualsOutput = false;

        // check if input file equals output file
        if (input.equals(output)) {
            inputEqualsOutput = true;
            zout = File.createTempFile(Long.toString(System.currentTimeMillis()), ".tmp");
        } else {
            zout = output;
        }

        // read input file and copy contents to output file
        FileInputStream fin = null;
        FileOutputStream fout = null;

        try {
            fin = new FileInputStream(input);
            fout = new FileOutputStream(zout);
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fout, Charset.defaultCharset()));
            LineReader reader = new LineReader(fin, inputTerminator.toCharArray());
            String line;
            while ((line = reader.readLine()) != null) {
                writer.write(line);
                if (reader.hasDelimiter()) {
                    writer.write(outputTerminator);
                }
            }
            writer.flush();
            writer.close();
            fin.close();
            fout.close();

            // if input file equals output file then move temp file
            if (inputEqualsOutput) {
                FileUtils utils = new FileUtils();
                utils.move(zout, output);
            }

            // send file converted event
            fireFileConverted(input, zout);
        } finally {
            IOTools.close(fin);
            IOTools.close(fout);
        }
    }

    @Override
    public String toString() {
        return "FileConvert {"
                + "characterEncoding='" + this.characterEncoding + '\''
                + ", listeners=" + this.listeners
                + '}';
    }

    /**
     * Fires <code>FileConvertedEvent</code>.
     * This event is fired upon converting a file.
     *
     * @param input  the input file to convert
     * @param output the output file to write to
     *
     * @see FileConvertedEvent
     */
    protected void fireFileConverted(File input, File output) {
        FileConvertedEvent event = new FileConvertedEvent(this, input, output);

        for (FileConvertListener listener : this.listeners) {
            listener.fileConverted(event);
        }
    }

}
