import javax.imageio.*;
import java.awt.image.*;
import java.awt.*;
import java.security.NoSuchAlgorithmException;
import java.security.MessageDigest;
import java.lang.reflect.*;
import java.net.*;
import java.io.*;
import javax.swing.text.*;
import javax.swing.event.*;
import javax.swing.*;
import java.util.concurrent.*;
import java.util.regex.*;
import java.util.List;
import java.util.zip.*;
import java.util.*;
import java.awt.event.*;


public class main {

static class DelayedUpdate {
  Runnable renderer;
  volatile long version;
  long lastUpdate; // AWT time
  int delay = 1000;
  
  DelayedUpdate(Runnable renderer) {
  this.renderer = renderer;}

  void trigger() {
    SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {

      awt_quickUpdate();
    } catch (Exception _e) {
  throw _e instanceof RuntimeException ? (RuntimeException) _e : new RuntimeException(_e); } }
});

    final long n = ++version;
    
    javax.swing.Timer timer = new javax.swing.Timer(delay, new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent _evt) {

      awt_show(n);
    
}});
    timer.setRepeats(false);
    timer.start(); 
  }

  void awt_quickUpdate() {
    if (lastUpdate < now()-delay) {
      render();
      lastUpdate = now(); // This can be rough, no problem
    }
  }

  void awt_show(long n) {
    if (n == version) {
      render();
      lastUpdate = now();
    }
  }
  
  void render() {
    try {
      renderer.run();
    } catch (Throwable e) {
      e.printStackTrace();
    }
  }
} // DelayedUpdate


static class Console extends WindowAdapter implements WindowListener, ActionListener, Runnable {
	private JFrame frame;
	private JTextArea textArea;
	StringBuffer buf = new StringBuffer();
	private Thread reader;
	private Thread reader2;
	private boolean quit;
					
	private final PipedInputStream pin=new PipedInputStream(); 
	private final PipedInputStream pin2=new PipedInputStream(); 
	
	final DelayedUpdate du = new DelayedUpdate(new Runnable() {
public void run() { try {

    textArea.append(buf.substring(textArea.getText().length()));
  
} catch (Exception __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}});

	public Console()
	{
		// create all components and add them
		frame=new JFrame("JavaX Output");
		
		/*Dimension screenSize=Toolkit.getDefaultToolkit().getScreenSize();
		Dimension frameSize=new Dimension((int)(screenSize.width/2),(int)(screenSize.height/2));
		int x=(int)(frameSize.width/2);
		int y=(int)(frameSize.height/2);
		frame.setBounds(x,y,frameSize.width,frameSize.height);*/
		
		// put in right-bottom corner
		Rectangle r = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
		int w = 550, h = 200;
		frame.setBounds(r.x+r.width-w, r.y+r.height-h, w, h);
		
		textArea=new JTextArea();
		textArea.setEditable(false);
		JButton button=new JButton("clear");
		JButton buttonkill=new JButton("kill");
		
		JPanel buttons = new JPanel(new GridLayout(1, 2));
		buttons.add(button);
		buttons.add(buttonkill);
		
		frame.getContentPane().setLayout(new BorderLayout());
		frame.getContentPane().add(new JScrollPane(textArea),BorderLayout.CENTER);
		frame.getContentPane().add(buttons,BorderLayout.SOUTH);
		frame.setVisible(true);		
		
		//frame.addWindowListener(this); // disabled for now
		button.addActionListener(this);
		buttonkill.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent _evt) {

		  print("Console: Kill button pressed!");
		  System.exit(0);
		
}});
		
		try
		{
			PipedOutputStream pout=new PipedOutputStream(this.pin);
			System.setOut(new PrintStream(pout,true)); 
		} 
		catch (java.io.IOException io)
		{
			appendText("Couldn't redirect STDOUT to this console\n"+io.getMessage());
		}
		catch (SecurityException se)
		{
			appendText("Couldn't redirect STDOUT to this console\n"+se.getMessage());
	    } 
		
		try 
		{
			PipedOutputStream pout2=new PipedOutputStream(this.pin2);
			System.setErr(new PrintStream(pout2,true));
		} 
		catch (java.io.IOException io)
		{
			appendText("Couldn't redirect STDERR to this console\n"+io.getMessage());
		}
		catch (SecurityException se)
		{
			appendText("Couldn't redirect STDERR to this console\n"+se.getMessage());
	    } 		
			
		quit=false; // signals the Threads that they should exit
				
		// Starting two seperate threads to read from the PipedInputStreams				
		//
		reader=new Thread(this);
		reader.setDaemon(true);	
		reader.start();	
		//
		reader2=new Thread(this);	
		reader2.setDaemon(true);	
		reader2.start();
	}
	
	public synchronized void windowClosed(WindowEvent evt)
	{
		quit=true;
		this.notifyAll(); // stop all threads
		try { reader.join(1000);pin.close();   } catch (Exception e){}		
		try { reader2.join(1000);pin2.close(); } catch (Exception e){}
		System.exit(0);
	}		
		
	public synchronized void windowClosing(WindowEvent evt)
	{
		frame.setVisible(false); // default behaviour of JFrame	
		frame.dispose();
	}
	
	public synchronized void actionPerformed(ActionEvent evt)
	{
		textArea.setText("");
		buf = new StringBuffer();
	}

	public synchronized void run()
	{
		try
		{			
			while (Thread.currentThread()==reader)
			{
				try { this.wait(100);}catch(InterruptedException ie) {}
				if (pin.available()!=0)
				{
					String input=this.readLine(pin);
					appendText(input);
				}
				if (quit) return;
			}
		
			while (Thread.currentThread()==reader2)
			{
				try { this.wait(100);}catch(InterruptedException ie) {}
				if (pin2.available()!=0)
				{
					String input=this.readLine(pin2);
					appendText(input);
				}
				if (quit) return;
			}			
		} catch (Exception e)
		{
			appendText("\nConsole reports an Internal error.");
			appendText("The error is: "+e);			
		}
	}
	
	public synchronized String readLine(PipedInputStream in) throws IOException
	{
		String input="";
		do
		{
			int available=in.available();
			if (available==0) break;
			byte b[]=new byte[available];
			in.read(b);
			input=input+new String(b,0,b.length);														
		}while( !input.endsWith("\n") &&  !input.endsWith("\r\n") && !quit);
		return input;
	}	
	
	public void appendText(String s) {
	  buf.append(s);
	  /*textArea.append(s);
	  textArea.setCaretPosition(textArea.getText().length()); // Hopefully not too slow...
	  */
	  du.trigger();
	}
} // Console
  
  public static void main(String[] args) throws Exception {
    new Console();
    print("Hello console");
    
    for (int i = 0; i < 100; i++)
      print("Hello " + i);
  }

static long now_virtualTime;
static long now() {
  return now_virtualTime != 0 ? now_virtualTime : System.currentTimeMillis();
}


static void print() {
  System.out.println();
}

static void print(Object o) {
  System.out.println(o);
}

static void print(long i) {
  System.out.println(i);
}
}