//
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;

import javax.swing.AbstractListModel;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

/**
 * Copyright (c) 2008, Collin Fagan All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer. Redistributions in binary
 * form must reproduce the above copyright notice, this list of conditions and
 * the following disclaimer in the documentation and/or other materials provided
 * with the distribution. Neither the name of aberrantcode.blogspot.com nor the
 * names of its contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * 
 * Demo class is an experiment to show some uses for the closure proposal FCM
 * (http://www.jroller.com/scolebourne/date/20080224). It only works on a
 * special prototype branch of java available from https://kijaro.dev.java.net/.
 * 
 * @author Collin Fagan
 * @email my email service is gmail and my user name is collin.fagan
 */
public class FCMModelsDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		FCMModelsDemo ezMods = new FCMModelsDemo();
	}

	/**
	 * builds the demo on the EDT
	 */
	public FCMModelsDemo(){
		SwingUtilities.invokeLater(#startApp());
	}

	/**
	 * Date Renderer Component
	 */
	private SimpleDateFormat sdfDateColumn = new SimpleDateFormat(
			"MMMM-dd-yyyy");
	private JLabel dateRenderer = new JLabel();

	/**
	 * startup method
	 */
	private void startApp(){
		JFrame demoFrame = new JFrame();
		demoFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		demoFrame.setSize(600,400);
		
		demoFrame.setLayout(new BorderLayout());
		
		// the list of all superhero data
		List heroes = buildData();
		
		JPanel listExamples = new JPanel();
		listExamples.setLayout(new BoxLayout(listExamples, BoxLayout.Y_AXIS));
		
		// First Name List
		JList firstNameList = new JList();
		FCMFieldListModel listModel = new FCMFieldListModel(heroes, SuperHero#firstName);
		firstNameList.setModel(listModel);
		listExamples.add(addTitle("First Name", new JScrollPane(firstNameList)));
		
		// Last Name List
		JList lastNameList = new JList();
		FCMFieldListModel lastListModel = new FCMFieldListModel(heroes, SuperHero#lastName);
lastNameList.setModel(lastListModel);
listExamples.add(addTitle("Last Name", new JScrollPane(lastNameList)));

// Full Name List
JList fullNameList = new JList();
FCMMethodListModel fullListModel = new FCMMethodListModel(heroes, SuperHero#getFullName());
fullNameList.setModel(fullListModel);

demoFrame.add(listExamples, BorderLayout.WEST);
demoFrame.add(addTitle("Full Name", new JScrollPane(fullNameList)), BorderLayout.EAST);

// Superhero Table
JTable superHeroTable = new JTable();
FCMTableModel superTableModel = new FCMTableModel(heroes, SuperHero#getFirstName(), SuperHero#getLastName(), SuperHero#getHeroName(), SuperHero#getFirstAppearance());
superHeroTable.setModel(superTableModel);

dateRenderer.setOpaque(true);
dateRenderer.setFont(UIManager.getFont("Table.font"));
dateRenderer.setHorizontalAlignment(JLabel.CENTER);

TableColumn dateColumn = superHeroTable.getColumn(SuperHero#getFirstAppearance().getName());

// in-line cell renderer for the date column of the superhero table
dateColumn.setCellRenderer(#(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column){
if(isSelected){
dateRenderer.setBackground(UIManager.getColor("Table.selectionBackground"));
}else{
dateRenderer.setBackground(UIManager.getColor("Table.background"));
}
dateRenderer.setText(sdfDateColumn.format((Date) value));
return dateRenderer;
});

demoFrame.add(addTitle("Super Heroes", new JScrollPane(superHeroTable)));
demoFrame.setVisible(true);
}

/**
* Wraps the component in a JPanel with a titled border
*/
private JComponent addTitle(String title, JComponent target) {
JPanel borderPanel = new JPanel();
target.setPreferredSize(new Dimension(100,200));
borderPanel.setLayout(new BorderLayout());
borderPanel.setBorder(BorderFactory.createTitledBorder(title));
borderPanel.add(target);
return borderPanel;
}

/**
* @return builds the superhero data
*/
public List buildData() {
ArrayList data = new ArrayList();
data.add(new SuperHero("Steve", "Rogers", "Captian America", "3-1-1941"));
data.add(new SuperHero("Tony", "Stark", "Iron Man", "3-1-1963"));
data.add(new SuperHero("Peter", "Parker", "Spider-Man", "8-1-1962"));
data.add(new SuperHero("Reed", "Richards", "Mr. Fantastic", "11-1-1961"));
data.add(new SuperHero("Norrin", "Radd", "Silver Surfer", "3-1-1966"));
data.add(new SuperHero("Robbie", "Baldwin", "Speedball", "1-1-1988"));
data.add(new SuperHero("Luke", "Cage", "Power Man", "6-1-1972"));
data.add(new SuperHero("Vance", "Astro", "Captian Astro", "1-1-1988"));
return data;
}

/**
* Superhero - used as an example bean for this demonstration
*/
private static class SuperHero {

// fields should not normally be public, but these are exposed to
// demonstrate the FCMFieldListModel class
public String firstName;
public String lastName;
public String heroName;

private Date firstAppearance;
private SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy");

public SuperHero(String firstName, String lastName, String heroName,
String date) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.heroName = heroName;
try {
this.firstAppearance = sdf.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
}

public String getFullName() {
return firstName + " " + lastName;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getHeroName() {
return heroName;
}

public void setHeroName(String heroName) {
this.heroName = heroName;
}

public Date getFirstAppearance() {
return firstAppearance;
}

public void setFirstAppearance(Date firstAppearance) {
this.firstAppearance = firstAppearance;
}
}

/**
* Wraps a list of elements and exposes one field to the list model
* interface
*
* @param
*/
private class FCMFieldListModel extends AbstractListModel {

private List backingList;
private Field targetField;

public FCMFieldListModel(List data, Field target) {
backingList = data;
targetField = target;
}

@Override
public Object getElementAt(int index) {
try {
return targetField.get(backingList.get(index));
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}

@Override
public int getSize() {
return backingList.size();
}

}

/**
* Wraps a list of elements and exposes one method to the list model
* interface
*
* @param
*/
private class FCMMethodListModel extends AbstractListModel {

private List backingList;
private Method targetMethod;

public FCMMethodListModel(List data, Method target) {
backingList = data;
targetMethod = target;
}

@Override
public Object getElementAt(int index) {
try {
Object item = backingList.get(index);
return targetMethod.invoke(item);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}

@Override
public int getSize() {
return backingList.size();
}

}

/**
* Wraps a list of elements and a list of methods. The methods define the
* columns and the elements define the rows.
*
* @param
*/
private class FCMTableModel extends AbstractTableModel {

private List backingDataList;
private List methodList;

public FCMTableModel(List data, Method... getters) {
methodList = Arrays.asList(getters);
backingDataList = data;
}

@Override
public Class getColumnClass(int columnIndex) {
return methodList.get(columnIndex).getReturnType();
}

@Override
public String getColumnName(int column) {
return methodList.get(column).getName();
}

@Override
public int getColumnCount() {
return methodList.size();
}

@Override
public int getRowCount() {
return backingDataList.size();
}

@Override
public Object getValueAt(int rowIndex, int columnIndex) {
try {
return methodList.get(columnIndex).invoke(
backingDataList.get(rowIndex));
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
}