/*
 *  Copyright 2006 Columbia University.
 *
 *  This file is part of MEAPsoft.
 *
 *  MEAPsoft is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  MEAPsoft 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 MEAPsoft; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 *  02110-1301 USA
 *
 *  See the file "COPYING" for the text of the license.
 */

package com.meapsoft.gui;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.text.*;
import javax.swing.*; 
import javax.swing.border.*; 

import util.RTSI;
import com.meapsoft.*;
import com.meapsoft.featextractors.*;


/**
 * GUI interface for FeatExtractor.  
 *
 * @author Douglas Repetto (douglas@music.columbia.edu)
 * and the MEAP team
 */
public class FeatExtractorPanel extends MEAPsoftGUIPanel
{
	//feature extractor GUI
	JCheckBox enableBox;
	Vector featureCheckBoxes;
	Vector featureDescriptions;
    Vector featureWeightFields;
	JLabel inputSegmentsFileLabel;
	JLabel outputFeaturesFileLabel;
	JTextField inputSegmentsFileField;
	JTextField outputFeaturesFileField;
	JButton displayFeaturesButton;
    JCheckBox clearNonMetaFeatures;

    /**
     * Create a new FeatExtractor panel.
     */
    public FeatExtractorPanel(MEAPsoftGUI msg)
	{
        super(msg);
        BuildFeatureExtractorsGUI();

        title = "Feature Extractor";
        helpURL += "#" + title;
    }

	private void BuildFeatureExtractorsGUI()
	{
		featureDescriptions = new Vector();
		
		Color c = new Color((int)(Math.random() * 127 + 127),
					(int)(Math.random() * 127 + 127),
					(int)(Math.random() * 127 + 127));
        color = c;

		setBackground(c);
		BoxLayout fbl = new BoxLayout(this, BoxLayout.Y_AXIS);
		setLayout(fbl);
		
		Vector classNames = SniffFeatureExtractors();

		featureCheckBoxes = new Vector(classNames.size());
        featureWeightFields = new Vector(classNames.size());
		
		JPanel enablePanel = new JPanel();
		enablePanel.setBackground(c);
		
		enableBox = new JCheckBox("ENABLE FEATURE EXTRACTOR");
		enableBox.setBackground(c);
		enableBox.setSelected(true);
		enablePanel.add(enableBox);
		
		enablePanel.add(helpButton);
		
		add(enablePanel);
		
		JPanel controlsPanel = new JPanel();
		controlsPanel.setBackground(c);
		BoxLayout fecp = new BoxLayout(controlsPanel, BoxLayout.Y_AXIS);
		controlsPanel.setLayout(fecp);
		
		JPanel extractorInputFNPanel = new JPanel();
		extractorInputFNPanel.setBackground(c);
		JLabel fEINL = new JLabel("input segments file: ");
		extractorInputFNPanel.add(fEINL);
		inputSegmentsFileLabel = new JLabel(" " + dataBaseName + ".seg ");
		inputSegmentsFileLabel.setOpaque(true);
		inputSegmentsFileLabel.setBackground(c.darker());
		extractorInputFNPanel.add(inputSegmentsFileLabel);
		controlsPanel.add(extractorInputFNPanel);

		int numFeatures = classNames.size();

		JPanel selectFeaturesPanel = new JPanel();
		selectFeaturesPanel.setBackground(c);
		TitledBorder title = BorderFactory.createTitledBorder(
			BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), 
			"Feature Extractors");
		title.setTitleJustification(TitledBorder.CENTER);
		selectFeaturesPanel.setBorder(title);
		controlsPanel.add(selectFeaturesPanel);


		JPanel selectMetaFeaturesPanel = new JPanel();
		selectMetaFeaturesPanel.setBackground(c);
		TitledBorder metaTitle = BorderFactory.createTitledBorder(
			BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), 
			"Meta Feature Extractors");
        selectMetaFeaturesPanel.setBorder(metaTitle);
		metaTitle.setTitleJustification(TitledBorder.CENTER);

        controlsPanel.add(selectMetaFeaturesPanel);
		
        clearNonMetaFeatures = new JCheckBox("clear non-meta features");
        clearNonMetaFeatures.setBackground(c); 
        clearNonMetaFeatures.setSelected(true);
        clearNonMetaFeatures.setAlignmentX(Component.CENTER_ALIGNMENT);
        controlsPanel.add(clearNonMetaFeatures);

		int fENum = 0;
		int mFENum = 0;
		JPanel fEPanel = null;
		JPanel mFEPanel = null;
		for (int i = 0; i < classNames.size(); i++)
		{
			String name = (String)classNames.elementAt(i);
			
			FeatureExtractor f = null;
			try
			{
				f = (FeatureExtractor)(Class.forName("com.meapsoft.featextractors." + name).newInstance());
			}
			catch (Exception e)
			{
				//we should never get here!
				e.printStackTrace();
			}

			//not using actions, we'll just check their states 
			//when the GO! button is hit...
			JToggleButton cb = new JCheckBox(name);
            if(f instanceof MetaFeatureExtractor)
                cb = new JRadioButton(name);

			featureCheckBoxes.add(cb);
			cb.setBackground(c);
			cb.setToolTipText((String)featureDescriptions.elementAt(i));
			
            cb.setActionCommand("enableFeatExt"+i);
            cb.addActionListener(this);

            JFormattedTextField tf = new JFormattedTextField(NumberFormat.getNumberInstance());
            tf.setValue(new Double(1.0));
            tf.setColumns(2);
            tf.setBackground(c);
            tf.setEnabled(false);
            featureWeightFields.add(tf);

            JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
            panel.setBackground(c);
            panel.add(cb);
			panel.add(tf);

			if(!(f instanceof MetaFeatureExtractor))
			{
				fENum++;
                if (name.equals("AvgPitchSimple"))
                {
                    cb.setSelected(true);
                    tf.setEnabled(true);
                }

                selectFeaturesPanel.add(panel);
            }
			else
			{
				mFENum++;
                // hack to keep the MFE panel symmetric when it
                // contains only one row of checkboxes.
                if(mFENum < 4)
                    panel.setLayout(new FlowLayout());
                selectMetaFeaturesPanel.add(panel);
			}
		}
        selectFeaturesPanel.setLayout(new GridLayout((int)(fENum/4.0 + 1), 4));
        selectMetaFeaturesPanel.setLayout(new GridLayout((int)(mFENum/4.0 + 1), 4));

		add(controlsPanel);

		JPanel extractorOutputFNPanel = new JPanel();
		extractorOutputFNPanel.setBackground(c);
		JLabel fEONL = new JLabel("output features file: ");
		extractorOutputFNPanel.add(fEONL);
		outputFeaturesFileLabel = new JLabel(" " + dataBaseName + ".feat ");
		outputFeaturesFileLabel.setOpaque(true);
		outputFeaturesFileLabel.setBackground(c.darker());
		extractorOutputFNPanel.add(outputFeaturesFileLabel);
        add(Box.createRigidArea(new Dimension(0, 10)));
		add(extractorOutputFNPanel);
		
		JPanel displayFeaturesPanel = new JPanel();
		displayFeaturesPanel.setBackground(c);
		displayFeaturesButton = new JButton("display extracted features");
		displayFeaturesButton.setEnabled(false);
		displayFeaturesButton.addActionListener(this);
		displayFeaturesButton.setActionCommand("displayFeatures");
		displayFeaturesButton.setBackground(c);
		displayFeaturesPanel.add(displayFeaturesButton);
		add(displayFeaturesPanel);
	}

	private Vector SniffFeatureExtractors()
	{
        Vector v = null;
        try 
        {
            v = RTSI.findnames("com.meapsoft.featextractors", Class.forName("com.meapsoft.featextractors.FeatureExtractor"));
            // \todo{where else should we look for feature extractors?}
            // also check current directory:
            Vector v2 = RTSI.findnames(System.getProperty("user.dir"), Class.forName("com.meapsoft.featextractors.FeatureExtractor")); 
            if(v2 != null)
                v.addAll(v2);
            
            for (int i = 0; i < v.size(); i++)
            {
				FeatureExtractor f = null;
				
				try
				{
					String name = "com.meapsoft.featextractors." + (String)v.elementAt(i);
					f = (FeatureExtractor)(Class.forName(name).newInstance());
					featureDescriptions.add(f.description());
				}
				catch (Exception e)
				{
					GUIUtils.ShowDialog("", GUIUtils.MESSAGE, meapsoftGUI.jframe);	
                    //ShowDialog(e, "", FATAL_ERROR);
				}
            }
        }
        catch(ClassNotFoundException e)
        {
			GUIUtils.ShowDialog("", GUIUtils.FATAL_ERROR, meapsoftGUI.jframe);	
        }

        return v;
	}

	public void enableDisplayButton(boolean enable)
	{
		displayFeaturesButton.setEnabled(enable);		
	}

	public void actionPerformed(ActionEvent arg0)
	{
		String command = arg0.getActionCommand();

		if (command.equals("displayFeatures"))
		{
            DataDisplayPanel.spawnWindow(featFile.getFeatures(), featFile.filename);
        }
        else if(command.startsWith("enableFeatExt"))
        {
            int i = Integer.parseInt(command.substring("enableFeatExt".length()));

            ((JTextField)featureWeightFields.get(i)).setEnabled(
                ((JToggleButton)featureCheckBoxes.get(i)).isSelected());
        }
    }

	public synchronized int run()
	{
        if(!enableBox.isSelected())
        {
            // not all composers need features, so by default set the
            // featFile to be the empty segmentFile
            if(featFile == null)
                featFile = segmentFile;

            return 0;
        }

		boolean regularFE = false;
		boolean metaFE = false;
		
        Vector featExts = new Vector();
        Vector featWeights = new Vector();
		for(int i = 0; i < featureCheckBoxes.size(); i++)	
        {
			JToggleButton cb = (JToggleButton)featureCheckBoxes.elementAt(i);

            try
            {
                if (cb.isSelected())
                {
                	FeatureExtractor fe = (FeatureExtractor)(Class.forName("com.meapsoft.featextractors." + cb.getText())
					.newInstance());
					if (fe instanceof MetaFeatureExtractor)
						metaFE = true;
					else
						regularFE = true;
						
					featExts.add(fe);
                    featWeights.add(((JFormattedTextField)featureWeightFields.elementAt(i)).getValue());
                }
            }
            catch(Exception e)
            {
				GUIUtils.ShowDialog(e, "Error loading featextractor", GUIUtils.FATAL_ERROR, meapsoftGUI.jframe);	
                return -1;
            }
            
            if (metaFE && !regularFE)
            {
				GUIUtils.ShowDialog("You need at least one regular feature extractor in order to use a " +
					"meta feature extractor!", GUIUtils.MESSAGE, meapsoftGUI.jframe);
				return -1;	
            }
        }

        FeatExtractor featExtractor = 
            new FeatExtractor(segmentFile, featFile, featExts);
        featExtractor.writeMEAPFile = meapsoftGUI.writeMEAPFile;
        featExtractor.setFeatureExtractorWeights(featWeights);
        featExtractor.setClearNonMetaFeatures(
            clearNonMetaFeatures.isSelected());

        JPanel progressPanel = new JPanel();
        progressPanel.add(new JLabel("Extracting features: "));
        JProgressBar progressBar = new JProgressBar(featExtractor.getProgress());
        progressBar.setStringPainted(true);
        progressPanel.add(progressBar);
        meapsoftGUI.setProgressPanel(progressPanel);

        try
        {				
            // clear whatever chunks may have already been calculated
            featFile.clearChunks();
            
            featExtractor.setup();
            featExtractor.processFeatFiles();

            if(featExtractor.writeMEAPFile)
                featFile.writeFile();
        }
        catch (Exception e)
        {
			GUIUtils.ShowDialog(e, "Error running Feature Extractor", GUIUtils.FATAL_ERROR, meapsoftGUI.jframe);	
            return -1;
		}

        displayFeaturesButton.setEnabled(true);

        return 0;
	}
}
