package org.positrium.chikarawo;

import java.awt.Component;
import java.awt.Font;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JViewport;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class LogWindow {

	private static final int MAX_LINE = 1024;

	private static LogWindow instance = new LogWindow();

	private JFrame frame;
	private JTextArea textArea;
	private JScrollPane scrollPane;
	private JViewport viewpoint;

	private int lastScrollHeight = -1;
	private int lastViewHeight = -1;
	private boolean changedFlg = false;
	private StringBuffer buffer = new StringBuffer();

	private LogWindow() {
		frame = new JFrame("NicoCacheログ");
		frame.setBounds(16, 16, 640, 480);
		// frame.setIconImage(IconImage.getImage());

		Font font = new Font(Font.MONOSPACED, Font.PLAIN, 16);

		textArea = new JTextArea();
		textArea.setFont(font);

		// タスクトレイのPopupMenuを作成
		final JPopupMenu popup = new JPopupMenu();
		JMenuItem menu_copy = new JMenuItem("コピー");
		menu_copy.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {

			}
		});
		popup.add(menu_copy);
		popup.addSeparator();
		final JCheckBoxMenuItem menu_wrap = new JCheckBoxMenuItem("右端で折り返す");
		menu_wrap.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				boolean blnState = menu_wrap.getState();
				textArea.setLineWrap(blnState);
				menu_wrap.setState(blnState);
			}
		});
		textArea.setLineWrap(menu_wrap.getState());
		popup.add(menu_wrap);
		popup.addSeparator();
		final JCheckBoxMenuItem menu_top = new JCheckBoxMenuItem("常に最前面に表示");
		menu_top.setState(true);
		menu_top.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				boolean blnState = menu_top.getState();
				frame.setAlwaysOnTop(blnState);
				menu_top.setState(blnState);
			}
		});
//		frame.setAlwaysOnTop(menu_top.getState());
		popup.add(menu_top);

		// ログウインドウ内の右クリックメニュー
		textArea.addMouseListener(new MouseAdapter() {

			@Override
			public void mousePressed(MouseEvent e) {
				// 右クリックの場合はポップアップを表示
				if (e.getButton() == MouseEvent.BUTTON3) {
					popup.show((Component) (e.getSource()), e.getX(), e.getY());
					return;
				}
				super.mousePressed(e);
			}

		});

		scrollPane = new JScrollPane(textArea);
		viewpoint = scrollPane.getViewport();

		// Viewpointのイベントに合わせてスクロールを調整
		{
			viewpoint.addChangeListener(new ChangeListener() {

				@Override
				public void stateChanged(ChangeEvent arg0) {
					Rectangle rect = viewpoint.getViewRect();

					// 内容と表示とウインドウサイズが変化していなければ以降の処理はしない
					if (lastScrollHeight == textArea.getHeight()
							&& lastViewHeight == rect.height
							&& changedFlg == false)
						return;

					// TODO デバッグ用
					/*
					 * if (Boolean.getBoolean("dareka.debug")) {
					 * System.out.println("lastScrollHeight:" +
					 * lastScrollHeight); System.out.println("lastViewHeight  :"
					 * + lastScrollHeight);
					 * System.out.println("textArea.getHeight:" +
					 * textArea.getHeight()); System.out.println("rect.y     :"
					 * + rect.y); System.out.println("rect.height:" +
					 * rect.height); }
					 */

					// 追記前のスクロール位置が一番下の場合、追記に合わせてスクロールを移動
					if (lastScrollHeight - lastViewHeight == rect.y) {
						rect.setLocation(rect.x, textArea.getHeight()
								- rect.height);
						textArea.scrollRectToVisible(rect);
					}

					lastScrollHeight = textArea.getHeight();
					lastViewHeight = rect.height;
					changedFlg = false;
				}

			});
		}

		frame.add(scrollPane);
		new LogAppender().start();
	}

	public static LogWindow getInstance() {
		return instance;
	}

	public void show() {
		// ウインドウを表示
		frame.setVisible(true);
		// ウインドウの最小化を戻す
		frame.setState(JFrame.NORMAL);
	}

	public void close() {
		instance = null;

		frame.dispose();
		frame = null;
	}

	public Rectangle getBounds() {
		return frame.getBounds();
	}

	public void setBounds(Rectangle r) {
		frame.setBounds(r);
	}
	
	public void append(String str) {
		synchronized (buffer) {
			buffer.append(str);
		}
	}

	public void append(byte[] b) {
		synchronized (buffer) {
			buffer.append(b);
		}
	}

	public void append(byte[] b, int off, int len) {
		append(new String(b, off, len));
	}

	public void append(int b) {
		synchronized (buffer) {
			buffer.append(b);
		}

	}

	private class LogAppender extends Thread {
		public void run() {
			while (true) {
				if (buffer.length() > 0) {
					// テキストエリアにログを追記
					synchronized (buffer) {
						textArea.append(buffer.toString());
						buffer.delete(0, buffer.length());
					}
					// 最大ログ行数を超えたら先頭から順に削除
					if (textArea.getLineCount() > MAX_LINE) {
						try {
							int offset = textArea.getLineEndOffset(textArea
									.getLineCount()
									- MAX_LINE - 1);
							textArea.getDocument().remove(0, offset);
						} catch (Exception ex) {
							ex.printStackTrace();
						}
					}
	
					// 　内容変更済みフラグを立てる
					changedFlg = true;
	
					// TODO デバッグ用
					if (Boolean.getBoolean("dareka.debug")) {
						frame.setTitle("NicoCacheログ ( " + textArea.getLineCount()
								+ " /" + MAX_LINE + ")");
					}
				}
				try {
					sleep(500);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

}
