/*
 * Copyright 2013 Yuichiro Moriguchi
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.morilib.awk.io;

import java.io.BufferedOutputStream;
import java.io.Closeable;
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.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

import net.morilib.awk.misc.System2;

public class AwkFiles {

	private static final String PIPE_ID = "\u0007\u0002";

	private Map<String, AwkReader> readers;
	private Map<String, PrintWriter> writers;
	private AwkReader stdin;
	private PrintWriter stdout, stderr;

	/**
	 * 
	 */
	public AwkFiles() {
		this(System.in, System.out, System.err);
	}

	/**
	 * 
	 * @param stdin
	 * @param stdout
	 * @param stderr
	 */
	public AwkFiles(Reader stdin, Writer stdout, Writer stderr) {
		readers     = new HashMap<String, AwkReader>();
		writers     = new HashMap<String, PrintWriter>();
		this.stdin  = new AwkReader(stdin);
		this.stdout = new PrintWriter(stdout);
		this.stderr = new PrintWriter(stderr);
	}

	/**
	 * 
	 * @param stdin
	 * @param stdout
	 * @param stderr
	 */
	public AwkFiles(InputStream stdin, OutputStream stdout,
			OutputStream stderr) {
		this(new InputStreamReader(stdin),
				new OutputStreamWriter(stdout),
				new OutputStreamWriter(stderr));
	}

	/**
	 * 
	 * @param filename
	 * @return
	 * @throws IOException
	 */
	public AwkReader getReader(String filename) throws IOException {
		AwkReader r;

		if(filename == null) {
			r = stdin;
		} else if((r = readers.get(filename)) == null) {
			r = new AwkReader(new InputStreamReader(
					new FileInputStream(filename)));
			readers.put(filename, r);
		}
		return r;
	}

	/**
	 * 
	 * @param filename
	 * @return
	 * @throws IOException
	 */
	public AwkReader getReadPipe(String command) throws IOException {
		BufferedOutputStream o;
		AwkReader r;
		File t;

		if((r = readers.get(PIPE_ID + command)) == null) {
			t = File.createTempFile("awkium", "pipe");
			t.deleteOnExit();
			o = new BufferedOutputStream(new FileOutputStream(t));
			System2.system(command, null, o);
			o.close();
			r = new AwkReader(new InputStreamReader(
					new FileInputStream(t)));
			readers.put(PIPE_ID + command, r);
		}
		return r;
	}

	/**
	 * 
	 * @param filename
	 * @return
	 * @throws IOException
	 */
	public PrintWriter openWriter(String filename) throws IOException {
		PrintWriter r;

		if(filename == null) {
			r = stdout;
		} else {
			if((r = writers.get(filename)) != null)  r.close();
			r = new PrintWriter(new OutputStreamWriter(
					new FileOutputStream(filename)));
			writers.put(filename, r);
		}
		return r;
	}

	/**
	 * 
	 * @param filename
	 * @return
	 * @throws IOException
	 */
	public PrintWriter getWriter(String filename) throws IOException {
		PrintWriter r;

		if(filename == null) {
			r = stdout;
		} else if((r = writers.get(filename)) == null) {
			r = new PrintWriter(new OutputStreamWriter(
					new FileOutputStream(filename)));
			writers.put(filename, r);
		}
		return r;
	}

	/**
	 * 
	 * @return
	 */
	public PrintWriter getStdout() {
		return stdout;
	}

	/**
	 * 
	 * @return
	 */
	public PrintWriter getStderr() {
		return stderr;
	}

	/**
	 * 
	 * @param filename
	 * @throws IOException
	 */
	public void close(String filename) throws IOException {
		Closeable c;

		if((c = readers.get(filename)) != null) {
			c.close();
		} else if((c = writers.get(filename)) != null) {
			c.close();
		}
	}

	//
	private static void closeq(Closeable c) {
		try {
			if(c != null)  c.close();
		} catch(IOException e) {
			// ignore
		}
	}

	/**
	 * 
	 */
	public void closeAll() {
		for(Closeable r : readers.values())  closeq(r);
		for(Closeable r : writers.values())  closeq(r);
	}

}
