package vermilion;

// TODO: make all imports explicit.

// TODO: Introduce static constant on SystemComposer for 'Patterns'
// and one on SystemRouter for 'Machines' (plus methods to get them
// rather than direct access through document).


import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.Font;
import relative.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.util.ArrayList;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

/**
 * A panel that displays a tracker interface. This includes machine and pattern
 * lists as well as the 'piano roll' event cueing control.
 */
public class JTrackerPanel extends JPanel
{
    // The system composer which owns this panel.
    protected SystemComposer sysComposer;

    // The piano roll tracker interface control.
    private JPatternTable trackerArea;

    // The list of machines that are in the composition.
    private DefaultListModel machineList;
    protected JList machineListC;
    
    // The list of patterns for a chosen machine, add/rem buttons.
    private DefaultListModel patternList;
    protected JList patternListC;
    protected JButton newPatternButton;
    protected JButton delPatternButton;

    // The list of tracks for a chosen machine/pattern, add/rem buttons.
    private DefaultListModel trackList;
    protected JList trackListC;
    protected JButton newTrackButton;
    protected JButton delTrackButton;

    // Play/stop buttons for panel and menu for main title bar.
    protected JMenu playbackMenu;
    protected JMenuItem playItem;
    protected JButton playButton;
    protected JMenuItem stopItem;
    protected JButton stopButton;

    // Pattern information label and pattern properties button
    private JLabel infoLabel;
    protected JButton patternPropsButton;

    /**
     * Constructs tracker interface the panel, keeping a reference to the
     * composer.
     */
    public JTrackerPanel(SystemComposer composer)
    {
        sysComposer = composer;
        ComposerListener composerListener = new ComposerListener(composer, this);
        ComposerSelectListener selectListener = new ComposerSelectListener(composer, this);

        RelativeLayout theLayout = new RelativeLayout(null);
        setLayout(theLayout);





        // Main panel for the tracker, play/stop buttons.
        trackerArea = new JPatternTable(composer);
        theLayout.registerForFontSizeChanges(trackerArea, 1.2f, "Courier New", Font.PLAIN);
        JScrollPane pane = new JScrollPane(trackerArea);
        this.add(pane, new RelativePlacement(0, 0, 800, 950));
        theLayout.registerForFontSizeChanges(trackerArea.getTableHeader(), 0.8f);

        infoLabel = new JLabel("Information", JLabel.CENTER);
        theLayout.registerForFontSizeChanges(infoLabel);
        this.add(infoLabel, new RelativePlacement(0, 950, 650, 50));

        patternPropsButton = new JButton("Properties");
        theLayout.registerForFontSizeChanges(patternPropsButton);
        patternPropsButton.addActionListener(composerListener);
        this.add(patternPropsButton, new RelativePlacement(650, 950, 150, 50));






        playButton = new JButton("Play");
        theLayout.registerForFontSizeChanges(playButton);
        this.add(playButton, new RelativePlacement(800, 0, 100, 50));
        playButton.addActionListener(composerListener);

        stopButton = new JButton("Stop");
        theLayout.registerForFontSizeChanges(stopButton);
        this.add(stopButton, new RelativePlacement(900, 0, 100, 50));
        stopButton.addActionListener(composerListener);





        // Machine list, for choosing target plugin.
        JLabel machineListLabel = new JLabel("Machines", JLabel.CENTER);
        theLayout.registerForFontSizeChanges(machineListLabel);
        this.add(machineListLabel, new RelativePlacement(800, 50, 200, 50));

        machineList = new DefaultListModel();
        machineListC = new JList(machineList);
        theLayout.registerForFontSizeChanges(machineListC);
        machineListC.addListSelectionListener(selectListener);
        this.add(machineListC, new RelativePlacement(800, 100, 200, 250));





        // Pattern list, for selecing/creating patterns.
        JLabel patternListLabel = new JLabel("Patterns", JLabel.CENTER);
        theLayout.registerForFontSizeChanges(patternListLabel);
        this.add(patternListLabel, new RelativePlacement(800, 350, 200, 50));

        newPatternButton = new JButton("Create");
        theLayout.registerForFontSizeChanges(newPatternButton);
        newPatternButton.addActionListener(composerListener);
        this.add(newPatternButton, new RelativePlacement(800, 400, 100, 50));

        delPatternButton = new JButton("Delete");
        theLayout.registerForFontSizeChanges(delPatternButton);
        delPatternButton.addActionListener(composerListener);
        this.add(delPatternButton, new RelativePlacement(900, 400, 100, 50));

        patternList = new DefaultListModel();
        patternListC = new JList(patternList);
        theLayout.registerForFontSizeChanges(patternListC);
        patternListC.addListSelectionListener(selectListener);
        this.add(patternListC, new RelativePlacement(800, 450, 200, 250));





        // Track list for adding/removing channels.
        JLabel trackListLabel = new JLabel("Tracks", JLabel.CENTER);
        theLayout.registerForFontSizeChanges(trackListLabel);
        this.add(trackListLabel, new RelativePlacement(800, 700, 200, 50));

        newTrackButton = new JButton("Add");
        theLayout.registerForFontSizeChanges(newTrackButton);
        this.add(newTrackButton, new RelativePlacement(800, 750, 100, 50));
        newTrackButton.addActionListener(composerListener);

        delTrackButton = new JButton("Remove");
        theLayout.registerForFontSizeChanges(delTrackButton);
        this.add(delTrackButton, new RelativePlacement(900, 750, 100, 50));
        delTrackButton.addActionListener(composerListener);

        trackList = new DefaultListModel();
        trackListC = new JList(trackList);
        theLayout.registerForFontSizeChanges(trackListC);
        this.add(trackListC, new RelativePlacement(800, 800, 200, 200));





        // Menu items for playback control.
        playbackMenu = new JMenu("Playback");
        theLayout.registerForFontSizeChanges(playbackMenu);

        playItem = new JMenuItem("Play");
        playItem.addActionListener(composerListener);
        theLayout.registerForFontSizeChanges(playItem);
        playbackMenu.add(playItem);

        stopItem = new JMenuItem("Stop");
        stopItem.addActionListener(composerListener);
        theLayout.registerForFontSizeChanges(stopItem);
        playbackMenu.add(stopItem);

        Main.window.addMenu(playbackMenu);
    }

    /**
     * Removes the menu we created, for when we reinitialise or close.
     */
    public void removeMenu()
    {
        Main.window.removeMenu(playbackMenu);
    }

    /**
     * Reloads the items in the machine list when the document is loaded or
     * if a machine is added or deleted (and maybe later renamed). Clears the
     * pattern list and editor area.
     */
    public void reloadMachines()
    {
        trackerArea.showPattern(null, null);

        machineList.clear();
        patternList.clear();
        trackList.clear();

        ArrayList<UsefulFileObject> machines = Main.document.getChildren("Machines");

        if (machines != null)
        {
            for (UsefulFileObject obj : machines)
            {
                MachineNode node = (MachineNode) obj;
                if (node != null) machineList.addElement(node);
            }
        }
    }

    /**
     * Passes through a command to save changes to the currently showing pattern
     * when we do a save.
     */
    public void documentSaving()
    {
        trackerArea.saveCurrentPattern();
    }

    /**
     * Called to clear the interface when a document is loaded. This is mainly
     * done as a special operation simply to avoid the usual saving checks
     * when we change the pattern.
     */
    public void documentLoaded()
    {
        machineList.clear();
        patternList.clear();
        trackList.clear();
        trackerArea.clearData();
    }

    /**
     * Refreshes the pattern currently being shown, for when we add/remove tracks.
     */
    public void refreshPattern()
    {
        trackerArea.refreshPattern();
    }

    /**
     * Returns the name of the track where the cursor currently sits, for when
     * the user wants to remove a track.
     */
    public String getCurrentTrackName()
    {
        if (trackerArea.gridX == 0) return null;
        return trackerArea.columns[trackerArea.gridX].columnName;
    }

    /**
     * Fired when the user selects a machine, to update the tracker display,
     * lists and pointers that we use.
     */
    void showMachine()
    {
        // Update current pointers.
        sysComposer.currMachine = (MachineNode) machineListC.getSelectedValue();
        sysComposer.currPattern = null;

        // Clear the pattern list.
        patternList.clear();
        //showPattern();

        if (sysComposer.currMachine == null) return;

        // Populate the list of patterns.
        ArrayList<UsefulFileObject> patterns = Main.document.getChildren("Patterns/" + sysComposer.currMachine.machineName);
        if (patterns != null)
        {
            for (UsefulFileObject obj : patterns)
            {
                PatternNode node = (PatternNode) obj;
                patternList.addElement(node);
            }
        }

        // If the machine is the master, add the master pattern to the list.
        if (sysComposer.currMachine.isMaster)
        {
            PatternNode masterPattern = sysComposer.masterPattern;

            if (masterPattern != null) patternList.addElement(masterPattern);
        }
    }

    /**
     * Fired when the user selects a pattern, to update the tracker display,
     * lists and pointers that we use.
     */
    public void showPattern()
    {
        // Update the current pointer.
        sysComposer.currPattern = (PatternNode) patternListC.getSelectedValue();

        // Clear the track list.
        trackList.clear();

        if (sysComposer.currPattern == null)
        {
            trackerArea.showPattern(null, null);
            return;
        }

        // Populate the track list.
        if (sysComposer.currPattern == sysComposer.masterPattern)
        {
            ArrayList<UsefulFileObject> machines = Main.document.getChildren("Machines");
            for (int x = 0; x < machines.size(); x++)
            {
                trackList.addElement(new ParameterPattern(((MachineNode) machines.get(x)).machineID, ""));
            }
        }
        else
        {
            // TODO: Cache these, also used in SystemComposer.createPattern.
            // The placeholders can also cache the last loaded pattern.
            MachineBase mach = (MachineBase) Main.plugins.getPlugin(sysComposer.currMachine.machineName);
            mach.setupMachine();

            for (ParameterBase param : mach.parameters)
            {
                if (param.isTrackParam == false) trackList.addElement(param);
            }
        }

        // Show the pattern.
        trackerArea.showPattern(sysComposer.currPattern, sysComposer.currMachine.machineName);
        trackerArea.grabFocus();
    }
}

/**
 * A listener for the buttons and menu that handles starting and stopping
 * playback, as well as pattern creation and other operations.
 */
class ComposerListener implements ActionListener
{
    private SystemComposer sysComposer;
    private JTrackerPanel trackerPanel;

    /**
     * Constructs the composer listener with reference to composer and panel.
     */
    public ComposerListener(SystemComposer owner, JTrackerPanel container)
    {
        sysComposer = owner;
        trackerPanel = container;
    }

    /**
     * Handles actions from the menu and buttons for the composer interface.
     */
    @Override
    public void actionPerformed(ActionEvent e)
    {
        if (e.getSource() == trackerPanel.playItem
            || e.getSource() == trackerPanel.playButton)
        {
            // TODO: Getting playback cursor pos.
            sysComposer.seek(0);
        }
        else if (e.getSource() == trackerPanel.stopItem
            || e.getSource() == trackerPanel.stopButton)
        {
            sysComposer.stop();
        }
        else if (e.getSource() == trackerPanel.newPatternButton)
        {
            sysComposer.createPattern();
        }
        else if (e.getSource() == trackerPanel.delPatternButton)
        {
            sysComposer.deletePattern();
        }
        else if (e.getSource() == trackerPanel.patternPropsButton)
        {
            sysComposer.showPatternProperties();
        }
        else if (e.getSource() == trackerPanel.newTrackButton)
        {
            ParameterBase param = (ParameterBase) trackerPanel.trackListC.getSelectedValue();
            sysComposer.createTrack(param.parameterName, param.parameterType);
        }
        else if (e.getSource() == trackerPanel.delTrackButton)
        {
            sysComposer.deleteTrack(trackerPanel.getCurrentTrackName());
        }
    }
}

/**
 * A listener for the buttons and menu that handles starting and stopping
 * playback, as well as pattern creation and other operations.
 */
class ComposerSelectListener implements ListSelectionListener
{
    private JTrackerPanel trackerPanel;

    /**
     * Construct the select listener with reference to composer and panel.
     */
    public ComposerSelectListener(SystemComposer owner, JTrackerPanel container)
    {
        trackerPanel = container;
    }

    /**
     * Handles actions from the list controls on the tracker panel.
     */
    @Override
    public void valueChanged(ListSelectionEvent e)
    {
        if (e.getSource() == trackerPanel.patternListC)
        {
            trackerPanel.showPattern();
        }
        else if (e.getSource() == trackerPanel.machineListC)
        {
            trackerPanel.showMachine();
        }
    }
}