package tainavi;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.SpringLayout;
import javax.swing.border.LineBorder;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;

/**
 * CHソート設定のタブ
 * @since 3.15.4β　VWChannelSortからクラス名変更
 * @version 3.16 全面リライト
 */
public abstract class AbsChannelSortView extends JScrollPane {

	private static final long serialVersionUID = 1L;

	public void setDebug(boolean b) { debug = b; }
	private boolean debug = false;
	
	
	/*******************************************************************************
	 * 抽象メソッド
	 ******************************************************************************/
	
	protected abstract Env getEnv();
	protected abstract TVProgramList getTVProgramList();
	protected abstract ChannelSort getChannelSort();

	protected abstract StatusTextArea getMWin();

	/**
	 * ソート設定の更新を反映してください
	 */
	protected abstract void updProc();
	
	
	
	/*******************************************************************************
	 * 呼び出し元から引き継いだもの
	 ******************************************************************************/
	
	private final Env env = getEnv();
	private final TVProgramList tvprograms = getTVProgramList();
	private final ChannelSort chsort = getChannelSort();

	private final StatusTextArea MWin = getMWin();			// これは起動時に作成されたまま変更されないオブジェクト

	
	
	/*******************************************************************************
	 * 定数
	 ******************************************************************************/
	
	private static final String MSGID = "[CHソート設定] ";
	//private static final String ERRID = "[ERROR]"+MSGID;
	private static final String DBGID = "[DEBUG]"+MSGID;
	
	//private static final int PARTS_WIDTH = 900;
	private static final int PARTS_HEIGHT = 30;
	private static final int SEP_WIDTH = 10;
	private static final int SEP_HEIGHT = 10;
	//private static final int BLOCK_SEP_HEIGHT = 75;

	private static final int LABEL_WIDTH = 250;
	private static final int BUTTON_WIDTH = 75;
	//private static final int BUTTON_WIDTH_LONG = 200;
	
	private static final int TABLE_WIDTH = 450;
	private static final int TABLE_HEIGHT = 475;
	
	private static final int UPDATE_WIDTH = 250;
	private static final int HINT_WIDTH = 750;

	private static final int PANEL_WIDTH = SEP_WIDTH+LABEL_WIDTH+SEP_WIDTH+TABLE_WIDTH+SEP_WIDTH;
	
	private static final String PAGEDIS_COLOR = "#888888";
	private static final String PAGEEN_ODD_COLOR = "#FFA0A0";
	private static final String PAGEEN_EVEN_COLOR = "#FF6060";
	
	private static final String LABEL_UPDATE = "更新を確定する";
	
	private static final String LABEL_ENABLED = "新聞形式の放送局表示順並べ替えは有効です";
	private static final String LABEL_DISABLED = "新聞形式の放送局表示順並べ替えは無効です";
	private static final String LABEL_ENABLED_U = LABEL_ENABLED+"（要更新確定）";
	private static final String LABEL_DISABLED_U = LABEL_DISABLED+"（要更新確定）";
	
	public static enum ChSortColumn {
		PAGE		("ページ",				50),
		WEBCHNAME	("Web番組表の放送局名",	TABLE_WIDTH-50),
		;

		private String name;
		private int iniWidth;

		private ChSortColumn(String name, int iniWidth) {
			this.name = name;
			this.iniWidth = iniWidth;
		}

		public String getName() {
			return name;
		}

		public int getIniWidth() {
			return iniWidth;
		}
		
		public int getColumn() {
			return ordinal();
		}
	};
	
	
	/*******************************************************************************
	 * 部品
	 ******************************************************************************/
	
	// コンポーネント
	
	private JPanel jpan_update = null;
	private JPanel jpan_chsort = null;
	
	private JButton jbtn_update = null;
	//private JButton jbtn_refresh = null;
	private JToggleButton jtgl_chsort = null;
	private JButton jbtn_up = null;
	private JButton jbtn_down = null;
	
	private JScrollPane jscr_entries = null;
	private JNETable jtbl_entries = null;
	
	private JTextAreaWithPopup jta_help = null;
	
	// コンポーネント以外
	
	RowItemList<ChSortItem> rowData = null;
	
	ChannelSort chsortbak = null;
	
	
	/*******************************************************************************
	 * コンストラクタ
	 ******************************************************************************/
	
	public AbsChannelSortView() {
		
		super();
		
		rowData = new RowItemList<ChSortItem>();
		
		this.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
		this.getVerticalScrollBar().setUnitIncrement(25);
		this.setViewportView(getJPan_chsort());
		this.setColumnHeaderView(getJPan_update());
		
		// 無効だろうが読む
		//chsort.load();
		
		// 初期値は？
		if ( env.getChSortEnabled() ) {
			chsortbak = null;
			
			jtgl_chsort.setSelected(true);
			
			updateChannelSortList(false);	// 保存はしない
		}
		else {
			backupChSort();
			
			_updateChannelSortTable();	// テーブルならべる
		}
		
		setUpdateButtonEnabled(false);	// 設定を変えないと押せない
	}
	
	private JPanel getJPan_update() {
		if (jpan_update == null)
		{
			jpan_update = new JPanel();
			jpan_update.setLayout(new SpringLayout());
			
			jpan_update.setBorder(new LineBorder(Color.GRAY));
			
			int y = SEP_HEIGHT;
			CommonSwingUtils.putComponentOn(jpan_update, getJBtn_update(LABEL_UPDATE), UPDATE_WIDTH, PARTS_HEIGHT, SEP_WIDTH, y);
			
			int yz = SEP_HEIGHT/2;
			int x = UPDATE_WIDTH+50;
			CommonSwingUtils.putComponentOn(jpan_update, getJta_help(), HINT_WIDTH, PARTS_HEIGHT+SEP_HEIGHT, x, yz);
			
			y += (PARTS_HEIGHT + SEP_HEIGHT);
			
			// 画面の全体サイズを決める
			Dimension d = new Dimension(PANEL_WIDTH,y);
			jpan_update.setPreferredSize(d);
		}
		return jpan_update;
	}
	
	private JPanel getJPan_chsort() {
		if (jpan_chsort == null) {
			jpan_chsort = new JPanel();
			jpan_chsort.setLayout(new SpringLayout());
			
			int y = SEP_HEIGHT;
			int x = SEP_WIDTH+LABEL_WIDTH+SEP_WIDTH;
			CommonSwingUtils.putComponentOn(jpan_chsort, getJTgl_chsort(), TABLE_WIDTH, PARTS_HEIGHT, x, y);
			
			//y+=(PARTS_HEIGHT+SEP_HEIGHT);
			//CommonSwingUtils.putComponentOn(jpan_chsort, getJBtn_refresh("CH設定を変更したら放送局リストをリフレッシュしてください"), TABLE_WIDTH, PARTS_HEIGHT, x, y);
			
			y+=(PARTS_HEIGHT+SEP_HEIGHT);
			CommonSwingUtils.putComponentOn(jpan_chsort, getJScr_entries(), TABLE_WIDTH, TABLE_HEIGHT, x, y);
			
			int yz = y + TABLE_HEIGHT/2;
			x -= (BUTTON_WIDTH+SEP_WIDTH);
			CommonSwingUtils.putComponentOn(jpan_chsort, getJBtn_up("上へ"), BUTTON_WIDTH, PARTS_HEIGHT, x, yz-PARTS_HEIGHT-10);
			CommonSwingUtils.putComponentOn(jpan_chsort, getJBtn_down("下へ"), BUTTON_WIDTH, PARTS_HEIGHT, x, yz+10);
			
			y+=(TABLE_HEIGHT+SEP_HEIGHT+SEP_HEIGHT);
			
			Dimension d = new Dimension(PANEL_WIDTH,y);
			jpan_chsort.setPreferredSize(d);
		}
		return jpan_chsort;
	}
	
	
	
	/*******************************************************************************
	 * アクション
	 ******************************************************************************/
	
	/**
	 *  既存にマッチしたものは順番を維持する・しないものは末尾に追加。こちらは外部（CH設定タブ）からの呼び出し用
	 */
	public void updateChannelSortTable() {
		
		if (debug) System.out.println(DBGID+"REFRESH KICKED BY CHSETTING");

		// 順番を引き継ぐ
		_updateChannelSortTable();
		// 結果を保存して
		updateChannelSortList(env.getChSortEnabled());	// 状態によって保存したりしなかったり
	}
	
	/**
	 * 既存にマッチしたものは順番を維持する・しないものは末尾に追加。こちらは内部からの呼び出し用
	 */
	private void _updateChannelSortTable() {
		
		if (debug) System.out.println(DBGID+"REFRESH");
		
		// 番組表プラグインの放送局リストをなめて、現在のリストとつきあわせて引き継げるものは引き継げるようによりわける
		ArrayList<ChSortItem> en = new ArrayList<ChSortItem>();
		ArrayList<ChSortItem> dis = new ArrayList<ChSortItem>();
		for (TVProgram p : tvprograms.getProgPlugins()) {
			for (Center cr : p.getSortedCRlist()) {
				ChSortItem si = new ChSortItem();
				si.page = "";
				si.webChName = cr.getCenter();
				si.areaCode = cr.getAreaCode();
				si.fireChanged();
				for (Center cs : chsort.getClst()) {
					if (cs.getCenter().equals(si.webChName) && cs.getAreaCode().equals(si.areaCode)) {
						en.add(si);
						si = null;
						break;
					}
				}
				if (si != null) {
					dis.add(si);
				}
			}
		}
		
		// よりわけたものをもとの順序にならべかえる
		RowItemList<ChSortItem> newRowData = new RowItemList<ChSortItem>();
		for (Center cs : chsort.getClst()) {
			// chsortの順序を引き継ぐ
			for (ChSortItem a : en) {
				if (cs.getCenter().equals(a.webChName) && cs.getAreaCode().equals(a.areaCode)) {
					newRowData.add(a);
				}
			}
		}
		
		// 新規追加分は入ってきた順番に追加
		for (ChSortItem a : dis) {
			newRowData.add(a);
		}
		
		// テーブル入れ替え
		rowData = newRowData;

		// Fire!
		((DefaultTableModel) jtbl_entries.getModel()).fireTableDataChanged();
		
	}
	
	private void updateChannelSortList(boolean save) {
		chsort.clear();
		for ( RowItem ra : rowData ) {
			ChSortItem a = (ChSortItem) ra;
			Center cr = new Center();
			cr.setCenter(a.webChName);
			cr.setAreaCode(a.areaCode);
			chsort.add(cr);
		}
		if (save) chsort.save();
	}
	
	private void setUpdateButtonEnabled(boolean b) {
		if ( b ) {
			jbtn_update.setForeground(Color.RED);
			jbtn_update.setEnabled(true);
			
			if ( jtgl_chsort.isSelected() ) {
				jtgl_chsort.setText(LABEL_ENABLED_U);
				jtgl_chsort.setForeground(Color.RED);
			}
			else {
				jtgl_chsort.setText(LABEL_DISABLED_U);
				jtgl_chsort.setForeground(Color.RED);
			}
		}
		else {
			jbtn_update.setForeground(Color.BLACK);
			jbtn_update.setEnabled(false);
			
			if ( jtgl_chsort.isSelected() ) {
				jtgl_chsort.setText(LABEL_ENABLED);
				jtgl_chsort.setForeground(Color.BLUE);
			}
			else {
				jtgl_chsort.setText(LABEL_DISABLED);
				jtgl_chsort.setForeground(Color.BLACK);
			}
		}
	}
	
	private void backupChSort() {
		chsortbak = new ChannelSort();
		copyChSort(chsortbak, chsort);
		chsort.clear();
	}
	private void undoChSort() {
		if ( chsortbak != null ) {
			chsort.clear();
			copyChSort(chsort, chsortbak);
			chsortbak = null;
		}
	}
	
	private void copyChSort(ChannelSort to, ChannelSort from) {
		to.clear();
		for ( Center cr : from.getClst() ) {
			to.add(cr);
		}
		
	}
	
	
	/*******************************************************************************
	 * リスナー
	 ******************************************************************************/
	
	/**
	 * 更新を確定したいなあ
	 */
	private final ActionListener al_update = new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent e) {
			
			env.setChSortEnabled(jtgl_chsort.isSelected());
			
			if ( env.getChSortEnabled() ) {
				// 放送局リストを作成する
				updateChannelSortList(true);
			}
			else {
				// 放送局リストをCH設定通りの並べに戻す
				_updateChannelSortTable();
			}

			// 更新確定ボタンは無効に戻す
			setUpdateButtonEnabled(false);
			
			// 本体側でなにか処理があるかなー？
			updProc();
			
			MWin.appendMessage(MSGID+"設定を保存しました");
		}
	};
	
	/**
	 * トグルしたいなー
	 */
	private final ItemListener il_chsort = new ItemListener() {
		@Override
		public void itemStateChanged(ItemEvent e) {
			
			if (debug) System.out.println(DBGID+"chsort "+e.paramString());
			
			JToggleButton source = (JToggleButton) e.getSource();
			
			// コンポーネントの状態を変える
			{
				jtbl_entries.setEnabled(source.isSelected());
				jbtn_up.setEnabled(source.isSelected());
				jbtn_down.setEnabled(source.isSelected());
			}
			
			if (source.isSelected()) {
				// 有効にした時
				undoChSort();
				
				_updateChannelSortTable();
			}
			else {
				// 無効にした時
				backupChSort();
				
				_updateChannelSortTable();
			}
			
			if ( ! jbtn_update.isSelected() ) setUpdateButtonEnabled(true);	// 変更されちゃった
		}
	};
	
	private final PropertyChangeListener pcl_up = new PropertyChangeListener() {
		@Override
		public void propertyChange(PropertyChangeEvent e) {
			if ( "BUTTON.BP_PUSHBUTTON".equals(e.getPropertyName()) && e.getNewValue() != null && "PRESSED".equals(e.getNewValue().toString()) ) {
				if (debug) System.out.println(DBGID+"up "+e.getPropertyName()+" "+e.getNewValue());
				
				int vrow = jtbl_entries.getSelectedRow();
				int cnt = jtbl_entries.getSelectedRowCount();
				if ( rowData.up(vrow, cnt) ) {
					((DefaultTableModel) jtbl_entries.getModel()).fireTableDataChanged();
					jtbl_entries.setRowSelectionInterval(vrow-1, vrow-1+(cnt-1));
				}
				
				if ( ! jbtn_update.isSelected() ) setUpdateButtonEnabled(true);	// 変更されちゃった
			}
		}
	};
	
	private final PropertyChangeListener pcl_down = new PropertyChangeListener() {
		@Override
		public void propertyChange(PropertyChangeEvent e) {
			if ( "BUTTON.BP_PUSHBUTTON".equals(e.getPropertyName()) && e.getNewValue() != null && "PRESSED".equals(e.getNewValue().toString()) ) {
				
				int vrow = jtbl_entries.getSelectedRow();
				int cnt = jtbl_entries.getSelectedRowCount();
				if ( rowData.down(vrow, cnt) ) {
					((DefaultTableModel) jtbl_entries.getModel()).fireTableDataChanged();
					jtbl_entries.setRowSelectionInterval(vrow+1, vrow+1+(cnt-1));
				}
				
				if ( ! jbtn_update.isSelected() ) setUpdateButtonEnabled(true);	// 変更されちゃった
			}
		}
	};
	
	
	
	/*******************************************************************************
	 * コンポーネント
	 ******************************************************************************/
	
	private JButton getJBtn_update(String s) {
		if (jbtn_update == null) {
			jbtn_update = new JButton(s);
			
			jbtn_update.addActionListener(al_update);
		}
		return jbtn_update;
	}
	
	private JToggleButton getJTgl_chsort() {
		if (jtgl_chsort == null) {
			jtgl_chsort = new JToggleButton();
			jtgl_chsort.setSelected(false);
			jtgl_chsort.addItemListener(il_chsort);
		}
		return jtgl_chsort;
	}
	
	private JScrollPane getJScr_entries() {
		if (jscr_entries == null) {
			jscr_entries = new JScrollPane();
			jscr_entries.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
			jscr_entries.getVerticalScrollBar().setUnitIncrement(25);
			jscr_entries.setViewportView(getJTbl_entries());
		}
		return jscr_entries;
	}
	
	private JNETable getJTbl_entries() {
		if (jtbl_entries == null) {
			
			// カラム名の初期化
			ArrayList<String> cola = new ArrayList<String>();
			for ( ChSortColumn lc : ChSortColumn.values() ) {
				if ( lc.getIniWidth() >= 0 ) {
					cola.add(lc.getName());
				}
			}
			final String[] colname = cola.toArray(new String[0]);
			
			DefaultTableModel model = new DefaultTableModel(colname, 0);
			jtbl_entries = new JChSortTable(model,false);
			jtbl_entries.getTableHeader().setReorderingAllowed(false);
			
			jtbl_entries.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
			
			// 各カラムの幅を設定する
			DefaultTableColumnModel columnModel = (DefaultTableColumnModel) jtbl_entries.getColumnModel();
			TableColumn column = null;
			for ( ChSortColumn lc : ChSortColumn.values() ) {
				if ( lc.getIniWidth() < 0 ) {
					continue;
				}
				column = columnModel.getColumn(lc.ordinal());
				column.setPreferredWidth(lc.getIniWidth());
			}
			
			// ページ欄は色つき＆センタリング
			column = columnModel.getColumn(ChSortColumn.PAGE.ordinal());
			TableCellRenderer renderer = new VWColorCellRenderer();
			column.setCellRenderer(renderer);
		}
		return jtbl_entries;
	}
	
	// 放送局を上へ・下へ
	private JButton getJBtn_up(String s) {
		if (jbtn_up == null) {
			jbtn_up = new JButton(s);
			jbtn_up.addPropertyChangeListener(pcl_up);
		}
		return jbtn_up;
	}
	private JButton getJBtn_down(String s) {
		if (jbtn_down == null) {
			jbtn_down = new JButton(s);
			jbtn_down.addPropertyChangeListener(pcl_down);
		}
		return jbtn_down;
	}
	
	//
	private JTextAreaWithPopup getJta_help() {
		if ( jta_help == null ) {
			jta_help = CommonSwingUtils.getJta(this,2,0);
			jta_help.append(
					"Web番組表をまたいで放送局を並べ替えるための機能です。先にCH設定を完了させる必要があります。\n"+
					"（この機能の有効無効は処理速度に影響しません）");
		}
		return jta_help;
	}
	
	
	/*******************************************************************************
	 * 独自部品
	 ******************************************************************************/

	// テーブルの行データの構造
	private class ChSortItem extends RowItem implements Cloneable {
		String page;
		String webChName;
		String areaCode;
		
		@Override
		protected void myrefresh(RowItem o) {
			ChSortItem c = (ChSortItem) o;
			c.addData(page);
			c.addData(webChName);
			c.addData(areaCode);
		}
		
		public ChSortItem clone() {
			return (ChSortItem) super.clone();
		}
	}
	
	// ChSortItemを使ったJTable拡張
	private class JChSortTable extends JNETable {

		private static final long serialVersionUID = 1L;

		@Override
		public Object getValueAt(int row, int column) {
			
			if ( column != 0 ) {
				// １カラム目以外はそのまま
				return rowData.get(row).get(column);
			}
			
			if ( ! env.isPagerEnabled() ) {
				// ページャーが無効ならグレーアウト
				return CommonSwingUtils.getColoredString(PAGEDIS_COLOR,"-");
			}
			
			// １行ごとに背景色を互い違いにする。またソートが無効な場合はグレーアウト
			int page = 1 + env.getPageIndex(1+this.convertRowIndexToView(row));
			int pagemax = 1 + env.getPageIndex(rowData.size());
			String color = ( ! jtgl_chsort.isSelected())?(PAGEDIS_COLOR):((page % 2 == 0)?(PAGEEN_ODD_COLOR):(PAGEEN_EVEN_COLOR));
			String text = String.format("%d / %d", page, pagemax);
			return CommonSwingUtils.getColoredString(color, text);
					
		}
		
		@Override
		public int getRowCount() {
			return rowData.size();
		}
		
		public JChSortTable(TableModel d, boolean b) {
			super(d,b);
		}
	}
}
