package com.ftinc.si.assist.test.gui;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.regex.Pattern;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;

import com.ftinc.si.assist.run.Messages;
import com.ftinc.si.assist.test.ClassOutLine;
import com.ftinc.si.assist.test.TestCommandRecord;
import com.ftinc.si.assist.test.Tool;

//TestCaseEditorで新規にTestCommandRecordを作る時に起動する。
public class ClassReadDialog extends JDialog {
	public static JDialog s_owner;

	private ArrayList<String> m_targets;
	private JTextField textClass;;

	public ClassOutLine m_curClass;

	private DefaultListModel<String> methd_model = new DefaultListModel<String>();
	private DefaultListModel<String> const_model = new DefaultListModel<String>();
	private JList<String> method_list;
	private JList<String> const_list;
	private JTextArea textArgs;

	public String className = null;
	public String methodName;
	public String[] objs;

	private JComboBox<String> focusedClass;

	@SuppressWarnings("unchecked")
	public ClassReadDialog(JDialog p, ArrayList<String> dirs, TestCommandRecord test) {
		super(p, true); // modalにする。
		setTitle(Messages.getString("ClassReadDialog.0")); //$NON-NLS-1$
		setResizable(false);
		if (p != null) {
			setBounds(p.getX() + 100, p.getY() + 100, 545, 470);
		} else {
			setBounds(100, 100, 545, 470);
		}

		m_targets = ((ArrayList<String>)dirs.clone());
		m_targets.add(Tool.fake_dir);

		getContentPane().setLayout(null);

		JLabel lblClass = new JLabel(Messages.getString("ClassReadDialog.4")); //$NON-NLS-1$
		lblClass.setBounds(12, 10, 50, 13);
		getContentPane().add(lblClass);

		textClass = new JTextField();
		textClass.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				if (e.getKeyCode() == KeyEvent.VK_ENTER) {
					if (textClass.getText().length() > 0) {
						className = textClass.getText();
						setClassName(className);
					}
				}
			}
		});
		textClass.setBounds(74, 10, 378, 19);
		getContentPane().add(textClass);
		textClass.setColumns(10);

		JButton btnFind = new JButton(Messages.getString("ClassReadDialog.5")); //$NON-NLS-1$
		btnFind.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				String cname = Tool.findClassName(Tool.getParent((Component)e.getSource(), "JDialog"), m_targets);
				if (m_targets.size() > 0 && cname == null) {
					//クラスが抽出できなかったとき。
					Tool.alertMSG(null, Messages.getString("ClassReadDialog.8")); //$NON-NLS-1$
				} else if (cname.length() > 0) {
					//長さ0はキャンセル
					className = cname;
					setClassName(className);
				}
			}
		});
		btnFind.setBounds(464, 6, 63, 21);
		getContentPane().add(btnFind);

		JLabel lblArgs = new JLabel(Messages.getString("ClassReadDialog.16")); //$NON-NLS-1$
		lblArgs.setBounds(12, 289, 50, 13);
		getContentPane().add(lblArgs);

		JButton btnOk = new JButton(Messages.getString("ClassReadDialog.17")); //$NON-NLS-1$
		btnOk.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				setVisible(false);
			}
		});
		btnOk.setBounds(196, 410, 91, 21);
		getContentPane().add(btnOk);

		JScrollPane scrollPane_1 = new JScrollPane();
		scrollPane_1.setBounds(74, 283, 453, 117);
		getContentPane().add(scrollPane_1);

		textArgs = new JTextArea();
		scrollPane_1.setViewportView(textArgs);

		JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
		tabbedPane.setBounds(12, 104, 515, 157);
		getContentPane().add(tabbedPane);

		JScrollPane scrollPane = new JScrollPane();
		tabbedPane.addTab(Messages.getString("ClassReadDialog.18"), null, scrollPane, null); //$NON-NLS-1$
		method_list = new JList<String>(methd_model);
		scrollPane.setViewportView(method_list);

		//method listの上の選択操作
		method_list.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if (className == null) return;

				//書式は、「番号：static/final/abstract(略可), methodName, varPos(略可)」
				String t_str = (String) method_list.getSelectedValue();
				if (t_str == null) return;

				String num = t_str.replaceAll("^([0-9]+):.*$", "$1");//メソッドを特定する番号 //$NON-NLS-1$ //$NON-NLS-2$
				methodName = t_str.replaceFirst("^[0-9]+:\\s*", ""); //$NON-NLS-1$ //$NON-NLS-2$ //番号を取る

				//可変長修飾なしのメソッド名
				String mName = methodName.replaceFirst("\\s*[0-9]+$", ""); //可変長引数情報を削除 //$NON-NLS-1$ //$NON-NLS-2$

				if (m_curClass != null) {
					//$_,$0,$1...の情報をt_resにまとめる。
					String t_res = "$_=" + m_curClass.getDeclaredMethods().get(num + "_$_") + "\n"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

					//staticの場合、$0は意味がないので、外す。
					if (!mName.matches("^.*\\sstatic\\s.*$")) { //$NON-NLS-1$
						t_res += "$0=" + m_curClass.getClassName() + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
					}

					Integer j = 1;
					String type_name = null;

					while ((type_name = m_curClass.getDeclaredMethods().get(num + "_$" + j.toString())) != null) { //$NON-NLS-1$
						t_res += "$" + j.toString() + "=" + type_name + "\n"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
						j++;
					}
					objs = t_res.split(Pattern.quote("\n")); //$NON-NLS-1$

					//fake系の型名があれば元に戻す。
					t_res = t_res.replaceAll(Pattern.quote("test.fake."), ""); //$NON-NLS-1$ //$NON-NLS-2$
					t_res = t_res.replaceAll(Pattern.quote("test.impl."), ""); //$NON-NLS-1$ //$NON-NLS-2$

					if (methodName.matches("^.+\\s[0-9]+$")) { //$NON-NLS-1$
						//可変長の場合、追加で「...」
						t_res += "$" + j.toString() + "=...\n"; //$NON-NLS-1$  //$NON-NLS-3$
					}
					if (m_curClass.getDeclaredMethods().get(num + "_updated") != null) {
						t_res += "\n Updated = " + m_curClass.getDeclaredMethods().get(num + "_updated");
					}
					textArgs.setText(t_res);
				}
			}
		});
		method_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

		JScrollPane scrollPane_2 = new JScrollPane();
		tabbedPane.addTab(Messages.getString("ClassReadDialog.34"), null, scrollPane_2, null); //$NON-NLS-1$

		const_list = new JList<String>(const_model);
		const_list.setToolTipText(""); //$NON-NLS-1$
		if (s_owner == null) {
			//TestCaseBrowserから立ち上げる場合、s_owner=null。Fakeの編集ではコンストラクタは不要。
			const_list.setEnabled(false);
		}

		//constructor list上の選択操作
		const_list.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if (className == null || m_curClass == null || s_owner == null) return;

				HashMap<String, String> t_map = m_curClass.getDeclaredConstructors();

				if (t_map == null) {
					Tool.alertMSG(null, Messages.getString("ClassReadDialog.36")); //$NON-NLS-1$
					return;
				}

				String t_con = (String)const_list.getSelectedValue();
				t_con = t_con.replaceFirst(":constructor", "");//コンストラクタを特定する番号。 //$NON-NLS-1$ //$NON-NLS-2$

				String vPos = t_map.get(t_con + "_varArgPos"); //$NON-NLS-1$

				methodName = m_curClass.getClassName();	//Constructorだから
				if (vPos != null) {
					methodName += " " + vPos; //$NON-NLS-1$
				}

				String t_res = "$_=" + m_curClass.getClassName() + "\n"; //$NON-NLS-1$ //$NON-NLS-2$

				Integer j = 1;
				String type_name = null;

				//$0はnullである。
				while ((type_name = t_map.get(t_con + "_$" + j.toString())) != null) { //$NON-NLS-1$
					t_res += "$" + j.toString() + "=" + type_name + "\n"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
					j++;
				}
				objs = t_res.split(Pattern.quote("\n")); //$NON-NLS-1$

				if (vPos != null) {
					//可変長の場合、追加で「...」
					t_res += "$" + j.toString() + "=...\n"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
				}

				textArgs.setText(t_res);
			}
		});
		scrollPane_2.setViewportView(const_list);

		JLabel lblFocused = new JLabel(Messages.getString("ClassReadDialog.46")); //$NON-NLS-1$
		lblFocused.setBounds(12, 59, 91, 13);
		getContentPane().add(lblFocused);

		focusedClass = new JComboBox<String>();

		//comboの選択操作。MethodListの中を埋める。
		focusedClass.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (className == null) return;

				//上下のクラスが選択されたら
				String t_str = focusedClass.getSelectedItem().toString();
				setMethods(t_str);
			}
		});
		focusedClass.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				//enterが押されたら

			}
		});

		focusedClass.setBounds(115, 56, 383, 16);
		getContentPane().add(focusedClass);

		JButton btnCancel = new JButton(Messages.getString("ClassReadDialog.btnCancel.text")); //$NON-NLS-1$
		btnCancel.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				className = null;
				setVisible(false);
			}
		});
		btnCancel.setBounds(301, 410, 91, 21);
		getContentPane().add(btnCancel);

		if (test != null) {
			setTitle(Messages.getString("ClassReadDialog.1") + Integer.valueOf(test.id).toString()); //$NON-NLS-1$
			if (test.className != null && test.className.length() > 0) {
				className = test.className;
				setClassName(className);
			}
		} else {
			setTitle(Messages.getString("ClassReadDialog.3")); //$NON-NLS-1$
		}
	}

	//第一引数は外部から指定で与えられることもある。
	public boolean setClassName(String c_name) {
		className = c_name;

		textClass.setText(className);
		if (c_name != null && c_name.length() > 0) {
			m_curClass = Tool.getClassOutLine(className);
			if (m_curClass != null) {
				setMethods(m_curClass.getClassName());

				//クラスをセットしたついでに、（未初期化なら）ComboBoxも初期化する。
				focusedClass.removeAll();
				focusedClass.addItem(m_curClass.getClassName());

				ClassOutLine s_class = m_curClass;
				while (s_class != null) {
					focusedClass.addItem(s_class.getClassName());
					s_class = s_class.getSuper();
				}
				ArrayList<ClassOutLine> t_lines = m_curClass.getInterfaces();
				for (int i = 0; i < t_lines.size(); i++) {
					focusedClass.addItem(t_lines.get(i).getClassName());
				}

				return true;
			}
		}
		return false;
	}

	//MethodListの中を埋める。
	private void setMethods(String c_name) {
		m_curClass = Tool.getClassOutLine(c_name);

		if (m_curClass == null) return;

		HashMap<String, String> t_con = m_curClass.getDeclaredConstructors();//最後尾のクラスのフィールド一覧
		ArrayList<String> t_list = new ArrayList<String>();

		for(Entry<String, String> entry : t_con.entrySet()) {
			if (entry.getKey().endsWith("_name")) { //$NON-NLS-1$
				String t_key = entry.getKey();
				t_key = t_key.replaceFirst("_name", ""); //$NON-NLS-1$ //$NON-NLS-2$

				t_key = String.format("%1$03d", Integer.valueOf(t_key));//3桁の数字で先頭0埋め //$NON-NLS-1$

				t_list.add(t_key + ":constructor"); //$NON-NLS-1$
			}
		}
		const_list.removeAll();
		sort(t_list, const_model);
		const_list.setModel(const_model);
		t_list.clear();

		HashMap<String, String> t_met = m_curClass.getDeclaredMethods();//最後尾のクラスのフィールド一覧

		for(Entry<String, String> entry : t_met.entrySet()) {
			if (entry.getKey().endsWith("_name")) { //$NON-NLS-1$
				String t_key = entry.getKey();
				t_key = t_key.replaceFirst("_name", ""); //$NON-NLS-1$ //$NON-NLS-2$

				t_key = String.format("%1$03d", Integer.valueOf(t_key));//3桁の数字で先頭0埋め //$NON-NLS-1$

				t_list.add(t_key + ":" + t_met.get(entry.getKey())); //$NON-NLS-1$
			}
		}
		method_list.removeAll();
		sort(t_list, methd_model);
		method_list.setModel(methd_model);
	}


	private void sort(ArrayList<String> list, DefaultListModel<String> model) {
		model.removeAllElements();
		Collections.sort(list);

		for (int i = 0; i < list.size(); i++) {
			model.addElement(list.get(i));
		}
	}

	public String getArgTypes() {
		String t_res = ""; //$NON-NLS-1$
		if (objs != null) {
			for (int i =0; i < objs.length; i++) {
				t_res += objs[i];
				if (i < objs.length -1) {
					t_res += ",\n"; //$NON-NLS-1$
				}
			}
		}

		//fake系の型名があれば元に戻す。
		t_res = t_res.replaceAll(Pattern.quote("test.fake."), ""); //$NON-NLS-1$ //$NON-NLS-2$
		t_res = t_res.replaceAll(Pattern.quote("test.impl."), ""); //$NON-NLS-1$ //$NON-NLS-2$
		return t_res;
	}
}
