/**
 * Copyright 2000-2011 NeuStar, Inc. All rights reserved.
 * NeuStar, the Neustar logo and related names and logos are registered
 * trademarks, service marks or tradenames of NeuStar, Inc. All other
 * product names, company names, marks, logos and symbols may be trademarks
 * of their respective owners.
 */

package com.googlecode.protobuf.format;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;

import com.google.protobuf.ExtensionRegistry;
import com.google.protobuf.Message;
import com.google.protobuf.UnknownFieldSet;
import com.googlecode.protobuf.format.JavaPropsFormat.ParseException;

public abstract class ProtobufFormatter {
    private Charset defaultCharset = Charset.defaultCharset();

    /**
     * Set the default character set to use for input / output data streams
     * @param cs the character set to use by default, when unspecified
     */
    public void setDefaultCharset(Charset cs) {
        defaultCharset = cs;
    }
    
    /**
     * Get the default character set to use for input / output streams
     * @return the character set to use by default, when unspecified
     */
    public Charset getDefaultCharset() {
        return defaultCharset;
    }
	
	/**
	 * @see print(Message, OutputStream, Charset)
	 * @param message the protobuf message to format
	 * @param output the stream to write the formatted message using the default charset
	 * @throws IOException
	 */
	public void print(final Message message, OutputStream output) throws IOException {
		print(message, output, defaultCharset);
	}
	
	/**
	 * Outputs a textual representation of the Protocol Message supplied into
	 * the parameter output. (This representation is the new version of the
	 * classic "ProtocolPrinter" output from the original Protocol Buffer system)
	 * 
	 * @param message the protobuf message to format
	 * @param output the stream to write the formatted message
	 * @param cs the character set to use
	 * @throws IOException
	 */
	abstract public void print(final Message message, OutputStream output, Charset cs) throws IOException;

	
	/**
	 * @see print(UnknownFieldSet, OutputStream, Charset)
	 * @param fields unknown fields to format
	 * @param output output the stream to write the formatted message using the default charset
	 * @throws IOException
	 */
	public void print(final UnknownFieldSet fields, OutputStream output) throws IOException {
		print(fields, output, defaultCharset);
	}

	/**
	 * @param fields unknown fields to format
	 * @param output output the stream to write the formatted message
	 * @param cs the character set to use
	 * @throws IOException
	 */
	abstract public void print(final UnknownFieldSet fields, OutputStream output, Charset cs) throws IOException;
	

	/**
     * Like {@code print()}, but writes directly to a {@code String} and returns it.
     */
	public String printToString(final Message message) {
		try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            print(message, out, defaultCharset);
            out.flush();
            return out.toString();
        } catch (IOException e) {
            throw new RuntimeException("Writing to a StringBuilder threw an IOException (should never happen).",
                                       e);
        }
	}
	
	/**
     * Like {@code print()}, but writes directly to a {@code String} and returns it.
     */
	public String printToString(final UnknownFieldSet fields) {
		try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            print(fields, out, defaultCharset);
            out.flush();
            return out.toString();
        } catch (IOException e) {
            throw new RuntimeException("Writing to a StringBuilder threw an IOException (should never happen).",
                                       e);
        }
	}
	
	/**
     * Thrown when parsing an invalid text format message.
     */
    public static class ParseException extends IOException {
    	private static final long serialVersionUID = 1L;

		public ParseException(String message) {
            super(message);
        }
    }
	
	
	/**
	 * Parse a text-format message from {@code input} and merge the contents
	 * into {@code builder}.
	 */
	abstract public void merge(final InputStream input, Charset cs,
			ExtensionRegistry extensionRegistry, 
			final Message.Builder builder) throws IOException;

	
	/**
	 * Parse a text-format message from {@code input} and merge the contents
	 * into {@code builder}.
	 */
	public void merge(final InputStream input, Charset cs, 
			final Message.Builder builder) throws IOException {
		
		merge(input, cs, ExtensionRegistry.getEmptyRegistry(), builder);
	}
	
	public void merge(final InputStream input, 
			final Message.Builder builder) throws IOException {
		
		merge(input, defaultCharset, 
				ExtensionRegistry.getEmptyRegistry(), builder);
	}
	
	public void merge(final InputStream input,
			ExtensionRegistry extensionRegistry, 
			final Message.Builder builder) throws IOException {
		merge(input, defaultCharset, extensionRegistry, builder);
	}
}
