package org.jcon.df.edit.module;

import org.jcon.df.edit.EditEvent;
import org.jcon.df.edit.EditListener;
import org.jcon.df.work.RowSetEvent;
import org.jcon.df.work.Row;
import org.jcon.df.work.RowSetReader;
import org.jcon.param.Param;
import org.jcon.param.ParamConverter;
import org.jcon.ui.table.BTable;
import org.jcon.ui.table.BTableEvent;
import org.jcon.ui.table.BTableListener;
import org.jcon.ui.table.BTableModel;
import org.jcon.ui.VisualLib;
import org.jcon.util.DataLib;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Vector;

/**
 * Provides a multi-column list of rows on a panel.
 *
 * @author Jack Harich
 */
public class RowListPanel implements
    Module, BTableListener {

//---------- Private Fields ------------------------------
private String           dataSourceID;
private Panel            mainPanel = new Panel();
private EditListener     controller;
private Vector           childModules = new Vector();
private Vector           controls = new Vector();

private Model        model   = new Model();
private BTable       table   = new BTable();
private String[][]   items   = new String[0][0];
private String[]     columnIDs; // JTable columns
private RowSetReader rowSetReader;
private boolean      ignoreEvents;
private boolean      ignoreValueChanged = true;
private String       name;

//---------- Initialization ------------------------------
public static void main(String args[]) {
    // obsolete new RowListPanel().runUnitTest();
}
//---------- BTableListener Implementation ---------------
public void processBTableEvent(BTableEvent evt) {
    int type = evt.getEventType();
    switch(type) {
        case BTableEvent.ROW_SELECTED:
            rowSelected(evt.getRowIndex());
            break;
        case BTableEvent.ROW_ACTIVATED:
            fireRowIndexActivated(evt.getRowIndex());
            break;
    }
}
private void rowSelected(int rowIndex) {
    //print(".rowSelected() - rowIndex = " + rowIndex + ", ignoreEvents = " + ignoreEvents);
    if (ignoreEvents || rowIndex < 0) return;

// *****************************************************
// *** Causes infinite loop in browse view setVisible(true)
    // Ignore first time
    if (ignoreValueChanged) {
        ignoreValueChanged = false;
    } else {
        if (controller == null) return;
        ignoreEvents = true;
            // Triggers CURRENT_ROW_CHANGED
            EditEvent editEvent = new EditEvent(
                EditEvent.MOVE_TO, dataSourceID);
            editEvent.setPosition("" + rowIndex);
            controller.processEditEvent(editEvent);
        ignoreEvents = false;
    }
}
//---------- Module Implementation -----------------------
//----- Name
public void setName(String name) {
    this.name = name;
}
public String getName() {
    return name;
}
//----- Other
public void setParam(Param param) {
    table.addBTableListener(this);
    table.setModel(model);
    //print(" received: " + param);
    dataSourceID = param.getString("DataSourceID");
    Param browseParam = param.getParam("Browse");
    // Add columns
    Vector columnLines = browseParam.getVector("Columns");

    columnIDs = new String[columnLines.size()];
    //print("columnIDs.length " + columnIDs.length);
    for (int i = 0; i < columnLines.size(); i++) {
        String line = (String)columnLines.elementAt(i);
        addColumnLine(i, line);
    }
    // Set preferred height
    int prefHeight = browseParam.getInt("Height");
    table.setPreferredHeight(prefHeight);
    // Prepare table
    table.init();
    mainPanel.setLayout(new BorderLayout());
    mainPanel.add("Center", table.getComponent());
}
public Component getComponent() {
    return mainPanel;
}
public void setEditListener(EditListener controller) {
    this.controller = controller;
}
public void setDataSourceID(String dataSourceID) {
    this.dataSourceID = dataSourceID;
}
public String getDataSourceID() {
    return dataSourceID;
}
public Vector getChildModules() {
    return childModules; // Empty
}
public Vector getControls() {
    return controls; // Empty
}
public void processRowSetEvent(RowSetEvent evt) {
    //print(".processRowSetEvent() - " + evt + ", ignoreEvents = " + ignoreEvents);
    if (ignoreEvents) return;

    switch(evt.getEventType()) {
        case RowSetEvent.ALL_ROWS_CHANGED:
            //print(" received ALL_ROWS_CHANGED");
            // ***populateItems(evt); // For testing
            rowSetReader = evt.getRowSetReader();
            model.fireTableDataChanged();
            // selectRow(0); // Poor visually ***********
            break;
        case RowSetEvent.CURRENT_ROW_INDEX_CHANGED:
            selectRow(evt.getCurrentIndex());
            break;
        case RowSetEvent.ROWS_INSERTED:
            model.fireTableRowsInserted(
                evt.getFirstIndexAffected(),
                evt.getLastIndexAffected());
            break;
        case RowSetEvent.ROWS_DELETED:
            model.fireTableRowsDeleted(
                evt.getFirstIndexAffected(),
                evt.getLastIndexAffected());
            break;
    }
}
public void requestFocus() {
    // Do nothing, may do something later
}
//---------- Private Methods -----------------------------
private void selectRow(int index) {
    //print(".selectRow() - index = " + index);
    if (getRowCount() == 0) return;

    // AVOID CAUSING CIRCULAR EVENT, causes ListSelectionEvent
    //print(".selectRow() - Setting row to " + index);
    ignoreEvents = true;
        table.setSelectedRow(index);
    ignoreEvents = false;
}
private void fireRowIndexActivated(int rowIndex) {
    if (controller != null) {
        EditEvent editEvent = new EditEvent(
            EditEvent.ROW_INDEX_ACTIVATED, dataSourceID);
        editEvent.setRowIndex(rowIndex);
        controller.processEditEvent(editEvent);
    }
}
private int getRowCount() {
    // *** return items.length; // For testing
    if (rowSetReader == null) {
        return 0;
    } else {
        return rowSetReader.getRowCount();
    }
}
// Returns -1 if none
private int getSelectedIndex() {
    if (getRowCount() == 0) return -1;

    int index = table.getSelectedRow();
    // Swing bug, index can be > items.size() - 1
    if (0 <= index && index <= getRowCount() - 1) {
        return index;
    } else {
        return -1; // Clicked on whitespace
    }
}
private void populateItems(RowSetEvent evt) {
    int rowCount = evt.getRowCount();
    items = new String[rowCount][table.getColumnCount()];
    for (int i = 0; i < rowCount; i++) {
        Row row = evt.getRowAt(i);
        for (int j = 0; j < columnIDs.length; j++) {
            String columnID = columnIDs[j];
            items[i][j] = (String)row.getValue(columnID);
        }
    }
    model.fireTableDataChanged();
}
// Line format: ColumnID, Display Text, columns
private void addColumnLine(int index, String line) {
    String[] data = DataLib.convertDelimStringToArray(line, ",");
    columnIDs[index] = data[0].trim();
    int width = Integer.valueOf(data[2].trim()).intValue();
    table.addColumn(index, width, data[1].trim());
}
//---------- Unit test
private void runUnitTest() {
    print(" - Please wait....");
    VisualLib.installLookAndFeel();
    // Prepare rowList
    RowListPanel rowList = new RowListPanel();
    rowList.setParam(getTestParam());
    // Populate
    rowList.populateTestItems();
    // Prepare frame
    Frame frame = new Frame("RowListPanel Unit Test");
    frame.setBackground(Color.lightGray);    frame.add("Center", rowList.getComponent());
    frame.pack();
    frame.setVisible(true);
}
// The spec for this class's Param
private Param getTestParam() {
    String text = "" +
    "DataSourceID is: Source1\n" +
    "Browse has:\n" +
    "    Height is: 200\n" +
    "    Columns hasLines:\n" +
    "        SysUser.LastName, Last Name, 20\n" +
    "        SysUser.FirstName, First Name, 15\n" +
    "        SysUser.UserID, UserID, 15\n" +
    "        SysUser.AdminCode, Admin Code, 15\n" +
    "        SysUser.LastLogon, Last Logon, 15\n" +
    "        End: Columns\n" +
    "    End: Browse\n";
    return ParamConverter.toParam(text);
}
private void populateTestItems() {
    int rowCount = 10;
    items = new String[rowCount][table.getColumnCount()];
    for (int i = 0; i < rowCount; i++) {
        Row row = createTestRow(i);
        for (int j = 0; j < columnIDs.length; j++) {
            String columnID = columnIDs[j];
            items[i][j] = (String)row.getValue(columnID);
        }
    }
    model.fireTableDataChanged();
}
private Row createTestRow(int rowIndex) {
    Row row = new Row();
    row.setValue("SysUser.MID", "" + rowIndex);
    row.setValue("SysUser.LastName", "Anderson" + rowIndex);
    row.setValue("SysUser.FirstName", "Martha");
    row.setValue("SysUser.UserID", "PPD445");
    row.setValue("SysUser.AdminCode", "ABCDE");
    row.setValue("SysUser.LastLogon", "3/23/1998");
    return row;
}
//--- Std
private static void print(String text) {
    System.out.println("RowListPanel" + text);
}
//========== Inner Classes ===============================
class Model extends BTableModel {
    //----- Abstract implementation  -----
    public int getRowCount() {
        // *** return items.length; // For testing
        if (rowSetReader == null) {
            return 0;
        } else {
            return rowSetReader.getRowCount();
        }
    }
    public Object getValueAt(int row, int col) {
        // *** return items[row][col]; // For testing
        return rowSetReader.getObject(row, columnIDs[col]);
    }
} // End inner class

} // End outer class
