package org.openswing.swing.table.client;

import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.basic.*;
import javax.swing.table.*;
import java.awt.event.MouseEvent;


/**
 * <p>Title: OpenSwing Framework</p>
 * <p>Description: Grid UI, redefined to allow cells span.</p>
 * <p>Copyright: Copyright (C) 2006 Mauro Carniel</p>
 *
 * <p> This file is part of OpenSwing Framework.
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the (LGPL) Lesser General Public
 * License as published by the Free Software Foundation;
 *
 *                GNU LESSER GENERAL PUBLIC LICENSE
 *                 Version 2.1, February 1999
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *       The author may be contacted at:
 *           maurocarniel@tin.it</p>
 *
 * @author Mauro Carniel
 * @version 1.0
 */
public class GridUI extends BasicTableUI {


  /** Paint a representation of the <code>table</code> instance
   * that was set in installUI().
   */
  public void paint(Graphics g, JComponent c) {
      if (table.getRowCount() <= 0 || table.getColumnCount() <= 0) {
          return;
      }
      Rectangle clip = g.getClipBounds();
      Point upperLeft = clip.getLocation();
      Point lowerRight = new Point(clip.x + clip.width - 1, clip.y + clip.height - 1);

      if (((Grid)table).hasCellSpan()) {
        // no cells span defined: it is needed to split visible area in more regions, according to cells span:
        int rMin = table.rowAtPoint(upperLeft);
        // This should never happen.
        if (rMin == -1) {
            rMin = 0;
        }
        int rMax = rMin;
        int h = 0;
        for(int i=rMin;i<table.getRowCount();i++)
          if (h<lowerRight.y) {
            h += table.getRowHeight(i);
            rMax = i;
          }
          else
            break;

        // If the table does not have enough rows to fill the view we'll get -1.
        // Replace this with the index of the last row.
        if (rMax == -1) {
            rMax = table.getRowCount()-1;
        }

        boolean ltr = table.getComponentOrientation().isLeftToRight();
        int cMin = table.columnAtPoint(ltr ? upperLeft : lowerRight);
        // This should never happen.
        if (cMin == -1) {
            cMin = 0;
        }

        int cMax = cMin;
        int w = 0;
        for(int i=cMin;i<table.getColumnCount();i++)
          if (w<lowerRight.x) {
            w += table.getColumnModel().getColumn(i).getWidth();
            cMax = i;
          }
          else
            break;

        // If the table does not have enough columns to fill the view we'll get -1.
        // Replace this with the index of the last column.
        if (cMax == -1) {
            cMax = table.getColumnCount()-1;
        }

        // Paint the grid.
        paintGrid(g, rMin, rMax, cMin, cMax);

        // Paint the cells.
        paintCells(g, rMin, rMax, cMin, cMax);

      }
      else {
        // no cells span defined: it is possible to identify a unique visible area for the whole grid:
        int rMin = table.rowAtPoint(upperLeft);
        int rMax = table.rowAtPoint(lowerRight);
        // This should never happen.
        if (rMin == -1) {
            rMin = 0;
        }
        // If the table does not have enough rows to fill the view we'll get -1.
        // Replace this with the index of the last row.
        if (rMax == -1) {
            rMax = table.getRowCount()-1;
        }

        boolean ltr = table.getComponentOrientation().isLeftToRight();
        int cMin = table.columnAtPoint(ltr ? upperLeft : lowerRight);
        int cMax = table.columnAtPoint(ltr ? lowerRight : upperLeft);
        // This should never happen.
        if (cMin == -1) {
            cMin = 0;
        }
        // If the table does not have enough columns to fill the view we'll get -1.
        // Replace this with the index of the last column.
        if (cMax == -1) {
            cMax = table.getColumnCount()-1;
        }

        // Paint the grid.
        paintGrid(g, rMin, rMax, cMin, cMax);

        // Paint the cells.
        paintCells(g, rMin, rMax, cMin, cMax);
      }

  }

  /*
   * Paints the grid lines within <I>aRect</I>, using the grid
   * color set with <I>setGridColor</I>. Paints vertical lines
   * if <code>getShowVerticalLines()</code> returns true and paints
   * horizontal lines if <code>getShowHorizontalLines()</code>
   * returns true.
   */
  private void paintGrid(Graphics g, int rMin, int rMax, int cMin, int cMax) {
      g.setColor(table.getGridColor());
      TableColumnModel cm = table.getColumnModel();
      int tableWidth;
      int tableHeight;
      Rectangle cell = null;

      for (int col = cMin; col <= cMax; col++) {

        if (table.getShowHorizontalLines()) {
          for (int row = rMin; row <= rMax; row++) {
            tableWidth = cm.getColumn(col).getWidth();
            cell = table.getCellRect(row, col, true);
            for (int i=col+1;i<col+((Grid)table).getSpan(row,col).getN2();i++) {
              tableWidth += cm.getColumn(i).getWidth();
            }

            g.drawLine(cell.x, cell.y - 1, cell.x+tableWidth - 1, cell.y - 1);
          }
        }
        if (table.getShowVerticalLines()) {
          for (int row = rMin; row <= rMax; row++) {
            cell = table.getCellRect(row, col, true);
            tableHeight = cell.y+cell.height;
            for (int i=col+1;i<col+((Grid)table).getSpan(row,col).getN2();i++) {
              cell.x += cm.getColumn(i).getWidth();
            }
            g.drawLine(cell.x - 1, cell.y, cell.x - 1, tableHeight - 1);
          }
        }

      }
  }



  private void paintCells(Graphics g, int rMin, int rMax, int cMin, int cMax) {
      JTableHeader header = table.getTableHeader();
      TableColumn draggedColumn = (header == null) ? null : header.getDraggedColumn();

      TableColumnModel cm = table.getColumnModel();
      Rectangle cellRect;
      TableColumn aColumn;
      for(int row = rMin; row <= rMax; row++) {
          for(int column = cMin; column <= cMax; column++) {
              cellRect = table.getCellRect(row, column, false);
//
              aColumn = cm.getColumn(column);
//              columnWidth = aColumn.getWidth();
//              cellRect.width = columnWidth - columnMargin;
              if (aColumn != draggedColumn) {
                  paintCell(g, cellRect, row, column);
              }
//              cellRect.x += columnWidth;
          }
      }


      // Paint the dragged column if we are dragging.
      if (draggedColumn != null) {
          paintDraggedArea(g, rMin, rMax, draggedColumn, header.getDraggedDistance());
      }

      // Remove any renderers that may be left in the rendererPane.
      rendererPane.removeAll();
  }



  private void paintDraggedArea(Graphics g, int rMin, int rMax, TableColumn draggedColumn, int distance) {
      int draggedColumnIndex = viewIndexForColumn(draggedColumn);

      Rectangle minCell = table.getCellRect(rMin, draggedColumnIndex, true);
      Rectangle maxCell = table.getCellRect(rMax, draggedColumnIndex, true);

      Rectangle vacatedColumnRect = minCell.union(maxCell);

      // Paint a gray well in place of the moving column.
      g.setColor(table.getParent().getBackground());
      g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
                 vacatedColumnRect.width, vacatedColumnRect.height);

      // Move to the where the cell has been dragged.
      vacatedColumnRect.x += distance;

      // Fill the background.
      g.setColor(table.getBackground());
      g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
                 vacatedColumnRect.width, vacatedColumnRect.height);

      // Paint the vertical grid lines if necessary.
      if (table.getShowVerticalLines()) {
          g.setColor(table.getGridColor());
          int x1 = vacatedColumnRect.x;
          int y1 = vacatedColumnRect.y;
          int x2 = x1 + vacatedColumnRect.width - 1;
          int y2 = y1 + vacatedColumnRect.height - 1;
          // Left
          g.drawLine(x1-1, y1, x1-1, y2);
          // Right
          g.drawLine(x2, y1, x2, y2);
      }

      for(int row = rMin; row <= rMax; row++) {
          // Render the cell value
          Rectangle r = table.getCellRect(row, draggedColumnIndex, false);
          r.x += distance;
          paintCell(g, r, row, draggedColumnIndex);

          // Paint the (lower) horizontal grid line if necessary.
          if (table.getShowHorizontalLines()) {
              g.setColor(table.getGridColor());
              Rectangle rcr = table.getCellRect(row, draggedColumnIndex, true);
              rcr.x += distance;
              int x1 = rcr.x;
              int y1 = rcr.y;
              int x2 = x1 + rcr.width - 1;
              int y2 = y1 + rcr.height - 1;

              if (((Grid)table).isVisible(row,draggedColumnIndex))
                g.drawLine(x1, y2, x2, y2);
          }
      }
  }


  private void paintCell(Graphics g, Rectangle cellRect, int row, int column) {

      if (!((Grid)table).isVisible(row,column)) {
        return;
      }


      if (table.isEditing() && table.getEditingRow()==row &&
                               table.getEditingColumn()==column) {
          Component component = table.getEditorComponent();
          component.setBounds(cellRect);
          component.validate();
      }
      else {
          TableCellRenderer renderer = table.getCellRenderer(row, column);
          Component component = table.prepareRenderer(renderer, row, column);
          rendererPane.paintComponent(g, component, table, cellRect.x, cellRect.y,
                                      cellRect.width, cellRect.height, true);
      }
  }


  private int viewIndexForColumn(TableColumn aColumn) {
      TableColumnModel cm = table.getColumnModel();
      for (int column = 0; column < cm.getColumnCount(); column++) {
          if (cm.getColumn(column) == aColumn) {
              return column;
          }
      }
      return -1;
  }


}
