/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package textkeymatcher.entity;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.swing.table.AbstractTableModel;
import textkeymatcher.entity.KeyMatchedRowMap.RowKey;
import textkeymatcher.entity.KeyMatchedRowMap.RowValues;

/**
 * データソースをキーと値に分解し、キーごとに集約されたデータマップから、
 * テーブル形式のグリッド状態にデータを展開する.<br>
 * ひとつのキーに複数の値がある場合、同じキーの行が個数分だけ展開されます.<br>
 * 列はキーと各データソース分となります.<br>
 * @author seraphy
 */
public class KeyMatchedRowView extends AbstractTableModel {
      
    /**
     * RowKeyとRowValuesへのアクセス方法を示すオブジェクト.<br>
     */
    private static class RowNumMap {

        /**
         * キー｀
         */
        private final RowKey rowKey;
        
        /**
         * 各データソースごとの値のセット
         */
        private final RowValues rowValues;
        
        /**
         * 値が複数行ある場合の行番号(0ベース)
         * 単一であれば0となる.
         */
        private final int rowNumber;
        

        public RowNumMap(RowKey rowKey, RowValues rowValues, int rowNumber) {
            this.rowKey = rowKey;
            this.rowValues = rowValues;
            this.rowNumber = rowNumber;
        }
        
        public RowKey getRowKey() {
            return rowKey;
        }
        
        public RowValues getRowValues() {
            return rowValues;
        }
        
        public int getRowNumber() {
            return rowNumber;
        }
    }
    
    
    /**
     * 行データのリスト.<br>
     * 各要素に列データへのアクセス方法が格納されている.<br>
     */
    private List<RowNumMap> rowNumMaps = Collections.emptyList();
    
    /**
     * 列数
     */
    private int numOfColumns;
    

    /**
     * 行数
     * @return 
     */
    @Override
    public int getRowCount() {
        return rowNumMaps.size();
    }

    /**
     * 指定した行のキーデータ(キー判定方法による文字列表現)を取得します.<br>
     * @param rowIndex 行
     * @return キーデータ｀
     */
    public String getKeyAt(int rowIndex) {
        if (rowIndex < 0 || rowIndex >= rowNumMaps.size()) {
            return null;
        }
        return rowNumMaps.get(rowIndex).getRowKey().toString();
    }
    
    /**
     * 指定した行のデータ部を取得します.<br>
     * @param rowIndex 行
     * @param columnIndex データ列
     * @return データ、なければ空文字
     */
    public String getDataAt(int rowIndex, int columnIndex) {
        if (rowIndex < 0 || rowIndex >= rowNumMaps.size()) {
            return null;
        }
        RowNumMap rowNumMap = rowNumMaps.get(rowIndex);
        RowValues rowValues = rowNumMap.getRowValues();
        int rowNumber = rowNumMap.getRowNumber();
        return rowValues.get(columnIndex, rowNumber);
    }

    /**
     * 第一列をキー、それ以降をデータ列として1つのテーブルのカラムとしてアクセスする.<br>
     * @param rowIndex 行
     * @param columnIndex 列、0はキー、それ以降はデータ列
     * @return キーまたはデータの文字列、該当ない場合は空文字
     */
    @Override
    public String getValueAt(int rowIndex, int columnIndex) {
        if (rowIndex < 0 || rowIndex >= rowNumMaps.size()) {
            return null;
        }
        if (columnIndex == 0) {
            return getKeyAt(rowIndex);
        }
        return getDataAt(rowIndex, columnIndex - 1);
    }

    /**
     * キーおよび列のデータ型.<br>
     * すべてStringである.<br>
     * @param i 列、0列はキー、それ以降はデータ列
     * @return 列のデータ形式
     */
    @Override
    public Class<?> getColumnClass(int i) {
        return String.class;
    }

    /**
     * キー列 + データ列数を返す.<br>
     * @return 列数
     */
    @Override
    public int getColumnCount() {
        return 1 + numOfColumns;
    }
    

    /**
     * データマップに従って、あらたしくグリッドを生成しなおします.<br>
     * @param dataMap データマップ
     */
    public void renumbering(Map<RowKey, RowValues> dataMap) {
        if (dataMap == null) {
            throw new IllegalArgumentException();
        }

        int numOfMaxColumns = 0;
        ArrayList<RowNumMap> rows = new ArrayList<RowNumMap>();
        for (Map.Entry<RowKey, RowValues> entry : dataMap.entrySet()) {
            RowKey rowKey = entry.getKey();
            RowValues rowValues = entry.getValue();

            // このキーに対していくつのデータソースが存在するのか?
            // (最大列数の決定のため)
            int dataWidth = rowValues.getDataWidth();
            if (numOfMaxColumns < dataWidth) {
                numOfMaxColumns = dataWidth;
            }

            // このキーに対して最大何行のデータがついているか?
            int numOfRows = rowValues.getMaxCount();
            
            // 行数分だけ同じキーを繰り返す.
            for (int rowNumber = 0; rowNumber < numOfRows; rowNumber++) {
                rows.add(new RowNumMap(rowKey, rowValues, rowNumber));
            }
        }
        
        this.rowNumMaps = rows;
        this.numOfColumns = numOfMaxColumns;
        
        fireTableStructureChanged();
    }
}
