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

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import lombok.experimental.UtilityClass;
import net.ranides.assira.collection.arrays.ArrayAllocator;

/**
 * Utility methods for quick read/write text into files and streams.
 *
 * @author ranides
 */
@UtilityClass
public final class IOStrings {

    /**
     * Opens provided file for reading, returns Reader using provided charset
     *
     * @param path path
     * @param cs cs
     * @return Reader
     * @throws IOException on error
     */
    public static Reader reader(Path path, Charset cs) throws IOException {
        return reader(path.toFile(), cs);
    }

    /**
     * Opens provided file for reading, returns Reader using provided charset
     *
     * @param file file
     * @param cs cs
     * @return Reader
     * @throws IOException on error
     */
    public static Reader reader(File file, Charset cs) throws IOException {
        return new InputStreamReader(new FileInputStream(file), cs);
    }

    /**
     * Opens provided file for write, returns Writer using provided charset
     *
     * @param path path
     * @param cs cs
     * @return Writer
     * @throws IOException on error
     */
    public static Writer writer(Path path, Charset cs) throws IOException {
        return writer(path.toFile(), cs);
    }

    /**
     * Opens provided file for write, returns Writer using provided charset
     *
     * @param file file
     * @param cs cs
     * @return Writer
     * @throws IOException on error
     */
    public static Writer writer(File file, Charset cs) throws IOException {
        return new OutputStreamWriter(new FileOutputStream(file), cs);
    }

    /**
     * Writes down content to writer and closes it.
     *
     * @param writer writer
     * @param content content
     * @throws IOException on error
     */
	public static void write(Writer writer, String content) throws IOException {
		try {
			writer.write(content);
		} finally {
			writer.close();
		}
	}

    /**
     * Writes down content to stream and closes it.
     *
     * @param ostream ostream
     * @param content content
     * @param charset charset
     * @throws IOException on error
     */
	public static void write(OutputStream ostream, String content, Charset charset) throws IOException {
		try {	
			write(new OutputStreamWriter(ostream, charset), content);
		} finally {
			ostream.close();
		}
	}

    /**
     * Writes down content to file.
     *
     * @param path path
     * @param content content
     * @param charset charset
     * @throws IOException on error
     */
	public static void write(Path path, String content, Charset charset) throws IOException {
        write(path.toFile(), content, charset);
    }

    /**
     * Writes down content to file.
     *
     * @param file file
     * @param content content
     * @param charset charset
     * @throws IOException on error
     */
    public static void write(File file, String content, Charset charset) throws IOException {
		write(new FileOutputStream(file), content, charset);
	}

    /**
     * Reads all content from reader and closes it
     *
     * @param reader reader
     * @return String
     * @throws IOException on error
     */
    public static String read(Reader reader) throws IOException {
        try {
            char[] buffer = new char[32];
            int r, c = 0;
            while((r = reader.read(buffer, c, buffer.length-c)) > 0) {
                c += r;
                buffer = ArrayAllocator.grow(buffer, c+32);
            }
            return new String(buffer, 0, c);
        } finally {
            reader.close();
        }
    }

    /**
     * Reads all content from stream and closes it
     *
     * @param istream istream
     * @param charset charset
     * @return String
     * @throws IOException on error
     */
    public static String read(InputStream istream, Charset charset) throws IOException {
        try {
            return IOStrings.read(new InputStreamReader(istream, charset));
        } finally {
            istream.close();
        }
    }

    /**
     * Reads all content from file
     *
     * @param path path
     * @param charset charset
     * @return String
     * @throws IOException on error
     */
    public static String read(Path path, Charset charset) throws IOException {
        return read(path.toFile(), charset);
    }

    /**
     * Reads all content from file
     *
     * @param file file
     * @param charset charset
     * @return String
     * @throws IOException on error
     */
    public static String read(File file, Charset charset) throws IOException {
        return read( new FileInputStream(file), charset );
    }

    /**
     * Reads all lines from reader and closes it.
     * Every newline format is supported.
     *
     * @param ireader ireader
     * @return lines
     * @throws IOException on error
     */
    public static List<String> readLines(Reader ireader) throws IOException {
        ArrayList<String> result = new ArrayList<>();
        try (BufferedReader reader = new BufferedReader(ireader)) {
            String line;
            while( null != (line=reader.readLine())) {
                result.add(line);
            }
        }
        result.trimToSize();
        return result;
    }

    /**
     * Reads all lines from stream and closes it.
     * Every newline format is supported.
     *
     * @param istream istream
     * @param charset charset
     * @return lines
     * @throws IOException on error
     */
    public static List<String> readLines(InputStream istream, Charset charset) throws IOException {
        return readLines(new BufferedReader(new InputStreamReader(istream, charset)));
    }

    /**
     * Reads all lines from file.
     * Every newline format is supported.
     *
     * @param path path
     * @param charset charset
     * @return lines
     * @throws IOException on error
     */
    public static List<String> readLines(Path path, Charset charset) throws IOException {
        return Files.readAllLines(path, charset);
    }

    /**
     * Reads all lines from file.
     * Every newline format is supported.
     *
     * @param file file
     * @param charset charset
     * @return lines
     * @throws IOException on error
     */
    public static List<String> readLines(File file, Charset charset) throws IOException {
        return readLines(new FileInputStream(file), charset);
    }

    /**
     * Writes all lines into writer and closes it.
     * Uses system dependent line separator.
     *
     * @param iwriter iwriter
     * @param content content
     * @throws IOException on error
     */
    public static void writeLines(Writer iwriter, Iterator<String> content) throws IOException {
        try(BufferedWriter writer = new BufferedWriter(iwriter)) {
            while(content.hasNext()) {
                writer.append(content.next());
                writer.newLine();
            }
        }
    }

    /**
     * Writes all lines into stream and closes it.
     * Uses system dependent line separator.
     *
     * @param ostream ostream
     * @param charset charset
     * @param content content
     * @throws IOException on error
     */
    public static void writeLines(OutputStream ostream, Charset charset, Iterator<String> content) throws IOException {
        writeLines(new OutputStreamWriter(ostream, charset), content);
    }

    /**
     * Writes all lines into file.
     * Uses system dependent line separator.
     *
     * @param path path
     * @param charset charset
     * @param content content
     * @throws IOException on error
     */
    public static void writeLines(Path path, Charset charset, Iterator<String> content) throws IOException {
        writeLines(path.toFile(), charset, content);
    }

    /**
     * Writes all lines into file.
     * Uses system dependent line separator.
     *
     * @param file file
     * @param charset charset
     * @param content content
     * @throws IOException on error
     */
    public static void writeLines(File file, Charset charset, Iterator<String> content) throws IOException {
        writeLines(new FileOutputStream(file), charset, content);
    }

    /**
     * Writes all lines into writer and closes it.
     * Uses system dependent line separator.
     *
     * @param iwriter iwriter
     * @param content content
     * @throws IOException on error
     */
    public static void writeLines(Writer iwriter, Iterable<String> content) throws IOException {
        writeLines(iwriter, content.iterator());
    }

    /**
     * Writes all lines into stream and closes it.
     * Uses system dependent line separator.
     *
     * @param ostream ostream
     * @param charset charset
     * @param content content
     * @throws IOException on error
     */
    public static void writeLines(OutputStream ostream, Charset charset, Iterable<String> content) throws IOException {
        writeLines(ostream, charset, content.iterator());
    }

    /**
     * Writes all lines into file.
     * Uses system dependent line separator.
     *
     * @param path path
     * @param charset charset
     * @param content content
     * @throws IOException on error
     */
    public static void writeLines(Path path, Charset charset, Iterable<String> content) throws IOException {
        writeLines(path.toFile(), charset, content);
    }

    /**
     * Writes all lines into file.
     * Uses system dependent line separator.
     *
     * @param file file
     * @param charset charset
     * @param content content
     * @throws IOException on error
     */
    public static void writeLines(File file, Charset charset, Iterable<String> content) throws IOException {
        writeLines(file, charset, content.iterator());
    }
    
}
