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

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

import javax.swing.JComponent;
import javax.swing.JFormattedTextField.AbstractFormatter;
import javax.swing.JFrame;
import javax.swing.JLabel;
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.TableModel;

import org.jdesktop.swingx.renderer.DefaultTableRenderer;
import org.jdesktop.swingx.renderer.StringValue;
import org.jdesktop.swingx.renderer.StringValues;

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.ColumnFilter;
import de.javasoft.swing.filter.ColumnFilterFactories.ComparableColumnFilterFactory;
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;

/**
 * Demonstrates how to add custom filter components.
 */
@SuppressWarnings("serial")
public class CustomFilterComponent extends JFrame
{
  public CustomFilterComponent()
  {
    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);
    
    //set initial filter value
    //TableColumnExt col = table.getColumnExt(4);
    //table.getFilterRowController().getColumnFilter(col).getRowFilterModel().setMatchValue(true);
//@/highlight
    
//@highlight
    //formatter used for renderer and filtering component
    AbstractFormatter formatter = new TimeFormatter();
    table.getColumn(5).setCellRenderer(new DefaultTableRenderer(new FormatterStringValue(formatter), JLabel.TRAILING));    
    IColumnFilterFactory<TableModel> timeColumnFactory = new ComparableColumnFilterFactory<TableModel>(formatter);
    ((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 CustomFilterComponent();
        }
        catch (Exception e)
        {
          e.printStackTrace();
        }
      }
    });
  }
  
  //
  //FormatterStringValue - implementation of StringValue
  //
  
  public static class FormatterStringValue implements StringValue
  {
    private AbstractFormatter formatter;

    public FormatterStringValue(AbstractFormatter formatter)
    {
      this.formatter = formatter;
    }

    public String getString(Object value)
    {
      try
      {
        String string = formatter.valueToString(value);
        return string != null ? string : "";
      }
      catch (ParseException e)
      {
        // ignore, not much we can do
      }
      return StringValues.TO_STRING.getString(value);
    }
  }

  //
  //TimeFormatter - custom formatter for columnFilter and renderer
  //
  
  private static class TimeFormatter extends 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 ColumnFilter<M>
  {
    private JYCheckBox filterField;
    private ChangeListener changeListener;

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

    @Override
    protected JComponent createFilteringComponent()
    {
      filterField = new JYCheckBox();
      filterField.setOpaque(false);
      filterField.setState(State.HALF_SELECTED);
      changeListener = createChangeListener();
      filterField.addChangeListener(changeListener);
      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));
      panel.add(filterField);
      return panel;
    }

    private ChangeListener createChangeListener()
    {
      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);
    }

    @Override
    protected void matchValueChanged(Object oldValue, Object newValue)
    {
      if (newValue != null && newValue.equals(oldValue)) 
        return;
      filterField.setState(newValue == null ? State.HALF_SELECTED : (Boolean)newValue ? State.SELECTED : State.DESELECTED);
    }

    @Override
    protected void doRelease() 
    {
      //remove the installed listeners
      filterField.removeChangeListener(changeListener);
    }    
  }
  
}
