/*
 * Copyright 1999-2011 Alibaba Group Holding Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.druid.support.jconsole;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map.Entry;

import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.border.BevelBorder;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumnModel;

import com.alibaba.druid.support.jconsole.model.ColumnGroup;
import com.alibaba.druid.support.jconsole.model.DruidTableModel;
import com.alibaba.druid.support.jconsole.model.GroupableTableHeader;
import com.alibaba.druid.support.jconsole.model.GroupableTableHeaderUI;
import com.alibaba.druid.support.jconsole.model.RowHeaderTable;

/**
 * 请求地址 /sql.json
 * 
 * 返回数据格式：
 * <pre>
 * {
    "ResultCode": 1,
    "Content": [
        {
            "BatchSizeMax": 0,
            "BatchSizeTotal": 0,
            "ConcurrentMax": 1,
            "DbType": "h2",
            "EffectedRowCount": 18,
            "EffectedRowCountHistogram": [
                0,6,0,0,0,0
            ],
            "EffectedRowCountMax": 4,
            "ErrorCount": 0,
            "ExecuteAndResultHoldTimeHistogram": [
                5,0,1,0,0,0,0,0
            ],
            "ExecuteAndResultSetHoldTime": 0,
            "ExecuteCount": 6,
            "FetchRowCount": 0,
            "FetchRowCountHistogram": [
                6,0,0,0,0,0
            ],
            "FetchRowCountMax": 0,
            "Histogram": [
                5,0,1,0,0,0,0,0
            ],
            "ID": 9,
            "InTransactionCount": 6,
            "LastSlowParameters": "[4]",
            "LastTime": "2012-08-22 02:57:56",
            "MaxTimespan": 18,
            "MaxTimespanOccurTime": "2012-08-22 12:38:56",
            "ResultSetHoldTime": 0,
            "RunningCount": 0,
            "SQL": "delete from acct_group_permission where group_id=?",
            "TotalTime": 21
        },
     ]
  }
  </pre>
  
  @author yunnysunny<yunnysunny@gmail.com>
 * */
public class DruidSQLPanel extends DruidPanel {

    private static final long              serialVersionUID               = 1L;
    private static final String            REQUEST_URL                    = "/sql.json";
    private static final ArrayList<String> SHOW_LIST                      = new ArrayList<String>() {

                                                                              private static final long serialVersionUID = 1L;

                                                                              {
                                                                                  add("SQL");
                                                                                  add("ExecuteCount");
                                                                                  add("TotalTime");
                                                                                  add("InTransactionCount");
                                                                                  add("ErrorCount");
                                                                                  add("EffectedRowCount");
                                                                                  add("FetchRowCount");
                                                                                  add("RunningCount");
                                                                                  add("ConcurrentMax");
                                                                                  add("Histogram");
                                                                                  add("EffectedRowCountHistogram");
                                                                                  add("ExecuteAndResultHoldTimeHistogram");
                                                                                  add("FetchRowCountHistogram");
                                                                              }

                                                                          };
    private static final ArrayList<String> REAL_SHOW_LIST                 = new ArrayList<String>() {

                                                                              private static final long serialVersionUID = 1L;

                                                                              {
                                                                                  add("SQL");
                                                                                  add("ExecuteCount");
                                                                                  add("TotalTime");
                                                                                  add("InTransactionCount");
                                                                                  add("ErrorCount");
                                                                                  add("EffectedRowCount");
                                                                                  add("FetchRowCount");
                                                                                  add("RunningCount");
                                                                                  add("ConcurrentMax");
                                                                                  add("Histogram");
                                                                                  add("EffectedRowCountHistogram");
                                                                                  add("ExecuteAndResultHoldTimeHistogram");
                                                                                  add("FetchRowCountHistogram");
                                                                              }
                                                                          };
    private static final String            HISOGRAM                       = "Histogram";
    private static final String            Effected_RowCount_HISOGRAM     = "EffectedRowCountHistogram";
    private static final String            ExecuteAndResult_Hold_HISOGRAM = "ExecuteAndResultHoldTimeHistogram";
    private static final String            FetchRowCount_HISOGRAM         = "FetchRowCountHistogram";
    private static final ArrayList<String> ARRAY_DATA_MAP                 = new ArrayList<String>() {

                                                                              private static final long serialVersionUID = 1L;
                                                                              {
                                                                                  add(HISOGRAM);
                                                                                  add(Effected_RowCount_HISOGRAM);
                                                                                  add(ExecuteAndResult_Hold_HISOGRAM);
                                                                                  add(FetchRowCount_HISOGRAM);
                                                                              }
                                                                          };
    private static final int               FIST_LIST_OFFSET               = 9;

    private ColumnGroup                    groupHistogram;
    private ColumnGroup                    groupEffectedRowCountHistogram;
    private ColumnGroup                    groupExecuteAndResultHoldTimeHistogram;
    private ColumnGroup                    groupFetchRowCountHistogram;
    private ArrayList<Integer>             listHistogram;
    private ArrayList<Integer>             listEffectedRowCountHistogram;
    private ArrayList<Integer>             listExecuteAndResultHoldTimeHistogram;
    private ArrayList<Integer>             listFetchRowCountHistogram;
    private ArrayList<String>              ids;
    private static final String            JSON_ID_NAME                   = "ID";

    public DruidSQLPanel(){
        super();
        this.url = REQUEST_URL;
    }

    private void addGroupData(String keyNow, int index) {
        // System.out.println("keyNow:"+keyNow+"index:"+index);
        if (HISOGRAM.equals(keyNow)) {
            listHistogram.add(index);
        } else if (Effected_RowCount_HISOGRAM.equals(keyNow)) {
            listEffectedRowCountHistogram.add(index);
        } else if (ExecuteAndResult_Hold_HISOGRAM.equals(keyNow)) {
            listExecuteAndResultHoldTimeHistogram.add(index);
        } else if (FetchRowCount_HISOGRAM.equals(keyNow)) {
            listFetchRowCountHistogram.add(index);
        }
    }

    /**
     * 数据预处理，将没有用到的数据删除掉，将牵扯到时间分布的数据拆分出来。
     * 
     * @param data 要处理的数据
     * @return 最终的处理结果
     */
    private ArrayList<LinkedHashMap<String, Object>> preProcess(ArrayList<LinkedHashMap<String, Object>> data) {
        groupHistogram = new ColumnGroup(HISOGRAM);
        groupEffectedRowCountHistogram = new ColumnGroup(Effected_RowCount_HISOGRAM);
        groupExecuteAndResultHoldTimeHistogram = new ColumnGroup(ExecuteAndResult_Hold_HISOGRAM);
        groupFetchRowCountHistogram = new ColumnGroup(FetchRowCount_HISOGRAM);
        listHistogram = new ArrayList<Integer>();
        listEffectedRowCountHistogram = new ArrayList<Integer>();
        listExecuteAndResultHoldTimeHistogram = new ArrayList<Integer>();
        listFetchRowCountHistogram = new ArrayList<Integer>();
        ids = new ArrayList<String>();

        ArrayList<LinkedHashMap<String, Object>> newData = new ArrayList<LinkedHashMap<String, Object>>();
        int dataIndex = 0;
        for (LinkedHashMap<String, Object> dataNow : data) {
            // 先把不需要显示的内容删除掉
            for (Iterator<Entry<String, Object>> it = dataNow.entrySet().iterator(); it.hasNext();) {
                Entry<String, Object> entry = it.next();
                String key = entry.getKey();
                Object value = entry.getValue();
                if (JSON_ID_NAME.equals(key)) {
                    ids.add(value != null ? value.toString() : null);
                }
                if (!SHOW_LIST.contains(key)) {
                    it.remove();
                }
            }
            // if (dataIndex==0)
            // System.out.println(dataNow.toString());
            int offset = FIST_LIST_OFFSET;

            for (String arrayKey : ARRAY_DATA_MAP) {// 把数组数据显示为单个数据
                Object arrayData = dataNow.get(arrayKey);

                if (arrayData instanceof ArrayList<?>) {

                    dataNow.remove(arrayKey);
                    if (dataIndex == 0) {
                        REAL_SHOW_LIST.remove(arrayKey);
                    }
                    @SuppressWarnings("unchecked")
                    ArrayList<Integer> arrayDataList = (ArrayList<Integer>) arrayData;
                    for (int j = 0, len = arrayDataList.size(); j < len; j++) {
                        int a = (j - 1 >= 0) ? (int) Math.pow(10, j - 1) : 0;
                        int b = (int) Math.pow(10, j);
                        String newKey = arrayKey + "-" + a + "~" + b + "ms";

                        dataNow.put(newKey, arrayDataList.get(j));
                        if (dataIndex == 0) {
                            REAL_SHOW_LIST.add(newKey);
                            int index = offset + j;
                            addGroupData(arrayKey, index);
                            if (j == len - 1) {
                                offset = index + 1;
                            }
                        }
                    }
                } else {

                }

            }// end of foreach ARRAY_DATA_MAP
            dataIndex++;
            newData.add(dataNow);
        }

        return newData;
    }

    private void addTableGroup() {

        TableColumnModel cm = table.getColumnModel();
        // System.out.println(SHOW_LIST.size());
        for (int i : listHistogram) {
            groupHistogram.add(cm.getColumn(i));
        }
        for (int j : listEffectedRowCountHistogram) {
            groupEffectedRowCountHistogram.add(cm.getColumn(j));
        }
        for (int x : listExecuteAndResultHoldTimeHistogram) {
            groupExecuteAndResultHoldTimeHistogram.add(cm.getColumn(x));
        }
        for (int y : listFetchRowCountHistogram) {
            groupFetchRowCountHistogram.add(cm.getColumn(y));
        }

        GroupableTableHeader header = (GroupableTableHeader) table.getTableHeader();
        header.addColumnGroup(groupHistogram);
        header.addColumnGroup(groupEffectedRowCountHistogram);
        header.addColumnGroup(groupExecuteAndResultHoldTimeHistogram);
        header.addColumnGroup(groupFetchRowCountHistogram);
        header.setUI(new GroupableTableHeaderUI());
    }

    @Override
    protected void tableDataProcess(ArrayList<LinkedHashMap<String, Object>> data) {
        table = new JTable() {

            private static final long serialVersionUID = 1L;

            @Override
            protected JTableHeader createDefaultTableHeader() {

                return new GroupableTableHeader(columnModel);
            }
        };
        data = preProcess(data);
        // System.out.println(SHOW_LIST);
        tableModel = new DruidTableModel(data, REAL_SHOW_LIST);
        // System.out.println(data.toString());
        table.setModel(tableModel);
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        addTableGroup();
        table.addMouseListener(new MouseAdapter() {

            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {// 点击几次，这里是双击事件
                    int row = table.getSelectedRow();
                    String id = ids.get(row);
                    new DruidSqlDetailFrame(id,conn);
                }
            }
        });

        RowHeaderTable header = new RowHeaderTable(table, 20);
        scrollPane.setRowHeaderView(header);
        scrollPane.setViewportView(table);
        JLabel jb = new JLabel("N", SwingConstants.CENTER);
        jb.setBorder(new BevelBorder(BevelBorder.RAISED, null, null, null, null));
        scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, jb);
    }
}
