/*
 * The tracker package defines a set of video/image analysis tools
 * built on the Open Source Physics framework by Wolfgang Christian.
 *
 * Copyright (c) 2019  Douglas Brown
 *
 * Tracker is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Tracker 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Tracker; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
 * or view the license online at <http://www.gnu.org/copyleft/gpl.html>
 *
 * For additional Tracker information and documentation, please see
 * <http://physlets.org/tracker/>.
 */
package org.opensourcephysics.cabrillo.tracker;

import java.util.*;
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;

import org.opensourcephysics.display.ResizableIcon;
import org.opensourcephysics.media.core.IntegerField;
import org.opensourcephysics.tools.FontSizer;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;

/**
 * This displays and sets point attachments.
 *
 * @author Douglas Brown
 */
public class AttachmentDialog extends JDialog
    implements PropertyChangeListener {
	

  // instance fields
  protected int trackID;
  protected TrackerPanel trackerPanel;
  protected boolean isVisible;
  protected JButton closeButton, helpButton;
  protected ArrayList<PointMass> masses;
  protected JTable table;
	protected int cellheight = 28; // depends on font level
  protected JComboBox rendererDropdown, editorDropdown, measuringToolDropdown;
  protected PointMass dummyMass;
  protected Icon dummyIcon = new ShapeIcon(null, 21, 16);
  protected JScrollPane scrollPane;
  protected AttachmentCellRenderer attachmentCellRenderer = new AttachmentCellRenderer();
  protected TTrackRenderer trackRenderer = new TTrackRenderer();
  protected JPanel attachmentsPanel, circleFitterPanel, circleFitterStartStopPanel;
  protected JRadioButton stepsButton, tracksButton;
  protected JCheckBox relativeCheckbox;
  protected IntegerField startField, countField;
  protected JLabel startLabel, countLabel;
  protected boolean refreshing;

  
  /**
   * Constructs an AttachmentDialog.
   *
   * @param track the measuring tool
   */
  public AttachmentDialog(TTrack track) {
    super(JOptionPane.getFrameForComponent(track.trackerPanel), false);
  	trackerPanel = track.trackerPanel;
    createGUI();
    setMeasuringTool(track);
		refreshDropdowns();
    trackerPanel.addPropertyChangeListener("track", this); //$NON-NLS-1$
    trackerPanel.addPropertyChangeListener("selectedtrack", this); //$NON-NLS-1$
    trackerPanel.addPropertyChangeListener("clear", this); //$NON-NLS-1$
    TFrame frame = trackerPanel.getTFrame();
    frame.addPropertyChangeListener("tab", this); //$NON-NLS-1$
    refreshGUI();
  }

  /**
   * Responds to property change events.
   *
   * @param e the property change event
   */
  public void propertyChange(PropertyChangeEvent e) {
    if (e.getPropertyName().equals("tab")) { //$NON-NLS-1$
      if (trackerPanel != null && e.getNewValue() == trackerPanel) {
        setVisible(isVisible);
      }
      else {
        boolean vis = isVisible;
        setVisible(false);
        isVisible = vis;
      }
    }
    else if (e.getPropertyName().equals("track")) { //$NON-NLS-1$
    	TTrack deleted = (TTrack)e.getOldValue();
    	if (deleted!=null) {
    		deleted.removePropertyChangeListener("name", this); //$NON-NLS-1$
    		deleted.removePropertyChangeListener("color", this); //$NON-NLS-1$
    		deleted.removePropertyChangeListener("footprint", this); //$NON-NLS-1$
    		TTrack measuringTool = TTrack.getTrack(trackID);
    		if (measuringTool!=null) {
    			if (measuringTool!=deleted) {
		    		TTrack[] attachments = measuringTool.getAttachments();
			     	for (int i = 0; i < attachments.length; i++) {
					  	if (deleted==attachments[i] || deleted==measuringTool) {
					  		attachments[i] = null;	  		
					  	}
			    	}
			    	measuringTool.refreshAttachments();
	    		}
    			else { // measuring tool has been deleted
    				trackID = 0;
    			}
    		}
    	}
  		refreshDropdowns();
    	refreshGUI();
    }
    else if (e.getPropertyName().equals("clear")) { //$NON-NLS-1$
    	for (Integer n: TTrack.activeTracks.keySet()) {
    		TTrack next = TTrack.activeTracks.get(n);
    		next.removePropertyChangeListener("name", this); //$NON-NLS-1$
    		next.removePropertyChangeListener("color", this); //$NON-NLS-1$
    		next.removePropertyChangeListener("footprint", this); //$NON-NLS-1$
    	}
  		refreshDropdowns();
    	refreshGUI();
    }
    else if (e.getPropertyName().equals("selectedtrack") && e.getNewValue()!=null) { //$NON-NLS-1$
      TTrack track = (TTrack)e.getNewValue();
      for (int i=0; i<measuringToolDropdown.getItemCount(); i++) {
      	if (track==measuringToolDropdown.getItemAt(i)) {
      		measuringToolDropdown.setSelectedIndex(i);
      		break;
      	}
      }
    }
//    else if (e.getPropertyName().equals("step") //$NON-NLS-1$
//    		|| e.getPropertyName().equals("steps")) { //$NON-NLS-1$
//    	measuringTool.refreshAttachments();
//    }
    else if (e.getPropertyName().equals("dataPoint")) { //$NON-NLS-1$
  		TTrack measuringTool = TTrack.getTrack(trackID);
    	measuringTool.refreshAttachments();
      DefaultTableModel dm = (DefaultTableModel)table.getModel();
      dm.fireTableDataChanged();
    }
    else refreshGUI();
  }

  /**
   * Overrides JDialog setVisible method.
   *
   * @param vis true to show this inspector
   */
  public void setVisible(boolean vis) {
    super.setVisible(vis);
    isVisible = vis;
  }

  /**
   * Disposes of this dialog.
   */
  public void dispose() {
    if (trackerPanel != null) {
      trackerPanel.removePropertyChangeListener("track", this); //$NON-NLS-1$
      trackerPanel.removePropertyChangeListener("selectedtrack", this); //$NON-NLS-1$
      trackerPanel.removePropertyChangeListener("clear", this); //$NON-NLS-1$
      for (TTrack p: masses) {
        p.removePropertyChangeListener("name", this); //$NON-NLS-1$
        p.removePropertyChangeListener("color", this); //$NON-NLS-1$
        p.removePropertyChangeListener("footprint", this); //$NON-NLS-1$
      }
      masses.clear();
      dummyMass.delete();
      dummyMass = null;
  		TTrack measuringTool = TTrack.getTrack(trackID);
    	if (measuringTool!=null) {
  	    measuringTool.removePropertyChangeListener("dataPoint", this); //$NON-NLS-1$  		
    	}
      TFrame frame = trackerPanel.getTFrame();
      if (frame != null) {
        frame.removePropertyChangeListener("tab", this); //$NON-NLS-1$
      }
    	trackerPanel.attachmentDialog = null;
      trackerPanel = null;
    }
    super.dispose();
  }

//_____________________________ private methods ____________________________

  /**
   * Creates the visible components of this panel.
   */
  private void createGUI() {
//  	setResizable(false);
    // create GUI components
    JPanel contentPane = new JPanel(new BorderLayout());
    setContentPane(contentPane);
    
    // put attachments panel in content pane CENTER
    attachmentsPanel = new JPanel(new BorderLayout());
    contentPane.add(attachmentsPanel, BorderLayout.CENTER);
    
    
    // put measuring tool dropdown in attachments panel NORTH
    JPanel north = new JPanel();
    north.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0));
    measuringToolDropdown = new JComboBox();
    measuringToolDropdown.setRenderer(trackRenderer);
    measuringToolDropdown.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        TTrack tool = (TTrack)measuringToolDropdown.getSelectedItem();
    		TTrack measuringTool = TTrack.getTrack(trackID);
      	if (tool==measuringTool) return;
        setMeasuringTool(tool);
      }
    });
    north.add(measuringToolDropdown);
    attachmentsPanel.add(north, BorderLayout.NORTH);
        
    dummyMass = new PointMass();
    		
    rendererDropdown = new JComboBox(new AttachmentComboBoxModel());
    rendererDropdown.setRenderer(trackRenderer);
    editorDropdown = new JComboBox(new AttachmentComboBoxModel());
    editorDropdown.setRenderer(trackRenderer);

    table = new JTable(new AttachmentTableModel()) {
    	@Override
    	public void setFont(Font font) {
    		super.setFont(font);
	      cellheight = font.getSize()+16;
    		setRowHeight(cellheight);
    		int w = (int)(60*(1+FontSizer.getLevel()*0.3));
	      getColumnModel().getColumn(0).setPreferredWidth(w);
	      getColumnModel().getColumn(1).setPreferredWidth(2*w);
    		getTableHeader().setPreferredSize(new Dimension(w, cellheight));
      }
    };
    attachmentCellRenderer = new AttachmentCellRenderer();
    table.setDefaultRenderer(PointMass.class, attachmentCellRenderer);
    table.setDefaultRenderer(String.class, attachmentCellRenderer);
    table.setRowHeight(cellheight);
    
    TableCellEditor editor = new AttachmentCellEditor();
    table.getColumnModel().getColumn(1).setCellEditor(editor);
    scrollPane = new JScrollPane(table) {
    	public Dimension getPreferredSize() {
    		Dimension dim = super.getPreferredSize();
    		int cellCount = Math.max(4, table.getRowCount()+1);
    		cellCount = Math.min(10, cellCount);
    		dim.height = cellCount*cellheight+8;
    		dim.width= table.getPreferredSize().width+20;
    		return dim;
    	}
    };

    // put table in attachments panel CENTER
    JPanel center = new JPanel(new GridLayout(1,1));
    attachmentsPanel.add(center, BorderLayout.CENTER);
    center.add(scrollPane);
    center.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
   
    // steps and tracks buttons
    stepsButton = new JRadioButton();
    tracksButton = new JRadioButton();
    Action tracksOrStepsAction = new AbstractAction() {
			@Override
			public void actionPerformed(ActionEvent e) {
      	if (refreshing) return;
      	CircleFitter fitter = (CircleFitter)TTrack.getTrack(trackID);
      	fitter.attachToSteps = !tracksButton.isSelected();
	    	fitter.refreshAttachments();
				refreshGUI();				
	      DefaultTableModel dm = (DefaultTableModel)table.getModel();
	      dm.fireTableDataChanged();				
			}   	
    };
    stepsButton.addActionListener(tracksOrStepsAction);
    tracksButton.addActionListener(tracksOrStepsAction);
    ButtonGroup group = new ButtonGroup();
    group.add(stepsButton);
    group.add(tracksButton);
    tracksButton.setSelected(true);
    
    // relative button
    relativeCheckbox = new JCheckBox();
    relativeCheckbox.setSelected(false);
    relativeCheckbox.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
      	if (refreshing) return;
      	CircleFitter fitter = (CircleFitter)TTrack.getTrack(trackID);
      	fitter.isRelativeFrameNumbers = relativeCheckbox.isSelected();
      	refreshFieldsAndButtons(fitter);
	    	fitter.refreshAttachments();
				refreshGUI();				
			}   	
    });
    
    // range action, listener and fields
    final Action frameRangeAction = new AbstractAction() {
			@Override
			public void actionPerformed(ActionEvent e) {
      	CircleFitter fitter = (CircleFitter)TTrack.getTrack(trackID);
    		fitter.setAttachmentStartFrame(startField.getIntValue());   		
    		fitter.setAttachmentFrameCount(countField.getIntValue());
    		refreshFieldsAndButtons(fitter);
        fitter.refreshAttachments();
		    DefaultTableModel dm = (DefaultTableModel)table.getModel();
		    dm.fireTableDataChanged();
		    fitter.trackerPanel.repaint();
			}
    };

    FocusListener frameRangeFocusListener = new FocusAdapter() {
      public void focusLost(FocusEvent e) {
      	if (e.getSource()==startField && startField.getBackground()!=Color.yellow) return;
      	if (e.getSource()==countField && countField.getBackground()!=Color.yellow) return;
      	frameRangeAction.actionPerformed(null);
      }
    };
    startField = new IntegerField(3);
    startField.addActionListener(frameRangeAction);
    startField.addFocusListener(frameRangeFocusListener);
    countField = new IntegerField(2);
    countField.addActionListener(frameRangeAction);
    countField.addFocusListener(frameRangeFocusListener);
    
  	startLabel = new JLabel();
  	startLabel.setBorder(BorderFactory.createEmptyBorder(0,4,0,0));
  	countLabel = new JLabel();
  	countLabel.setBorder(BorderFactory.createEmptyBorder(0,4,0,0));
    
    // put circleFitter panel in attachments panel SOUTH
    circleFitterPanel = new JPanel(new BorderLayout());
    circleFitterPanel.setBorder(BorderFactory.createTitledBorder("")); //$NON-NLS-1$
    
    // put steps and tracks buttons in circleFitterPanel NORTH
    JPanel buttonbar = new JPanel();
    circleFitterPanel.add(buttonbar, BorderLayout.NORTH);
    buttonbar.add(stepsButton);
    buttonbar.add(tracksButton);
    
    // create circleFitterStartStopPanel
    circleFitterStartStopPanel = new JPanel(new BorderLayout());
    Border empty = BorderFactory.createEmptyBorder(0,4,0,4);
    Border etched = BorderFactory.createEtchedBorder();
    circleFitterStartStopPanel.setBorder(BorderFactory.createCompoundBorder(empty, etched));
    
    
    // put start and end frame controls in circleFitterStartStopPanel CENTER
    buttonbar = new JPanel();
    buttonbar.add(startLabel);
    buttonbar.add(startField);
//    buttonbar.add(startSpinner);
    buttonbar.add(countLabel);
    buttonbar.add(countField);
//    buttonbar.add(endSpinner);
    circleFitterStartStopPanel.add(buttonbar, BorderLayout.CENTER);
    
    // put relative checkbox in circleFitterStartStopPanel SOUTH
    buttonbar = new JPanel();
    circleFitterStartStopPanel.add(buttonbar, BorderLayout.SOUTH);
    buttonbar.add(relativeCheckbox);
    

    // help and close buttons
    helpButton = new JButton();
    helpButton.setForeground(new Color(0, 0, 102));
    helpButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
    		TTrack measuringTool = TTrack.getTrack(trackID);
      	String keyword = measuringTool==null? "circle":  //$NON-NLS-1$
      		measuringTool instanceof Protractor? "protractor":  //$NON-NLS-1$
      		measuringTool instanceof TapeMeasure? "tape": "circle"; //$NON-NLS-1$ //$NON-NLS-2$
        trackerPanel.getTFrame().showHelp(keyword+"#attach", 0); //$NON-NLS-1$
      }
    });
    closeButton = new JButton();
    closeButton.setForeground(new Color(0, 0, 102));
    closeButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        setVisible(false);
      }
    });
    
    // put help and close button in content pane SOUTH
    buttonbar = new JPanel();
    contentPane.add(buttonbar, BorderLayout.SOUTH);
    buttonbar.add(helpButton);
    buttonbar.add(closeButton);
  }
  
  /**
   * Sets the measuring tool.
   */
  protected void setMeasuringTool(TTrack tool) {
		TTrack measuringTool = TTrack.getTrack(trackID);
  	if (measuringTool!=null) {
	    measuringTool.removePropertyChangeListener("dataPoint", this); //$NON-NLS-1$  		
  	}
  	
    measuringTool = tool;
  	trackID = measuringTool.getID();
	  measuringTool.addPropertyChangeListener("dataPoint", this); //$NON-NLS-1$  
	  
    measuringTool.refreshAttachments();
    refreshDropdowns();
    if (measuringTool instanceof CircleFitter) {
    	CircleFitter fitter = (CircleFitter)measuringTool;
	    refreshFieldsAndButtons(fitter);
    }
    DefaultTableModel dm = (DefaultTableModel)table.getModel();
    dm.fireTableDataChanged();
    refreshGUI();
  }

  /**
   * Refreshes the attachment and measuring tool dropdowns.
   */
  protected void refreshDropdowns() {
		masses = trackerPanel.getDrawables(PointMass.class);
    for (TTrack p: masses) {
      p.removePropertyChangeListener("name", this); //$NON-NLS-1$
      p.removePropertyChangeListener("color", this); //$NON-NLS-1$
      p.removePropertyChangeListener("footprint", this); //$NON-NLS-1$
    }
		TTrack measuringTool = TTrack.getTrack(trackID);
		if (measuringTool!=null && measuringTool instanceof TapeMeasure) {
			// can't attach calibration stick to models--creates circular dependency
			TapeMeasure tape = (TapeMeasure)measuringTool;
			if (tape.isStickMode()) {
				masses.removeAll(trackerPanel.getDrawables(ParticleModel.class));
			}
		}
    for (TTrack p: masses) {
      p.addPropertyChangeListener("name", this); //$NON-NLS-1$
      p.addPropertyChangeListener("color", this); //$NON-NLS-1$
      p.addPropertyChangeListener("footprint", this); //$NON-NLS-1$
    }
		FontSizer.setFonts(rendererDropdown, FontSizer.getLevel());
		rendererDropdown.setModel(new AttachmentComboBoxModel());
		FontSizer.setFonts(editorDropdown, FontSizer.getLevel());
		editorDropdown.setModel(new AttachmentComboBoxModel());
    
		FontSizer.setFonts(measuringToolDropdown, FontSizer.getLevel());
		java.util.Vector<TTrack> tools = new java.util.Vector<TTrack>();
    for (TTrack track: trackerPanel.getTracks()) {
    	if (track instanceof TapeMeasure
    			|| track instanceof Protractor
    			|| track instanceof CircleFitter) {
    		tools.add(track);
    	}
    }
    for (TTrack p: tools) {
      p.removePropertyChangeListener("name", this); //$NON-NLS-1$
      p.removePropertyChangeListener("color", this); //$NON-NLS-1$
      p.removePropertyChangeListener("footprint", this); //$NON-NLS-1$
      p.addPropertyChangeListener("name", this); //$NON-NLS-1$
      p.addPropertyChangeListener("color", this); //$NON-NLS-1$
      p.addPropertyChangeListener("footprint", this); //$NON-NLS-1$    	
    }
    measuringToolDropdown.setModel(new DefaultComboBoxModel(tools));
    if (!tools.isEmpty() && measuringTool!=null) {
    	measuringToolDropdown.setSelectedItem(measuringTool);
    }
    else {
    	// measuring tool is null, so set it to first in list, if any
    	for (TTrack next: tools) {
    		setMeasuringTool(next);
    		break;
    	}
    }
  }
  
  /**
   * Refreshes the start and end fields based on the state of a CircleFitter.
   * Also refreshes the button state.
   * 
   * @param fitter the CircleFitter
   */
  protected void refreshFieldsAndButtons(CircleFitter fitter) {
  	if (fitter.attachToSteps && fitter.isRelativeFrameNumbers) {
      startField.getFormat().applyPattern("+#;-#"); //$NON-NLS-1$
  	}
  	else {
      startField.getFormat().applyPattern("#;-#"); //$NON-NLS-1$
  	}
  	int min = fitter.isRelativeFrameNumbers? 
  			1-trackerPanel.getPlayer().getVideoClip().getFrameCount(): 
  				trackerPanel.getPlayer().getVideoClip().getFirstFrameNumber();
  	int max = trackerPanel.getPlayer().getVideoClip().getLastFrameNumber();
  	startField.setMaxValue(max);
  	startField.setMinValue(min);
    startField.setIntValue(fitter.isRelativeFrameNumbers? fitter.relativeStart: fitter.absoluteStart);
  	countField.setMaxValue(CircleFitter.maxDataPointCount);
  	countField.setMinValue(1);
    countField.setIntValue(fitter.getAttachmentFrameCount());
    
    refreshing = true;
    stepsButton.setSelected(fitter.attachToSteps);
    relativeCheckbox.setSelected(fitter.isRelativeFrameNumbers);
    refreshing = false;
  }
  
  /**
   * Updates this dialog to show the system's current attachments.
   */
  protected void refreshGUI() {
    setTitle(TrackerRes.getString("AttachmentInspector.Title")); //$NON-NLS-1$
    helpButton.setText(TrackerRes.getString("Dialog.Button.Help")); //$NON-NLS-1$  
    closeButton.setText(TrackerRes.getString("Dialog.Button.Close")); //$NON-NLS-1$
    dummyMass.setName(TrackerRes.getString("DynamicSystemInspector.ParticleName.None")); //$NON-NLS-1$
  	startLabel.setText(TrackerRes.getString("AttachmentInspector.Label.StartFrame")); //$NON-NLS-1$
  	countLabel.setText(TrackerRes.getString("AttachmentInspector.Label.FrameCount")); //$NON-NLS-1$
  	stepsButton.setText(TrackerRes.getString("AttachmentInspector.Button.Steps")); //$NON-NLS-1$
  	tracksButton.setText(TrackerRes.getString("AttachmentInspector.Button.Tracks")); //$NON-NLS-1$
  	relativeCheckbox.setText(TrackerRes.getString("AttachmentInspector.Checkbox.Relative")); //$NON-NLS-1$
  	stepsButton.setToolTipText(TrackerRes.getString("AttachmentInspector.Button.Steps.Tooltip")); //$NON-NLS-1$
  	tracksButton.setToolTipText(TrackerRes.getString("AttachmentInspector.Button.Tracks.Tooltip")); //$NON-NLS-1$
  	relativeCheckbox.setToolTipText(TrackerRes.getString("AttachmentInspector.Checkbox.Relative.Tooltip")); //$NON-NLS-1$
    TitledBorder border = (TitledBorder)circleFitterPanel.getBorder();
  	border.setTitle(TrackerRes.getString("AttachmentInspector.Border.Title.AttachTo")); //$NON-NLS-1$
    
  	// refresh layout to include/exclude circle fitter items
  	boolean hasCircleFitterPanel = attachmentsPanel.getComponentCount()>2;
  	boolean hasStartStopPanel = circleFitterPanel.getComponentCount()>1;
  	boolean changedLayout = false;
		TTrack measuringTool = TTrack.getTrack(trackID);
  	if (measuringTool instanceof CircleFitter) {
      // put circleFitter panel in attachments panel SOUTH
      changedLayout = !hasCircleFitterPanel;
      attachmentsPanel.add(circleFitterPanel, BorderLayout.SOUTH);

      CircleFitter fitter = (CircleFitter)measuringTool;
      if (!fitter.attachToSteps) {
        changedLayout = changedLayout || hasStartStopPanel;
        circleFitterPanel.remove(circleFitterStartStopPanel);      	
      }
      else {
      	if (fitter.isRelativeFrameNumbers) {
//      		startLabel.setText(TrackerRes.getString("AttachmentInspector.Label.Offset")); //$NON-NLS-1$
      	}
        changedLayout = changedLayout || !hasStartStopPanel;
        circleFitterPanel.add(circleFitterStartStopPanel, BorderLayout.CENTER);      	
      }
  	}
  	else {
      attachmentsPanel.remove(circleFitterPanel);
      changedLayout = hasCircleFitterPanel;
  	}
  	if (changedLayout) {
  		pack();
  	}
    repaint();
  }
  
  public void setFontLevel(int level) {
		FontSizer.setFonts(this, level);
		FontSizer.setFonts(attachmentCellRenderer.label, level);
		FontSizer.setFonts(table, level);
		FontSizer.setFonts(circleFitterPanel, level);
		FontSizer.setFonts(circleFitterStartStopPanel, level);
		refreshDropdowns();
		pack();
  }
  

  
  /**
   * A class to provide model data for the attachment table.
   */
  class AttachmentTableModel extends DefaultTableModel {
    public int getRowCount() {
  		TTrack measuringTool = TTrack.getTrack(trackID);
    	if (measuringTool==null) return 0;
    	
    	if (measuringTool instanceof CircleFitter) {
    		CircleFitter fitter = (CircleFitter)measuringTool;
    		if (fitter.attachToSteps) {
    			return 1;
    		}
    	}
    	TTrack[] attachments = measuringTool.getAttachments();
    	return attachments==null?	0: attachments.length;
    }

    public int getColumnCount() {return 2;}

    public Object getValueAt(int row, int col) {
  		TTrack measuringTool = TTrack.getTrack(trackID);
    	if (col==0) {
		    return measuringTool.getAttachmentDescription(row);
    	}
    	return measuringTool.getAttachments()[row];
    }
    
    public String getColumnName(int col) {
    	return col==0? TrackerRes.getString("AttachmentInspector.Header.PointName"): //$NON-NLS-1$
    		TrackerRes.getString("AttachmentInspector.Header.AttachedTo"); //$NON-NLS-1$
    }
    
    public boolean isCellEditable(int row, int col) {return col==1;}
    
    public Class<?> getColumnClass(int col) {return col==0? String.class: PointMass.class;}
    
    public void setValueAt(Object val, int row, int col) {} // empty method

  }
  
  /**
   * A class to render attachment table cells.
   */
  class AttachmentCellRenderer implements TableCellRenderer {
  	
  	JLabel label;
  	
  	AttachmentCellRenderer() {
  		label = new JLabel();
  		label.setHorizontalAlignment(SwingConstants.CENTER);
			label.setBackground(Color.white);
  	}
  	
    public Component getTableCellRendererComponent(JTable table, Object val,
        boolean selected, boolean hasFocus, int row, int col) {
    	
    	if (col==0) {
    		label.setText((String)val);
    		label.validate();
    		return label;
    	}
    	
    	rendererDropdown.setSelectedItem(val==null? dummyMass: val);
			return rendererDropdown;
    }
        
  }
  
  /**
   * A class to edit PointMass table cells in a JComboBox.
   */
  class AttachmentCellEditor extends DefaultCellEditor {
  	
  	AttachmentCellEditor() {
  		super(editorDropdown);
  	}
  	
    public Component getTableCellEditorComponent(JTable table, Object value,
				 boolean isSelected, int row, int column) {
    	editorDropdown.setSelectedItem(value==null? dummyMass: value);
    	return super.getTableCellEditorComponent(table, value, isSelected, row, column);
    }
    
  	public Object getCellEditorValue() {
      Object obj = super.getCellEditorValue();
			int row = table.getSelectedRow();
			if (row<0) return null;
  		TTrack measuringTool = TTrack.getTrack(trackID);
			TTrack[] attachments = measuringTool.getAttachments();
			if (attachments[row]!=null) {
				attachments[row].removePropertyChangeListener("step", measuringTool); //$NON-NLS-1$
				attachments[row].removePropertyChangeListener("steps", measuringTool); //$NON-NLS-1$
			}
			attachments[row] = obj==dummyMass? null: (PointMass)obj;
    	measuringTool.refreshAttachments();
			refreshGUI();
			
      DefaultTableModel dm = (DefaultTableModel)table.getModel();
      dm.fireTableDataChanged();
      return obj;
    }
  	
  }
 
  /**
   * A class to render track labels for the attachment JComboBoxes.
   */
  class TTrackRenderer extends JLabel implements ListCellRenderer {
  	
  	TTrackRenderer() {
			setOpaque(true);
			setBorder(BorderFactory.createEmptyBorder(3, 4, 3, 0));
  	}
  	
    public Component getListCellRendererComponent(JList list, Object val, int index,
        boolean selected, boolean hasFocus) {

      if (selected) {
        setBackground(list.getSelectionBackground());
        setForeground(list.getSelectionForeground());
      } else {
        setBackground(list.getBackground());
        setForeground(list.getForeground());
      }
			if (val!=null) {
				TTrack track = (TTrack)val;
				setText(track.getName());
				Icon icon = track==dummyMass? 
						new ResizableIcon(dummyIcon): 
						track.getFootprint().getIcon(21, 16);
				int factor = FontSizer.getIntegerFactor();
				if (icon instanceof ResizableIcon) {
					((ResizableIcon)icon).resize(factor);
				}
				setIcon(icon);
			}
			return this;
    }

  }
  
  /**
   * A class to provide model data for the attachment JComboBoxes.
   */
  class AttachmentComboBoxModel extends AbstractListModel implements ComboBoxModel {  	
  	
  	Object selected = dummyMass;
  	
		public int getSize() {
			return masses==null? 1: masses.size()+1;
		}

		public Object getElementAt(int index) {
			return index==0? dummyMass: masses.get(index-1);
		}

		public void setSelectedItem(Object anItem) {
			selected = anItem;
		}

		public Object getSelectedItem() {
			return selected==null? dummyMass: selected;
		}
  	
  }
}