/*
    This file is part of PortableFtpServer.

    PortableFtpServer 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 3 of the License, or
    (at your option) any later version.

    PortableFtpServer 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 PortableFtpServer.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.erc.pftps;

import java.awt.Color;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;

/**
 * The Class MessageConsole.
 */
public class MessageConsole {
	
	/** The text component. */
	private JTextComponent textComponent;
	
	/** The document. */
	private Document document;
	
	/** The limit lines listener. */
	private DocumentListener limitLinesListener;

	/**
	 * Instantiates a new message console.
	 *
	 * @param textComponent the text component
	 */
	public MessageConsole(JTextComponent textComponent){
		this.textComponent = textComponent;
		this.document = textComponent.getDocument();
		textComponent.setEditable(false);
	}

	/**
	 * Redirect out.
	 */
	public void redirectOut(){
		System.setOut( new PrintStream(new ConsoleOutputStream(Color.LIGHT_GRAY), true));
	}

	/**
	 * Redirect err.
	 */
	public void redirectErr(){	
		System.setErr( new PrintStream(new ConsoleOutputStream(Color.red), true));
		
	}

	/**
	 * Sets the message lines.
	 *
	 * @param lines the new message lines
	 */
	public void setMessageLines(int lines) {
		if (limitLinesListener != null){
			document.removeDocumentListener( limitLinesListener );
		}
		limitLinesListener = new LimitLinesDocumentListener(lines);
		document.addDocumentListener( limitLinesListener );
	}

	/**
	 * The listener interface for receiving limitLinesDocument events.
	 * The class that is interested in processing a limitLinesDocument
	 * event implements this interface, and the object created
	 * with that class is registered with a component using the
	 * component's <code>addLimitLinesDocumentListener<code> method. When
	 * the limitLinesDocument event occurs, that object's appropriate
	 * method is invoked.
	 *
	 * @see LimitLinesDocumentEvent
	 */
	class LimitLinesDocumentListener implements DocumentListener {
		
		/** The maximum lines. */
		private int maximumLines;

		/**
		 * Instantiates a new limit lines document listener.
		 *
		 * @param maximumLines the maximum lines
		 */
		public LimitLinesDocumentListener(int maximumLines) {
			this.maximumLines = maximumLines;
		}

		/* (non-Javadoc)
		 * @see javax.swing.event.DocumentListener#insertUpdate(javax.swing.event.DocumentEvent)
		 */
		public void insertUpdate(final DocumentEvent e) {
			SwingUtilities.invokeLater( new Runnable() { public void run() { removeLines(e); } });
		}

		/* (non-Javadoc)
		 * @see javax.swing.event.DocumentListener#removeUpdate(javax.swing.event.DocumentEvent)
		 */
		public void removeUpdate(DocumentEvent e) {}
		
		/* (non-Javadoc)
		 * @see javax.swing.event.DocumentListener#changedUpdate(javax.swing.event.DocumentEvent)
		 */
		public void changedUpdate(DocumentEvent e) {}

		/**
		 * Removes the lines.
		 *
		 * @param e the e
		 */
		private void removeLines(DocumentEvent e) {
			Document document = e.getDocument();
			Element root = document.getDefaultRootElement();
			while (root.getElementCount() > maximumLines) {
				Element line = root.getElement(0);
				int end = line.getEndOffset();
				try {
					document.remove(0, end);
				} catch(BadLocationException ble) {
					//System.out.println(ble);
				}
			}
		}
	}
	
	/**
	 * The Class ConsoleOutputStream.
	 */
	class ConsoleOutputStream extends ByteArrayOutputStream {

		/** The eol. */
		private final String EOL = System.getProperty("line.separator");
		
		/** The attributes. */
		private SimpleAttributeSet attributes;
		
		/** The buffer. */
		private StringBuffer buffer = new StringBuffer(80);
		
		/** The is first line. */
		private boolean isFirstLine;

		/**
		 * Instantiates a new console output stream.
		 *
		 * @param textColor the text color
		 * @param printStream the print stream
		 */
		public ConsoleOutputStream(Color textColor) {
			if (textColor != null) {
				attributes = new SimpleAttributeSet();
				StyleConstants.setForeground(attributes, textColor);
			}
			isFirstLine = true;
		}

		/* (non-Javadoc)
		 * @see java.io.OutputStream#flush()
		 */
		public void flush() {
			String message = toString();
			if (message.length() == 0) return;
			handleAppend(message);
			reset();
		}

		/**
		 * Handle append.
		 *
		 * @param message the message
		 */
		private void handleAppend(String message){
			if (document.getLength() == 0){
				buffer.setLength(0);
			}
			if (EOL.equals(message)){
				buffer.append(message);
			} else {
				buffer.append(message);
				clearBuffer();
			}
		}

		/**
		 * Clear buffer.
		 */
		private void clearBuffer() {
			if (isFirstLine && document.getLength() != 0) {
			    buffer.insert(0, "\n");
			}
			isFirstLine = false;
			String line = buffer.toString();
			try {
				int offset = document.getLength();
				document.insertString(offset, line, attributes);
				textComponent.setCaretPosition( document.getLength() );
			}catch (BadLocationException ble) {}
			buffer.setLength(0);
		}
	}
}