package de.javasoft.synthetica.democenter.examples.jytable;

import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;

import javax.swing.JComponent;
import javax.swing.JFormattedTextField.AbstractFormatter;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.text.DefaultFormatterFactory;

import de.javasoft.swing.JYCheckBox;
import de.javasoft.swing.JYCheckBox.State;
import de.javasoft.swing.JYTable;
import de.javasoft.swing.JYTableHeader;
import de.javasoft.swing.JYTableScrollPane;
import de.javasoft.swing.filter.AbstractComparableColumnFilter;
import de.javasoft.swing.filter.ColumnFilter;
import de.javasoft.swing.filter.ComparableColumnFilter;
import de.javasoft.swing.filter.FilterContainer;
import de.javasoft.swing.filter.IColumnFilterFactory;
import de.javasoft.swing.jytable.JYTableColumnExt;
import de.javasoft.swing.jytable.renderer.CellLayoutHint;
import de.javasoft.swing.sort.RowFilters;

/**
 * Demonstrates how to use a JYTable.
 */
@SuppressWarnings("serial")
public class CustomTableFilters extends JFrame
{
  public CustomTableFilters()
  {
    super("Custom Table Filters");
    createAndAddComponents(getContentPane());

    //setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);      
    setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    setSize(700,300);
    setLocationRelativeTo(null);
    setVisible(true);
  }

  /**
   * Create components and add them to the container.
   */
  private void createAndAddComponents(Container container)
  {
    String[] columnNames = {"First Name", "Last Name", "Sport", "# of Years", "Active", "Best run time"};
    Object[][] data = { 
                        {"Kathy", "Smith", "Snowboarding", 5, false, 58000},
                        {"John", "Doe", "Walking", 3, true, 68000}, 
                        {"Sue", "Black", "Jogging", 2, false, 61000},
                        {"Jane", "White", "Speed reading", 20, false, 120000}, 
                        {"Joe", "Brown", "Climbing", 10, true, 65000},
                        {"Peter", "Parker", "Broad jumping", 3, true, 52000}, 
                      };
    final Class<?>[] columnClasses = {String.class, String.class, String.class, Integer.class, Boolean.class, Integer.class}; 

    DefaultTableModel model = new DefaultTableModel(data, columnNames)
    {
      @Override
      public Class<?> getColumnClass(int columnIndex)
      {
        return columnClasses[columnIndex];
      }
    };
    JYTable table = new JYTable(model);

//@highlight
    IColumnFilterFactory<TableModel> activeColumnFactory = new IColumnFilterFactory<TableModel>()
    {
      public ColumnFilter<TableModel> createColumnFilter(int tableColumn)
      {
        return new TriStateColumnFilter<TableModel>(tableColumn);
      }
    };
    ((JYTableColumnExt) table.getColumn(4)).setColumnFilterFactory(activeColumnFactory);
//@/highlight
    
//@highlight
    table.getColumn(5).setCellRenderer(new TimeRenderer(table.getDefaultRenderer(Object.class)));
    
    IColumnFilterFactory<TableModel> timeColumnFactory = new IColumnFilterFactory<TableModel>()
    {
      public ColumnFilter<TableModel> createColumnFilter(int tableColumn)
      {
        return new TimeColumnFilter<TableModel>(tableColumn);
      }
    };
    ((JYTableColumnExt) table.getColumn(5)).setColumnFilterFactory(timeColumnFactory);
//@/highlight
    
    JYTableHeader header =  (JYTableHeader)table.getTableHeader();
    CellLayoutHint hint = header.getCellLayoutHint();
    //center header text
    header.setCellLayoutHint(new CellLayoutHint(hint.sortMarkerPosition, SwingConstants.CENTER, hint.verticalAlignment));
    //use a JYTableScrollPane for the filter row
    JYTableScrollPane scrollPane = new JYTableScrollPane(table);
    container.add(scrollPane);    
  }

  
  /**
   * Static main method for application startup. 
   */
  public static void main(String[] args)
  {
    EventQueue.invokeLater(new Runnable()
    {
      public void run()
      {
        try
        {
          UIManager.setLookAndFeel("de.javasoft.plaf.synthetica.SyntheticaStandardLookAndFeel");
          new CustomTableFilters();
        }
        catch (Exception e)
        {
          e.printStackTrace();
        }
      }
    });
  }
  
  
  //
  //TimeRenderer
  //
  
  private static class TimeRenderer implements TableCellRenderer
  {
    private TableCellRenderer delegate;
    Calendar cal;
    
    public TimeRenderer(TableCellRenderer defaultRenderer)
    {
      this.delegate = defaultRenderer;
      cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    }

    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
    {
      JLabel l = (JLabel)delegate.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
      cal.setTimeInMillis(value instanceof Integer ? (Integer)value : 0);
      if (cal.get(Calendar.HOUR_OF_DAY) == 0)
        l.setText(String.format("%tM:%tS", cal, cal));
      else
        l.setText(String.format("%tT", cal));
      return l;
    }    
  } 
  
  
  //
  //TimeColumn Filter - customized formatting
  //
  
  private static class TimeColumnFilter<M> extends ComparableColumnFilter<M>
  {
    public TimeColumnFilter(int columnIndex)
    {
      super(columnIndex);
      setFormatterFactory(new DefaultFormatterFactory(new AbstractFormatter()
      {        
        @Override
        public String valueToString(Object value) throws ParseException
        {
          if (value == null)
            return null;
          Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
          cal.setTimeInMillis(value instanceof Integer ? (Integer)value : 0);
          return cal.get(Calendar.HOUR_OF_DAY) == 0 ? String.format("%tM:%tS", cal, cal) : String.format("%tT", cal);
        }
        
        @Override
        public Object stringToValue(String text) throws ParseException
        {
          //fault tolerant implementation
          int hours = 0;
          int minutes = 0;
          int seconds = 0;
          try
          {
            if (text.contains(":"))
            {
              String[] s = text.split(":");
              if (s.length > 2)
              {  
                hours = Integer.parseInt(s[0]);
                minutes = Integer.parseInt(s[1]);
                seconds = Integer.parseInt(s[2]);
              }                  
              else
              {  
                minutes = Integer.parseInt(s[0]);
                if (s.length > 1)
                  seconds = Integer.parseInt(s[1]);
              }  
            }
            else
              minutes = Integer.parseInt(text);
          }
          catch (NumberFormatException e)
          {
          }
          return (hours*3600+minutes*60+seconds)*1000;
        }
      }));    
    }
  }
  
  //
  //JYCheckBox Filter - custom filter component
  //  

  public class TriStateColumnFilter<M> extends AbstractComparableColumnFilter<M>
  {
    private JYCheckBox filterField;

    public TriStateColumnFilter(int tableColumn)
    {
      super(tableColumn);
    }

    @Override
    public void clear()
    {
      if (getLifeCycle() != LifeCycle.ACTIVATED)
        return;
      filterField.setState(State.HALF_SELECTED);
    }

    @Override
    protected JComponent createFilteringComponent()
    {
      filterField = new JYCheckBox();
      filterField.setOpaque(false);
      filterField.setState(State.HALF_SELECTED);
      filterField.addChangeListener(createCheckBoxListener());
      filterField.getActionMap().put("reset-key", createClearAction());
      filterField.getInputMap().put(KeyStroke.getKeyStroke("ESCAPE"), "reset-key");

      JComponent panel = new FilterContainer();
      panel.setLayout(new FlowLayout(FlowLayout.CENTER,0,0));
      JComponent mrc = getMatchRuleComponent();
      mrc.setVisible(false);
      panel.add(mrc);
      panel.add(filterField);
      return panel;
    }

    private ChangeListener createCheckBoxListener()
    {
      return new ChangeListener()
      {
        public void stateChanged(ChangeEvent e)
        {
          matchValueChanged();
        }
      };
    }

    @Override
    protected void matchValueChanged()
    {
      Boolean value = null;
      JYCheckBox.State state = filterField.getState();
      if (state != State.HALF_SELECTED)
        value = state == State.SELECTED ? true : false;
      getRowFilterModel().setMatchValue(value);
    }

    //Overridden to limit the match rules to type ComparisonTypeExt.
    @Override
    protected List<?> getMatchRules()
    {
      List<Object> list = new ArrayList<Object>();
      list.add(RowFilters.ComparisonTypeExt.EQUAL);
      return list;
    }
  }
  
}
