import javax.swing.*;
import java.awt.event.*;
import java.awt.*;


public class Console {

    // a Panel in which we draw text

    class TextPanel extends JPanel {

        private Font font;
        private int nCols, nRows, fontWidth, fontHeight, fontAscent;
        
        private int cursorX, cursorY;
        private StringBuffer [] buffer;

        // Constructor

        TextPanel( int nCols, int nRows, Font font, int fontWidth, int fontHeight, int fontAscent ) {
            
            super();

            this.nCols = nCols;
            this.nRows = nRows;
            this.font = font;
            this.fontWidth = fontWidth;
            this.fontHeight = fontHeight;
            this.fontAscent = fontAscent;

            setPreferredSize(new Dimension( nCols*fontWidth+2, nRows*fontHeight+1 ));
            setBackground(Color.black);
            setForeground(Color.white);
            buffer = new StringBuffer[nRows];
            for (int i=0; i<nRows; i++) {
                buffer[i] = new StringBuffer(nCols);
                for (int j=0; j<nCols; j++)
                    buffer[i].append( " " );
            }
            cursorX = 0;
            cursorY = 0;
        }

        // Set the cursor position.  Assumes that the upper-left corner is row 1, column 1.

        public void setCursor( int row, int col ) {
            
            cursorX = col-1;
            cursorY = row-1;
        }

        // Print a character or string at the current cursor position

        public void print( char c ) {
            
            buffer[cursorY].setCharAt(cursorX,c);
            cursorX = cursorX + 1;
            if (cursorX >= nCols)
                cursorX = nCols;
        }

        public void print( String s ) {
            
            for (int i=0; i<s.length(); i++)
                print( s.charAt(i) );
        }

        // Redraw the whole screen from the stored image

        protected void paintComponent(Graphics g) {

            super.paintComponent(g);

            g.setFont( font );

            for (int i=0; i<25; i++)
                g.drawString( buffer[i].toString(), 1, fontHeight*i+fontAscent );
        }
    }

    
    class KeyLogger extends KeyAdapter {

        final int logSize = 5;
        char [] log;
        int head, next;
        boolean isFull, isEmpty;

        KeyLogger() {
            log = new char[ logSize ];
            head = 0;
            next = 0;
            isFull = false;
            isEmpty = true;
        }

        public void keyTyped( java.awt.event.KeyEvent e ) {

            char c = e.getKeyChar();

            if (!isFull) {      // log isn't full: store it

                log[ next++ ] = c;
                if (next >= logSize)
                    next = 0;
                if (next == head)
                    isFull = true;
                isEmpty = false;
            }
        }

        public char getNextKey() {

            while (isEmpty) // wait for a keystroke if none is logged
                try {
                    Thread.sleep( 100 );
                } catch (Exception e) {}

            char c = log[ head++ ];
            if (head >= logSize)
                head = 0;

            isFull = false;
            if (head == next)
                isEmpty = true;

            return c;
        }
    }

    // ---- class Console --------------------------------------------------

    private JFrame frame;               // the Frame that holds everything
    private TextPanel textPanel;        // within the Frame: the Panel which holds text
    private KeyLogger keyLogger;        // storage and return of asynchronous keystrokes

    // By default, make an 80x25 console

    Console() {
        this(80,25);
    }

    // But we can choose any size

    Console( int nCols, int nRows ) {

        frame = new JFrame();

        keyLogger = new KeyLogger();
        frame.addKeyListener( keyLogger );
        //frame.setFocusable(true);
        frame.requestFocus();

        textPanel = new TextPanel( nCols, nRows, new Font("Monospaced",Font.PLAIN,12), 7, 15, 11 );
        frame.getContentPane().add( textPanel );
        frame.pack();
        frame.setVisible(true);
    }

    public void setCursor( int row, int col ) {
        textPanel.setCursor( row, col );
    }

    public void print( char c ) {
        textPanel.print( c );
    }

    public void print( String s ) {
        textPanel.print( s );
    }

    public void repaint() {
        textPanel.repaint();
    }

    public char readChar() {
        return keyLogger.getNextKey();
    }   
}
