package jeql.workbench.ui.data;

import java.awt.Color;
import java.awt.Component;
import java.awt.SystemColor;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

import org.locationtech.jts.geom.Geometry;

import jeql.api.row.Row;
import jeql.api.row.RowSchema;
import jeql.monitor.MonitorRowList;
import jeql.style.StyleConstants;
import jeql.util.ClassUtil;
import jeql.util.ColorUtil;
import jeql.util.MethodUtil;
import jeql.util.SwingUtil;
import jeql.workbench.RowListGeometryList;
import jeql.workbench.Workbench;
import jeql.workbench.WorkbenchConstants;

class RowListTable extends JTable
{
  private static Map<String, RowListColumnStyle> colStyleMap = new HashMap<String, RowListColumnStyle>();
  
  private static RowListColumnStyle findColStyle(RowSchema schema)
  {
    String key = RowListColumnStyle.columnKey(schema);
    //System.out.println("Finding style for " + key);
    RowListColumnStyle cs = colStyleMap.get(key);
    if (cs == null) {
      cs = new RowListColumnStyle(key, schema);
      colStyleMap.put(key, cs);
      //System.out.println("Creating style...");
    }
    return cs;
  }
  
  private static final Color LIGHT_GRAY = new Color(220, 220, 220);

  private boolean useMonospacedFont = true;
  
  private boolean[] isColorColumn;
  
  private RowListColumnStyle colStyle;

  private int highlightRow = -1;
  
  public RowListTable(MonitorRowList mrl)
  {
    setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    // setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
    setModel(new RowListTableModel(mrl));
    int numCols = mrl.getSchema().size();
    isColorColumn = colorColumns(mrl.getSchema());
    init();
    
    // allow sorting if available in Java version
    MethodUtil.invokeVoidSafe(this, "setAutoCreateRowSorter", boolean.class, Boolean.TRUE);
    //setAutoCreateRowSorter(true);
  }

  public void setMonospaced(boolean useMonospacedFont)
  {
    this.useMonospacedFont = useMonospacedFont;
  }
  
  public void setHighlightRow(int row) {
    highlightRow = row;
  }
  
  public void clearHighlightRow() {
    highlightRow = -1;
  }
  
  private boolean isHighlightedRow(int row) {
    return row == highlightRow;
  }
  
  public int getSelectedIndex() {
    if (getSelectionModel().isSelectionEmpty()) 
      return -1;
    int focusRowIndex = getSelectionModel().getLeadSelectionIndex();
    return focusRowIndex;
  }
  
  public Row getSelectedRowValue() {
    if (getSelectionModel().isSelectionEmpty()) 
      return null;
    int selectedRowIndex = getSelectionModel().getLeadSelectionIndex();
    int modelRowIndex = convertRowIndexToModel(selectedRowIndex);
    return ((RowListTableModel) getModel()).getRow(modelRowIndex);

  }
  
  private static final int ROW_NUM_COLUMN_WIDTH = 60;
  
  private void init()
  {
    // getColumnModel().setColumnMargin(4);
    RowListTableModel model = (RowListTableModel) getModel();
    colStyle = findColStyle(model.getSchema());
    
    for (int i = 0; i < model.getColumnCount(); i++) {
      TableColumn col = getColumnModel().getColumn(i);

      // first column is row number
      if (i == 0) {
        col.setMinWidth(ROW_NUM_COLUMN_WIDTH);
        col.setPreferredWidth(ROW_NUM_COLUMN_WIDTH);
        col.setMaxWidth(ROW_NUM_COLUMN_WIDTH);
      }
      else if (i < model.getColumnCount()) {
      // don't set width for last column, to allow it to stretch
        //col.setPreferredWidth(100);
        col.setPreferredWidth(colStyle.width(i - 1));
        //System.out.print("col " + (i-1) + " to width " + colStyle.width(i - 1) + " -style " + colStyle.getKey());
      }
      col.setHeaderRenderer(new RowListTableHeaderRenderer(getTableHeader()
          .getDefaultRenderer()));
      if (model.getColumnClass(i) == Double.class) {
        col.setCellRenderer(new DoubleFormatRenderer());
      }
    }
    getColumnModel().addColumnModelListener(new TableColumnModelListener() {
      public void columnMarginChanged(ChangeEvent arg0) 
      {
        updateColStyle();
      }

      public void columnAdded(TableColumnModelEvent arg0)      { }
      public void columnMoved(TableColumnModelEvent arg0) {}
      public void columnRemoved(TableColumnModelEvent arg0) {}
      public void columnSelectionChanged(ListSelectionEvent arg0){}
    });
    
    addMouseListener(new java.awt.event.MouseAdapter() {
      @Override
      public void mouseClicked(java.awt.event.MouseEvent evt) {
        boolean isDblClick = evt.getClickCount() >= 2;
        boolean isCtlClick = SwingUtil.isCtlKeyPressed(evt);
        boolean isZoom = evt.isControlDown() && SwingUtilities.isRightMouseButton(evt);
        
        int row = rowAtPoint(evt.getPoint());
        int col = columnAtPoint(evt.getPoint());
        
        // skip row number column
        if (col <= 0) return;
        
        if (row >= 0 && col > 0) {
          int modelRow = convertRowIndexToModel(row);
           Object val = ((RowListTableModel) getModel()).getRawValueAt(modelRow, col);
           //System.out.println(val); 
           if (isDblClick) {
             Workbench.controller().inspect(val);
           }
           else if (isZoom) {
             Workbench.geomView().zoomObject(val);
           }
        }
      }
    });
    
    getSelectionModel().addListSelectionListener(new ListSelectionListener(){
      public void valueChanged(ListSelectionEvent event) {
        Workbench.controller().selectGeometryFromDataRow();
      }
    });

  }


  
  private void updateColStyle()
  {
    for (int i = 0; i < getModel().getColumnCount(); i++) {
      if (i > 0) {
        TableColumn col = getColumnModel().getColumn(i);
        colStyle.setWidth(i - 1, col.getPreferredWidth());
      }
    }
  }

//Implement table header tool tips.
  protected JTableHeader createDefaultTableHeader() {
      return new JTableHeader(columnModel) {
          public String getToolTipText(MouseEvent e) {
              String tip = null;
              java.awt.Point p = e.getPoint();
              int colIndex = columnModel.getColumnIndexAtX(p.x);
              if (colIndex < 0) return "";
              int realIndex = columnModel.getColumn(colIndex).getModelIndex();
              return ClassUtil.classname(getModel().getColumnClass(realIndex));
          }
      };
  }
  
  /**
   * Override <tt>prepareRenderer()</tt> to set the font size in the instances
   * of the cell renderers returned by the Look-And-Feel's renderer.
   */
  public Component prepareRenderer(TableCellRenderer renderer, int row,
      int col)
  {
    Component c = super.prepareRenderer(renderer, row, col);
    if (getModel().getColumnClass(col) == String.class && useMonospacedFont) 
      c.setFont(WorkbenchConstants.FONT_LUCIDA_CONSOLE);
    
    if (isHighlightedRow(row)) c.setBackground(Color.YELLOW);
    return c;
  }
  


  public TableCellRenderer getCellRenderer(int row, int col)
  {
    // System.out.println(" getCellRenderer  " + row + ", " + column);
    JLabel renderer = (JLabel) super.getCellRenderer(row, col);
    
    int modelRow = convertRowIndexToModel(row);
    //-------- Row striping
    if (col == 0) {
      // row number column
      renderer.setBackground(SystemColor.control);
    }
    else {
      // data column
      renderer.setBackground(((row % 2) == 0) ? Color.white : LIGHT_GRAY);
    }
    
    //--------- Show repeated column values
    if ( ((RowListTableModel) getModel()).isShowSpaces()
        && isRepeatedColumnValue(row, col)) {
      renderer.setBackground(Color.GRAY);
    }
    
    // show strings in different colour
    if (getModel().getColumnClass(col) == String.class) {
      renderer.setForeground(WorkbenchConstants.CLR_DARK_BLUE);
    }
    else if (getModel().getColumnClass(col) == Geometry.class) {
      renderer.setForeground(WorkbenchConstants.CLR_DARK_GREEN);
    }
    if (col > 0 && isColorColumn[col - 1]) {
      Color clr = ColorUtil.RGBAtoColor((String) getModel().getValueAt(modelRow, col));
      if (clr == null) {
        clr = Color.white;
      }
      renderer.setBackground(clr);
      renderer.setForeground(readableForeground(clr));
    }

    return (TableCellRenderer) renderer;
  }
  
  private boolean isRepeatedColumnValue(int row, int col)
  {
    if (row == 0) return false;
    Object o = getModel().getValueAt(row, col);
    if (o == null) return false;
    
    Object oprev = getModel().getValueAt(row-1, col);
    if (o.equals(oprev))
      return true;
    return false;
  }
  
  private static Color readableForeground(Color background)
  {
    //float[] hsv = ColorUtil.toHSV(background);
    //boolean isDark = hsv[1] > 0.8 && hsv[2] < 0.5;
    boolean isDark = background.getRed() + background.getGreen() + background.getBlue() > 384;  // = 3 * 128
    if (isDark) return Color.BLACK;
    return Color.WHITE;
  }
  
  private static boolean[] colorColumns(RowSchema schema)
  {
    int numCols = schema.size();
    boolean[] isColorColumn = new boolean[numCols];
    for (int i = 0; i < numCols; i++) {
      setColorCol(StyleConstants.COLOR, schema, isColorColumn);
      setColorCol(StyleConstants.STROKE, schema, isColorColumn);
      setColorCol(StyleConstants.FONT_COLOR, schema, isColorColumn);
      setColorCol(StyleConstants.LABEL_COLOR, schema, isColorColumn);
      setColorCol(StyleConstants.HALO_COLOR, schema, isColorColumn);
      setColorCol(StyleConstants.FILL, schema, isColorColumn);
      setColorCol(StyleConstants.COL_MARKER_COLOR, schema, isColorColumn);
    }
    return isColorColumn;
  }
  
  private static void setColorCol(String clrColName, RowSchema schema, boolean[] isColorColumn)
  {
    int i = schema.getColIndex(clrColName);
    if (i < 0) return;
    isColorColumn[i] = true;

  }
}

class RowListTableHeaderRenderer implements TableCellRenderer
{
  TableCellRenderer originalRenderer;

  RowListTableHeaderRenderer(TableCellRenderer originalRenderer)
  {
    this.originalRenderer = originalRenderer;
  }

  // This method is called each time a column header
  // using this renderer needs to be rendered.

  public Component getTableCellRendererComponent(JTable table, Object value,
      boolean isSelected, boolean hasFocus, int row, int col)
  {
    JLabel label = (JLabel) originalRenderer.getTableCellRendererComponent(
        table, value, isSelected, hasFocus, row, col);

    // Configure the component with the specified value
    label.setText(value.toString());

    // Set tool tip if desired
    // setToolTipText((String)value);
    label.setBackground(SystemColor.control);

    // setForeground(SystemColor.control);
    // Since the renderer is a component, return itself
    return label;
  }

  // The following methods override the defaults for performance reasons
  public void validate()
  {
  }

  public void revalidate()
  {
  }

  
  protected void firePropertyChange(String propertyName, Object oldValue,
      Object newValue)
  {
  }

  public void firePropertyChange(String propertyName, boolean oldValue,
      boolean newValue)
  {
  }
  
}