package co.dc.aptiv;

import java.awt.Color;
import java.awt.Dimension;
import java.io.OutputStream;
import java.io.PrintStream;

import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.AbstractDocument;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

public class ConsolePane extends JScrollPane {
    private JTextPane textPane = new JTextPane();

    private static ConsolePane console = null;

    public static synchronized ConsolePane getInstance() {
        if (console == null) {
            console = new ConsolePane();
        }
        return console;
    }

    private ConsolePane() {

        setViewportView(textPane);

        // Set up System.out
        PrintStream mySystemOut = new MyPrintStream(System.out, Color.BLACK);
        System.setOut(mySystemOut);

        // Set up System.err
        PrintStream mySystemErr = new MyPrintStream(System.err, Color.RED);
        System.setErr(mySystemErr);

        textPane.setEditable(true);
        setPreferredSize(new Dimension(640, 120));
    }

    /**
     * Returns the number of lines in the document.
     */
    private final int getLineCount() {
        return textPane.getDocument().getDefaultRootElement().getElementCount();
    }

    /**
     * Returns the start offset of the specified line.
     *
     * @param line The line
     * @return The start offset of the specified line, or -1 if the line is
     * invalid
     */
    private int getLineStartOffset(int line) {
        Element lineElement = textPane.getDocument().getDefaultRootElement() .getElement(line);
        if (lineElement == null) {
            return -1;
        } else {
            return lineElement.getStartOffset();
        }
    }

    /**
     * 清除超过行数时前面多出行的字符
     */
    private void replaceRange(String str, int start, int end) {
        if (end < start) {
            throw new IllegalArgumentException("end before start");
        }
        Document doc = textPane.getDocument();
        if (doc != null) {
            try {
                if (doc instanceof AbstractDocument) {
                    ((AbstractDocument) doc).replace(start, end - start, str,
                            null);
                } else {
                    doc.remove(start, end - start);
                    doc.insertString(start, str, null);
                }
            } catch (BadLocationException e) {
                throw new IllegalArgumentException(e.getMessage());
            }
        }
    }

    class MyPrintStream extends PrintStream {
        //输出时所用字体颜色
        private Color foreground;

        /**
         * 构造自己的 PrintStream
         *
         * @param out        可传入 System.out 或 System.err, 实际不起作用
         * @param foreground 显示字体颜色
         */
        MyPrintStream(OutputStream out, Color foreground) {
            //使用自动刷新
            super(out, true);
            this.foreground = foreground;
        }

        /**
         * 在这里重截,所有的打印方法都要调用最底一层的方法
         */
        @Override
        public void write(byte[] buf, int off, int len) {
            final String message = new String(buf, off, len);

            /** SWING非界面线程访问组件的方式 */
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    try {
                        StyledDocument doc = (StyledDocument) textPane.getDocument();
                        // Create a style object and then set the style
                        // attributes
                        Style style = doc.addStyle("StyleName", null);
                        // Foreground color
                        StyleConstants.setForeground(style, foreground);
                        doc.insertString(doc.getLength(), message, style);
                    } catch (BadLocationException e) {
                        // e.printStackTrace();
                    }
                    // Make sure the last line is always visible
                    textPane.setCaretPosition(textPane.getDocument().getLength());
                    // Keep the text area down to a certain line count
                    int idealLine = 150;
                    int maxExcess = 50;
                    int excess = getLineCount() - idealLine;
                    if (excess >= maxExcess) {
                        replaceRange("", 0, getLineStartOffset(excess));
                    }
                }
            });
        }
    }
}
