/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.text;

import net.ranides.assira.io.IOUtils;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UncheckedIOException;
import java.io.Writer;
import java.nio.charset.Charset;

/**
 * This class is alternative to java PrintWriter or PrintStream.
 * It wraps Appendable output.
 *
 *  Configuration options (for example list separators) could be configured independently in every opened scope
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
@SuppressWarnings({
    "PMD.TooManyPublicMethods",
    "PMD.TooManyDifferentMethods"
})
public final class StrAppender extends AbstractAppender<StrAppender> {
    
    private static final long serialVersionUID = 1L;

    private final Appendable target;

    /**
     * Creates new appender storing formatted output in target
     */
    public StrAppender(Appendable target) {
        this.target = target;
    }

    /**
     * Creates new appender storing formatted output in provided stream
     *
     * Uses UTF8 for charset conversion
     */
    public StrAppender(OutputStream target) {
        this.target = new OutputStreamWriter(target, Charsets.UTF8);
    }

    /**
     * Creates new appender storing formatted output in provided stream
     *
     * @param target target
     * @param cs cs
     */
    public StrAppender(OutputStream target, Charset cs) {
        this.target = new OutputStreamWriter(target, cs);
    }

    @Override
    public StrAppender append(CharSequence value) {
        if (value == null) {
            return append();
        }
        try {
            target.append(value);
            return this;
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    @Override
    public StrAppender append(CharSequence value, int begin, int end) {
        if (value == null) {
            return append();
        }
        try {
            target.append(value, begin, end);
            return this;
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    @Override
    public StrAppender append(char[] value) {
        return (value == null) ? append() : append(StringUtils.wrap(value));
    }

    @Override
    public StrAppender append(char[] value, int begin, int end) {
        return (value == null) ? append() : append(StringUtils.wrap(value, begin, end));
    }

    @Override
    public StrAppender append(char c) {
        try {
            target.append(c);
            return this;
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    @Override
    public String toString() {
        return target.toString();
    }

    /**
     * Returns new instance of Writer view.
     * Writer can be used to change buffer and vice versa.
     *
     * @return Writer
     */
    public Writer asWriter() {
        return new SWriter();
    }

    private final class SWriter extends AbstractPrintWriter {
        
        @Override
        public void close() {
            try {
                IOUtils.closeAny(target);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            } catch (RuntimeException e) {
                throw e;
            } catch (Exception e) {
                throw new UncheckedIOException(new IOException(e));
            }
        }
        
        @Override
        public void flush() {
            try {
                IOUtils.flushAny(target);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            } catch (RuntimeException e) {
                throw e;
            } catch (Exception e) {
                throw new UncheckedIOException(new IOException(e));
            }
        }

    }

}
