/*
 * File:     CharacterRenderer.java
 * Project:  MPI Linguistic Application
 * Date:     03 April 2006
 *
 * Copyright (C) 2001-2006  Max Planck Institute for Psycholinguistics
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package mpi.eudico.client.annotator.interlinear;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;

import java.nio.charset.UnsupportedCharsetException;

import java.text.DateFormat;

import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;


/**
 * A class that renders interlinearized content as characters to a File, using
 * UTF-8 encoding.
 *
 * @author Han Sloetjes
 * @version 1.0
 */
public class CharacterRenderer {
    /** new line string */
    private final String NEW_LINE = "\n";

    /** white space string */
    private final String SPACE = " ";
    private Interlinear interlinear;
    private File outFile;

    /** the character encoding for the text file */
    private String charEncoding;

    /**
     * Creates a new CharacterRenderer instance
     *
     * @param interlinear the Interlinear object holding the formatted,
     *        interlinearized content
     * @param outFile the File to write to
     */
    public CharacterRenderer(Interlinear interlinear, File outFile) {
        this(interlinear, outFile, "UTF-8");
    }

    /**
     * Creates a new CharacterRenderer instance
     *
     * @param interlinear the Interlinear object holding the formatted,
     *        interlinearized content
     * @param outFile the File to write to
     * @param charEncoding the character encoding for output
     */
    public CharacterRenderer(Interlinear interlinear, File outFile,
        String charEncoding) {
        this.interlinear = interlinear;
        this.outFile = outFile;
        this.charEncoding = charEncoding;
    }

    /**
     * Renders (writes) the content to a File, adding spaces and new line
     * characters  as needed.
     *
     * @throws IOException any IOException that can occur while writing to a
     *         file
     * @throws FileNotFoundException thrown when the export file could not be
     *         found
     * @throws NullPointerException when the Interlinear object or the export
     *         file  is <code>null</code>
     */
    public void renderText() throws IOException, FileNotFoundException {
        if (interlinear == null) {
            throw new NullPointerException("Interlinear object is null");
        }

        if (outFile == null) {
            throw new NullPointerException("Export file is null");
        }

        // create output stream
        BufferedWriter writer = null;

        try {
            FileOutputStream out = new FileOutputStream(outFile);
            OutputStreamWriter osw = null;

            try {
                osw = new OutputStreamWriter(out, charEncoding);
            } catch (UnsupportedCharsetException uce) {
                osw = new OutputStreamWriter(out, "UTF-8");
            }

            writer = new BufferedWriter(osw);

            // file info
            writer.write(interlinear.getTranscription().getFullPath());
            writer.write(NEW_LINE);

            writer.write(DateFormat.getDateTimeInstance(DateFormat.FULL,
                    DateFormat.SHORT, Locale.getDefault()).format(new Date(
                        System.currentTimeMillis())));

            writer.write(NEW_LINE);
            writer.write(NEW_LINE);

            // annotations
            ArrayList blocks = interlinear.getMetrics().getPrintBlocks();
            InterlinearBlock printBlock = null;
            ArrayList tiers = null;
            InterlinearTier pt = null;

            for (int i = 0; i < blocks.size(); i++) {
                printBlock = (InterlinearBlock) blocks.get(i);
                tiers = printBlock.getPrintTiers();

                for (int j = 0; j < tiers.size(); j++) {
                    pt = (InterlinearTier) tiers.get(j);
                    renderTier(interlinear, pt, writer);
                }

                for (int j = 0; j < interlinear.getBlockSpacing(); j++) {
                    writer.write(NEW_LINE);
                }
            }

            writer.flush();
            writer.close();
        } finally {
            try {
                writer.close();
            } catch (Exception ee) {
            }
        }
    }

    /**
     * Writes the contents of the annotations of a single tier to the file.
     *
     * @param inter the Interlinear object
     * @param pt the tier to write
     * @param writer the buffered writer
     *
     * @throws IOException any ioexception
     */
    private void renderTier(Interlinear inter, InterlinearTier pt, Writer writer)
        throws IOException {
        // tier label
        if (inter.isTierLabelsShown()) {
            if (pt.isTimeCode()) {
                writer.write(interlinear.getMetrics().TC_TIER_NAME);
                padSpaces(writer,
                    inter.getMetrics().getLeftMargin() -
                    interlinear.getMetrics().TC_TIER_NAME.length());
            } else {
                writer.write(pt.getTierName());
                padSpaces(writer,
                    inter.getMetrics().getLeftMargin() -
                    pt.getTierName().length());
            }
        }

        // annotations
        ArrayList annos = pt.getAnnotations();
        InterlinearAnnotation pa = null;
        InterlinearAnnotation prevPa = null;

        for (int i = 0; i < annos.size(); i++) {
            pa = (InterlinearAnnotation) annos.get(i);

            if (pa.nrOfLines == 1) {
                int pad = 0;

                if (prevPa != null) {
                    pad = pa.x - (prevPa.x + prevPa.realWidth);
                } else {
                    pad = pa.x;
                }

                padSpaces(writer, pad);
                writer.write(pa.getValue());
            } else {
                for (int line = 0; line < pa.getLines().length; line++) {
                    if (line == 0) {
                        writer.write(pa.getLines()[line]); //rest of line is empty

                        if (line != (pa.getLines().length - 1)) {
                            writer.write(NEW_LINE);
                        }
                    } else {
                        if (inter.isTierLabelsShown()) {
                            // fill the label margin
                            padSpaces(writer, inter.getMetrics().getLeftMargin());
                        }

                        writer.write(pa.getLines()[line]);

                        if (line != (pa.getLines().length - 1)) {
                            writer.write(NEW_LINE);
                        }
                    }
                }
            }

            prevPa = pa;
        }

        // end with a new line
        writer.write(NEW_LINE);
    }

    /**
     * Writes the specified number of whitespace characters to the file in
     * order to fill up the space to the next annotation
     *
     * @param writer the buffered writer
     * @param numSpaces the number of whitespace characters to write
     *
     * @throws IOException any IOEception
     */
    private void padSpaces(Writer writer, int numSpaces)
        throws IOException {
        if (numSpaces <= 0) {
            return;
        }

        for (int i = 0; i < numSpaces; i++) {
            writer.write(SPACE);
        }
    }
}
