package turgen;

import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import javax.swing.*;
import turgen.decoder.*;

public class DecoderFrame extends javax.swing.JFrame implements Logger,UIPersistor {
    
    private WavePulseDecoder waveDecoder;
    private AudioPulseDecoder audioDecoder;
    private PulseDecoder decoder;
    private JComponent[] decodingInvocationComponents;
    private JComponent[] decodingCancelationComponents;
    private Thread decodingThread;
    private String[] impulseStrings = {"|","/","-","\\"};
    private int impulsePhase=0;

    private final Object decoderSyncronizer = new Object();
    
    /** Creates new form DecoderFrame */
    public DecoderFrame() {

        initComponents();
        restoreDefaults();
        handleComponentDependencies();

        waveDecoder = new WavePulseDecoder();
        audioDecoder = new AudioPulseDecoder();
        decoder=null;
        
        decodingInvocationComponents = new JComponent[] {
            jbtDecode,
            jbtDecodeMultiple,
            jslNavigation,
            jtfSample,
            jbtJump,
            jbtDetach,
            jcoDecoders,
            pSettingsCover,
            jcbMonitorMode,
            
        };

        decodingCancelationComponents = new JComponent[] {jbtStop};


        
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        fcDecodeInput = new javax.swing.JFileChooser();
        fcDecodeOutput = new javax.swing.JFileChooser();
        pFiles = new javax.swing.JPanel();
        jlbWave = new javax.swing.JLabel();
        jtfWaveFile = new javax.swing.JTextField();
        jbtBrowseWave = new javax.swing.JButton();
        jLabel2 = new javax.swing.JLabel();
        jtfOutDir = new javax.swing.JTextField();
        jbtBrowseDir = new javax.swing.JButton();
        pInputParams = new javax.swing.JPanel();
        jcoSignalSource = new javax.swing.JComboBox();
        jlbSourceChannel = new javax.swing.JLabel();
        jcoSourceChannel = new javax.swing.JComboBox();
        jlbSampleRate = new javax.swing.JLabel();
        jcoSampleRate = new javax.swing.JComboBox();
        pAttachDetach = new javax.swing.JPanel();
        jbtAttach = new javax.swing.JButton();
        jbtDetach = new javax.swing.JButton();
        jlbSignalSource = new javax.swing.JLabel();
        pDecoding = new javax.swing.JPanel();
        pNav = new javax.swing.JPanel();
        jslNavigation = new javax.swing.JSlider();
        jtfSample = new javax.swing.JTextField();
        jbtJump = new javax.swing.JButton();
        pDeco = new javax.swing.JPanel();
        jlbDecoder = new javax.swing.JLabel();
        jcoDecoders = new javax.swing.JComboBox();
        jPanel2 = new javax.swing.JPanel();
        jbtDecode = new javax.swing.JButton();
        filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(4, 0));
        jbtDecodeMultiple = new javax.swing.JButton();
        filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(4, 0));
        jbtStop = new javax.swing.JButton();
        jcbMonitorMode = new javax.swing.JCheckBox();
        jlbImpulse = new javax.swing.JLabel();
        pControls = new javax.swing.JPanel();
        jTabbedPane1 = new javax.swing.JTabbedPane();
        pLog = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        jtaLog = new javax.swing.JTextArea();
        jPanel1 = new javax.swing.JPanel();
        btnClearLog = new javax.swing.JButton();
        jspSettings = new javax.swing.JScrollPane();
        pSettingsCover = new javax.swing.JPanel();
        pGeneralSettings = new javax.swing.JPanel();
        jcbPrependSampleNumber = new javax.swing.JCheckBox();
        jcbIgnoreBadChecksums = new javax.swing.JCheckBox();
        jcbPreferAdaptiveTransferSpeedDetection = new javax.swing.JCheckBox();
        pMonitorModeSettings = new javax.swing.JPanel();
        jcbMonitorSaveAllBytes = new javax.swing.JCheckBox();
        pTurbo2000SuperTurbo = new javax.swing.JPanel();
        jcbCsTurboSaveHeaderToExtraFile = new javax.swing.JCheckBox();
        jcbCsTurboAlwaysSaveAsBinaryFile = new javax.swing.JCheckBox();
        pASTSettings = new javax.swing.JPanel();
        jlbASTFormat = new javax.swing.JLabel();
        jcoASTFormat = new javax.swing.JComboBox();
        pTurboROMSettings = new javax.swing.JPanel();
        jlbTurboROMFormat = new javax.swing.JLabel();
        jcoTurboROMFormat = new javax.swing.JComboBox();
        filler3 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));

        fcDecodeOutput.setDialogType(javax.swing.JFileChooser.SAVE_DIALOG);
        fcDecodeOutput.setFileSelectionMode(javax.swing.JFileChooser.DIRECTORIES_ONLY);

        setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
        setTitle("Turbo decoder");
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                on_close(evt);
            }
        });
        getContentPane().setLayout(new java.awt.BorderLayout(0, 4));

        pFiles.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Input and output"));
        pFiles.setLayout(new java.awt.GridBagLayout());

        jlbWave.setText("WAVE file:");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
        pFiles.add(jlbWave, gridBagConstraints);

        jtfWaveFile.setColumns(32);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
        pFiles.add(jtfWaveFile, gridBagConstraints);

        jbtBrowseWave.setMnemonic('b');
        jbtBrowseWave.setText("Browse");
        jbtBrowseWave.setMargin(new java.awt.Insets(2, 2, 2, 2));
        jbtBrowseWave.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jbtBrowseWaveActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        pFiles.add(jbtBrowseWave, gridBagConstraints);

        jLabel2.setText("Output directory:");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
        pFiles.add(jLabel2, gridBagConstraints);

        jtfOutDir.setColumns(32);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
        pFiles.add(jtfOutDir, gridBagConstraints);

        jbtBrowseDir.setMnemonic('o');
        jbtBrowseDir.setText("Browse");
        jbtBrowseDir.setMargin(new java.awt.Insets(2, 2, 2, 2));
        jbtBrowseDir.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jbtBrowseDirActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 0);
        pFiles.add(jbtBrowseDir, gridBagConstraints);

        jcoSignalSource.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "WAVE file", "Audio IN" }));
        jcoSignalSource.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {
                onSignalSourceChanged(evt);
            }
        });
        pInputParams.add(jcoSignalSource);

        jlbSourceChannel.setText("Channel:");
        pInputParams.add(jlbSourceChannel);

        jcoSourceChannel.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Mono", "Left", "Right" }));
        pInputParams.add(jcoSourceChannel);

        jlbSampleRate.setText("Rate:");
        pInputParams.add(jlbSampleRate);

        jcoSampleRate.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "44100", "48000", "96000" }));
        pInputParams.add(jcoSampleRate);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        pFiles.add(pInputParams, gridBagConstraints);

        pAttachDetach.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 5, 0));

        jbtAttach.setMnemonic('a');
        jbtAttach.setText("Attach decoder");
        jbtAttach.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                on_attachWaveFile(evt);
            }
        });
        pAttachDetach.add(jbtAttach);

        jbtDetach.setMnemonic('d');
        jbtDetach.setText("Detach decoder");
        jbtDetach.setEnabled(false);
        jbtDetach.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jbtDetachActionPerformed(evt);
            }
        });
        pAttachDetach.add(jbtDetach);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 5;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 0.5;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 4, 0);
        pFiles.add(pAttachDetach, gridBagConstraints);

        jlbSignalSource.setText("Signal source:");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
        pFiles.add(jlbSignalSource, gridBagConstraints);

        getContentPane().add(pFiles, java.awt.BorderLayout.NORTH);

        pDecoding.setLayout(new java.awt.GridBagLayout());

        pNav.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Navigation"));
        pNav.setLayout(new java.awt.GridBagLayout());

        jslNavigation.setValue(0);
        jslNavigation.setEnabled(false);
        jslNavigation.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                on_sliderMoved(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(0, 2, 0, 5);
        pNav.add(jslNavigation, gridBagConstraints);

        jtfSample.setColumns(11);
        jtfSample.setFont(new java.awt.Font("Monospaced", 0, 11)); // NOI18N
        jtfSample.setEnabled(false);
        jtfSample.setMinimumSize(new java.awt.Dimension(32, 18));
        jtfSample.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jtfSampleActionPerformed(evt);
            }
        });
        pNav.add(jtfSample, new java.awt.GridBagConstraints());

        jbtJump.setMnemonic('j');
        jbtJump.setText("Jump");
        jbtJump.setEnabled(false);
        jbtJump.setMargin(new java.awt.Insets(2, 2, 2, 2));
        jbtJump.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jbtJumpActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 0);
        pNav.add(jbtJump, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        pDecoding.add(pNav, gridBagConstraints);

        pDeco.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Decoding"));
        pDeco.setLayout(new java.awt.GridBagLayout());

        jlbDecoder.setText("Decoder:");
        jlbDecoder.setEnabled(false);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(5, 4, 0, 3);
        pDeco.add(jlbDecoder, gridBagConstraints);

        jcoDecoders.setFont(new java.awt.Font("Dialog", 0, 12)); // NOI18N
        jcoDecoders.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Turbo 2000", "Turbo 2000 - kilobyte blocks", "Universal turbo", "Turbo Tape", "KSO Turbo 2000", "Turbo Blizzard", "Turbo ROM", "Atari Super Turbo" }));
        jcoDecoders.setEnabled(false);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(5, 3, 0, 3);
        pDeco.add(jcoDecoders, gridBagConstraints);

        jPanel2.setLayout(new javax.swing.BoxLayout(jPanel2, javax.swing.BoxLayout.X_AXIS));

        jbtDecode.setMnemonic('f');
        jbtDecode.setText("Decode one file");
        jbtDecode.setEnabled(false);
        jbtDecode.setMargin(new java.awt.Insets(2, 2, 2, 2));
        jbtDecode.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jbtDecodeActionPerformed(evt);
            }
        });
        jPanel2.add(jbtDecode);
        jPanel2.add(filler1);

        jbtDecodeMultiple.setMnemonic('e');
        jbtDecodeMultiple.setText("Decode until EOF");
        jbtDecodeMultiple.setEnabled(false);
        jbtDecodeMultiple.setMargin(new java.awt.Insets(2, 2, 2, 2));
        jbtDecodeMultiple.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jbtDecodeMultipleActionPerformed(evt);
            }
        });
        jPanel2.add(jbtDecodeMultiple);
        jPanel2.add(filler2);

        jbtStop.setMnemonic('s');
        jbtStop.setText("STOP");
        jbtStop.setToolTipText("Stop decoding (SHIFT for emergency stop) ");
        jbtStop.setEnabled(false);
        jbtStop.setMargin(new java.awt.Insets(2, 2, 2, 2));
        jbtStop.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jbtStopActionPerformed(evt);
            }
        });
        jPanel2.add(jbtStop);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(4, 4, 0, 0);
        pDeco.add(jPanel2, gridBagConstraints);

        jcbMonitorMode.setText("Monitor mode");
        jcbMonitorMode.setToolTipText("Decode raw blocks");
        jcbMonitorMode.setBorderPainted(true);
        jcbMonitorMode.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
        pDeco.add(jcbMonitorMode, new java.awt.GridBagConstraints());

        jlbImpulse.setFont(new java.awt.Font("Monospaced", 1, 14)); // NOI18N
        jlbImpulse.setText(" ");
        jlbImpulse.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridy = 1;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        pDeco.add(jlbImpulse, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        pDecoding.add(pDeco, gridBagConstraints);

        pControls.setLayout(new java.awt.BorderLayout());

        pLog.setLayout(new java.awt.BorderLayout());

        jtaLog.setEditable(false);
        jtaLog.setColumns(20);
        jtaLog.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N
        jtaLog.setRows(20);
        jScrollPane1.setViewportView(jtaLog);

        pLog.add(jScrollPane1, java.awt.BorderLayout.CENTER);

        jPanel1.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 5, 2));

        btnClearLog.setMnemonic('c');
        btnClearLog.setText("Clear log");
        btnClearLog.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnClearLogActionPerformed(evt);
            }
        });
        jPanel1.add(btnClearLog);

        pLog.add(jPanel1, java.awt.BorderLayout.SOUTH);

        jTabbedPane1.addTab("Log", pLog);

        jspSettings.setPreferredSize(new java.awt.Dimension(411, 320));

        pSettingsCover.setLayout(new java.awt.GridBagLayout());

        pGeneralSettings.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "General", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 0, 12))); // NOI18N
        pGeneralSettings.setLayout(new java.awt.GridBagLayout());

        jcbPrependSampleNumber.setText("Prepend sample numbers to output file names");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
        pGeneralSettings.add(jcbPrependSampleNumber, gridBagConstraints);

        jcbIgnoreBadChecksums.setText("Ignore bad checksums");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
        pGeneralSettings.add(jcbIgnoreBadChecksums, gridBagConstraints);

        jcbPreferAdaptiveTransferSpeedDetection.setText("Prefer adaptive transfer speed detection");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
        pGeneralSettings.add(jcbPreferAdaptiveTransferSpeedDetection, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(4, 0, 0, 0);
        pSettingsCover.add(pGeneralSettings, gridBagConstraints);

        pMonitorModeSettings.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Monitor mode", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 0, 12))); // NOI18N
        pMonitorModeSettings.setLayout(new java.awt.GridBagLayout());

        jcbMonitorSaveAllBytes.setText("Save all bytes of decoded blocks");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
        pMonitorModeSettings.add(jcbMonitorSaveAllBytes, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(4, 0, 0, 0);
        pSettingsCover.add(pMonitorModeSettings, gridBagConstraints);

        pTurbo2000SuperTurbo.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Turbo 2000 and Super Turbo (CS)", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 0, 12))); // NOI18N
        pTurbo2000SuperTurbo.setLayout(new java.awt.GridBagLayout());

        jcbCsTurboSaveHeaderToExtraFile.setText("Save header to extra file");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
        pTurbo2000SuperTurbo.add(jcbCsTurboSaveHeaderToExtraFile, gridBagConstraints);

        jcbCsTurboAlwaysSaveAsBinaryFile.setText("Always save as binary file");
        jcbCsTurboAlwaysSaveAsBinaryFile.setToolTipText("");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
        pTurbo2000SuperTurbo.add(jcbCsTurboAlwaysSaveAsBinaryFile, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(4, 0, 0, 0);
        pSettingsCover.add(pTurbo2000SuperTurbo, gridBagConstraints);

        pASTSettings.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Atari Super Turbo", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 0, 12))); // NOI18N
        pASTSettings.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 4, 0));

        jlbASTFormat.setLabelFor(jcoASTFormat);
        jlbASTFormat.setText("File format:");
        pASTSettings.add(jlbASTFormat);

        jcoASTFormat.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "AST" }));
        jcoASTFormat.setAlignmentX(0.0F);
        pASTSettings.add(jcoASTFormat);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 4;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(4, 0, 0, 0);
        pSettingsCover.add(pASTSettings, gridBagConstraints);

        pTurboROMSettings.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Turbo ROM", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 0, 12))); // NOI18N
        pTurboROMSettings.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 4, 0));

        jlbTurboROMFormat.setLabelFor(jcoASTFormat);
        jlbTurboROMFormat.setText("File format:");
        pTurboROMSettings.add(jlbTurboROMFormat);

        jcoTurboROMFormat.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Binary file", "BASIC" }));
        jcoTurboROMFormat.setAlignmentX(0.0F);
        pTurboROMSettings.add(jcoTurboROMFormat);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(4, 0, 0, 0);
        pSettingsCover.add(pTurboROMSettings, gridBagConstraints);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 4;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        pSettingsCover.add(filler3, gridBagConstraints);

        jspSettings.setViewportView(pSettingsCover);

        jTabbedPane1.addTab("Settings", jspSettings);

        pControls.add(jTabbedPane1, java.awt.BorderLayout.CENTER);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weighty = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(8, 0, 0, 0);
        pDecoding.add(pControls, gridBagConstraints);

        getContentPane().add(pDecoding, java.awt.BorderLayout.CENTER);

        pack();
    }// </editor-fold>//GEN-END:initComponents


private void attachDecoder(String source, String channel)    {

    /*Close open decoder if any*/
    if (decoder!=null) {
        try {
            decoder.close(null);
        }
        catch (Exception e1) {
            e1.printStackTrace();
        }
    }


    /*Initialize either decoder*/
    try {
        /*Audio decoder*/
        if (!(source.equals("WAVE file"))) {

            /*Initialize decoder*/
            decoder=audioDecoder;
            decoder.init(channel,this,(String)jcoSampleRate.getSelectedItem());
        }
        /*Wave file decoder*/
        else {
            decoder=waveDecoder;
            decoder.init(jtfWaveFile.getText().trim(),this,channel);
        }
    }
    /*Failure - close and issue message*/
    catch(Exception e) {
        try {
            decoder.close(null);
        }
        catch (Exception e2) {
            e2.printStackTrace();
        }
        setAttachDependentControlsEnabled(false);
        e.printStackTrace();
        JOptionPane.showMessageDialog(this, Utils.getTitledExceptionMessage("Unable to initialize decoder", e));
        return;
    }

    /*Prepare GUI elements*/
    long siz = decoder.getTotalSamples();
    jtfSample.setText("0");
    jslNavigation.setMinimum(0);
    jslNavigation.setMaximum((int)siz);
    jslNavigation.setValue(0);
    setAttachDependentControlsEnabled(true);

}

private void detachDecoder() {

    /*Close the decoder*/
    try {
        decoder.close(null);
    }
    catch (Exception e) {
        e.printStackTrace();
    }

    /*Prepare GUI elements*/
    setAttachDependentControlsEnabled(false);
}


private void on_attachWaveFile(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_on_attachWaveFile
    
    
    attachDecoder((String)jcoSignalSource.getSelectedItem(),(String)jcoSourceChannel.getSelectedItem());
    
}//GEN-LAST:event_on_attachWaveFile

private void on_sliderMoved(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_on_sliderMoved
    jtfSample.setText(Integer.toString(jslNavigation.getValue()));
    
}//GEN-LAST:event_on_sliderMoved

private void jbtDecodeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jbtDecodeActionPerformed
    performDecoding(false);
}//GEN-LAST:event_jbtDecodeActionPerformed

private void btnClearLogActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnClearLogActionPerformed
    jtaLog.setText("");
}//GEN-LAST:event_btnClearLogActionPerformed

private void jbtJumpActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jbtJumpActionPerformed
    
    int loc;
    try {
        loc = Integer.parseInt(jtfSample.getText().trim());
    }
    catch(NumberFormatException e) {
        /*Nothing to do*/
        return;
    }
    jslNavigation.setValue(loc);
}//GEN-LAST:event_jbtJumpActionPerformed

private void jbtBrowseWaveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jbtBrowseWaveActionPerformed
    if(fcDecodeInput.showOpenDialog(this)==JFileChooser.CANCEL_OPTION) return;
        File f=fcDecodeInput.getSelectedFile();
        if (f!=null) {
            jtfWaveFile.setText(f.getAbsolutePath());
        }
}//GEN-LAST:event_jbtBrowseWaveActionPerformed

private void jbtBrowseDirActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jbtBrowseDirActionPerformed
    if(fcDecodeOutput.showSaveDialog(this)==JFileChooser.CANCEL_OPTION) return;
        File f=fcDecodeOutput.getSelectedFile();
        if (f!=null) {
            jtfOutDir.setText(f.getAbsolutePath());
        }
}//GEN-LAST:event_jbtBrowseDirActionPerformed

private void jbtDecodeMultipleActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jbtDecodeMultipleActionPerformed
    performDecoding(true);
}//GEN-LAST:event_jbtDecodeMultipleActionPerformed

private void on_close(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_on_close

    if (decodingThread!=null && decodingThread.isAlive()) {
        handleStop(null);
    }
    
    setVisible(false);
}//GEN-LAST:event_on_close

private void jbtDetachActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jbtDetachActionPerformed
    detachDecoder();
}//GEN-LAST:event_jbtDetachActionPerformed

private void jbtStopActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jbtStopActionPerformed

    handleStop(evt);

}//GEN-LAST:event_jbtStopActionPerformed

private void jtfSampleActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jtfSampleActionPerformed
    jbtJumpActionPerformed(evt);
}//GEN-LAST:event_jtfSampleActionPerformed

    private void onSignalSourceChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_onSignalSourceChanged
        
        String s = (String)evt.getItem();
        
        if (s.equalsIgnoreCase("WAVE file")) {
            if (evt.getStateChange()==ItemEvent.SELECTED) {
                setWaveInputSourceComponentsEnabled(true);
            }  
            else {
                setWaveInputSourceComponentsEnabled(false);
            }
        }
    
    }//GEN-LAST:event_onSignalSourceChanged

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new DecoderFrame().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton btnClearLog;
    private javax.swing.JFileChooser fcDecodeInput;
    private javax.swing.JFileChooser fcDecodeOutput;
    private javax.swing.Box.Filler filler1;
    private javax.swing.Box.Filler filler2;
    private javax.swing.Box.Filler filler3;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTabbedPane jTabbedPane1;
    private javax.swing.JButton jbtAttach;
    private javax.swing.JButton jbtBrowseDir;
    private javax.swing.JButton jbtBrowseWave;
    private javax.swing.JButton jbtDecode;
    private javax.swing.JButton jbtDecodeMultiple;
    private javax.swing.JButton jbtDetach;
    private javax.swing.JButton jbtJump;
    private javax.swing.JButton jbtStop;
    private javax.swing.JCheckBox jcbCsTurboAlwaysSaveAsBinaryFile;
    private javax.swing.JCheckBox jcbCsTurboSaveHeaderToExtraFile;
    private javax.swing.JCheckBox jcbIgnoreBadChecksums;
    private javax.swing.JCheckBox jcbMonitorMode;
    private javax.swing.JCheckBox jcbMonitorSaveAllBytes;
    private javax.swing.JCheckBox jcbPreferAdaptiveTransferSpeedDetection;
    private javax.swing.JCheckBox jcbPrependSampleNumber;
    private javax.swing.JComboBox jcoASTFormat;
    private javax.swing.JComboBox jcoDecoders;
    private javax.swing.JComboBox jcoSampleRate;
    private javax.swing.JComboBox jcoSignalSource;
    private javax.swing.JComboBox jcoSourceChannel;
    private javax.swing.JComboBox jcoTurboROMFormat;
    private javax.swing.JLabel jlbASTFormat;
    private javax.swing.JLabel jlbDecoder;
    private javax.swing.JLabel jlbImpulse;
    private javax.swing.JLabel jlbSampleRate;
    private javax.swing.JLabel jlbSignalSource;
    private javax.swing.JLabel jlbSourceChannel;
    private javax.swing.JLabel jlbTurboROMFormat;
    private javax.swing.JLabel jlbWave;
    private javax.swing.JSlider jslNavigation;
    private javax.swing.JScrollPane jspSettings;
    private javax.swing.JTextArea jtaLog;
    private javax.swing.JTextField jtfOutDir;
    private javax.swing.JTextField jtfSample;
    private javax.swing.JTextField jtfWaveFile;
    private javax.swing.JPanel pASTSettings;
    private javax.swing.JPanel pAttachDetach;
    private javax.swing.JPanel pControls;
    private javax.swing.JPanel pDeco;
    private javax.swing.JPanel pDecoding;
    private javax.swing.JPanel pFiles;
    private javax.swing.JPanel pGeneralSettings;
    private javax.swing.JPanel pInputParams;
    private javax.swing.JPanel pLog;
    private javax.swing.JPanel pMonitorModeSettings;
    private javax.swing.JPanel pNav;
    private javax.swing.JPanel pSettingsCover;
    private javax.swing.JPanel pTurbo2000SuperTurbo;
    private javax.swing.JPanel pTurboROMSettings;
    // End of variables declaration//GEN-END:variables
    
    
    public void performDecoding(boolean automated) {

        final boolean _automat;
        final FileDecoder _decoder;
        final String _outdir;
        final DecoderConfig _prm;
        
        /*Choose Turbo type*/
        int turboType = jcoDecoders.getSelectedIndex();
        if (turboType<0) return;
        
        /*Set sample according to navigation slider*/
        try {
            decoder.setCurrentSample(jslNavigation.getValue());
        }
        catch(Exception e) {
            e.printStackTrace();
        }
        
        /*What turbo system?*/
        if (jcbMonitorMode.isSelected()) {
            _decoder = new TurboMonitorDecoder(turboType);
        }
        else {

            switch(turboType) {
                case 0: {
                    _decoder = new Turbo2000Decoder();
                    break;
                }
                case 1: {
                    _decoder = new KBlockDecoder();
                    break;
                }
                case 2: {
                    _decoder = new UniversalTurboFileDecoder();
                    break;
                }

                case 3: {
                    _decoder = new TurboTapeDecoder();
                    break;
                }

                case 4: {
                    _decoder = new KSOTurbo2000FileDecoder();
                    break;
                }
                case 5: {
                    _decoder = new TurboBlizzardFileDecoder();
                    break;
                }
                case 6: {
                    _decoder = new TurboRomFileDecoder();
                    break;
                }
                case 7: {
                    _decoder = new ASTFileDecoder();
                    break;
                }

                default: {
                    _decoder = null;
                }
            }
        }
        
        /*Try to set parameters of the decoder. Always get a copy for given
         decoder invocation*/
         _prm=getConfigFromDialog();
        
        if (_prm==null) {
            JOptionPane.showMessageDialog(this, "Settings of decoder are incorrect");
            return;
        }
        
        _outdir=jtfOutDir.getText().trim();
        _automat=automated;
        
        /*Run the thread*/
        decodingThread = new Thread(new Runnable() {
            public void run() {
                synchronized (decoderSyncronizer) {
                    runFileDecoder(_decoder, _automat, _outdir,_prm);
                }
            }
        });
         
        decodingThread.start();
        
    }
    
    void setAttachDependentControlsEnabled(boolean b) {
        
        /*Decoder type independent elements to be enabled*/
        jbtDecode.setEnabled(b);
        jslNavigation.setEnabled(b);
        jtfSample.setEnabled(b);
        jbtJump.setEnabled(b);
        jcoDecoders.setEnabled(b);
        jbtDecodeMultiple.setEnabled(b);
        jlbDecoder.setEnabled(b);
        jbtDetach.setEnabled(b);
        
        /*Decoder type independent elements to be disabled*/
        b=!b;
        jtfWaveFile.setEnabled(b);
        jbtBrowseWave.setEnabled(b);
        jlbWave.setEnabled(b);
        jbtAttach.setEnabled(b);
        jcoSourceChannel.setEnabled(b);
        jlbSignalSource.setEnabled(b);
        jcoSignalSource.setEnabled(b);
        jlbSourceChannel.setEnabled(b);
        jcoSampleRate.setEnabled(b);
        jlbSampleRate.setEnabled(b);
        
        handleComponentDependencies();
        
    }

    public void addMessage(String s,boolean fromExtThread) {
        
        final String k=s;
        
        if (fromExtThread==false) {
            jtaLog.append(s);
            jtaLog.append(Turgen.ln);
        }
        else {
            try {
                SwingUtilities.invokeAndWait(new Runnable() {
                    public void run() {
                        jtaLog.append(k);
                        jtaLog.append(Turgen.ln);
                    }
                });
                }
            
            catch(InterruptedException ie) {
            }
            catch(InvocationTargetException ite) {
            }
}
            
    }
    

    public void clear() {
        jtaLog.setText("");
    }
    
    
    
    private DecoderConfig getConfigFromDialog() {
        DecoderConfig retVal=new DecoderConfig();
        flushAll(retVal);
        return retVal;
    }

    
    void flushGenConfig(DecoderConfig d) {
        d.genIgnoreBadSum=jcbIgnoreBadChecksums.isSelected();
        d.genPreferAdaptiveSpeedDetection=jcbPreferAdaptiveTransferSpeedDetection.isSelected();
        d.genPrependSampleNumber=jcbPrependSampleNumber.isSelected();
    }
    
    void flushMonitorModeConfig(DecoderConfig d) {
        d.monitorSaveAllBytes=jcbMonitorSaveAllBytes.isSelected();
    }
    
    void flushTurbo2000AndSuperTurboConfig(DecoderConfig d) {
        d.csTurboSaveHeaderToExtraFile=jcbCsTurboSaveHeaderToExtraFile.isSelected();
        d.csTurboAlwaysSaveAsBinary=jcbCsTurboAlwaysSaveAsBinaryFile.isSelected();
    }
    
    void flushTurboROMConfig(DecoderConfig d) {
        d.turboROMFileFormat=jcoTurboROMFormat.getSelectedIndex();
    }
    
    void suckGenConfig(DecoderConfig d) {
        jcbIgnoreBadChecksums.setSelected(d.genIgnoreBadSum);
        jcbPreferAdaptiveTransferSpeedDetection.setSelected(d.genPreferAdaptiveSpeedDetection);
        jcbPrependSampleNumber.setSelected(d.genPrependSampleNumber);
    }
    
    void suckMonitorModeConfig(DecoderConfig d) {
        jcbMonitorSaveAllBytes.setSelected(d.monitorSaveAllBytes);
    }
    
    void suckTurbo2000AndSuperTurboConfig(DecoderConfig d) {
        jcbCsTurboSaveHeaderToExtraFile.setSelected(d.csTurboSaveHeaderToExtraFile);
        jcbCsTurboAlwaysSaveAsBinaryFile.setSelected(d.csTurboAlwaysSaveAsBinary);
    }
    
    void suckTurboROMConfig(DecoderConfig d) {
        jcoTurboROMFormat.setSelectedIndex(d.turboROMFileFormat);
    }
    
    void flushAll(DecoderConfig d) {
        flushGenConfig(d);
        flushMonitorModeConfig(d);
        flushTurbo2000AndSuperTurboConfig(d);
        flushTurboROMConfig(d);
    }

    void suckAll(DecoderConfig d) {
        suckGenConfig(d);
        suckMonitorModeConfig(d);
        suckTurbo2000AndSuperTurboConfig(d);
        suckTurboROMConfig(d);
    }

    final void restoreDefaults() {
        DecoderConfig d = new DecoderConfig();
        flushAll(d);
    }
    
    void updateFileDialogUI() {
        SwingUtilities.updateComponentTreeUI(fcDecodeInput);
        SwingUtilities.updateComponentTreeUI(fcDecodeOutput);
    }
    
    void runFileDecoder(FileDecoder d,boolean multiple,String outdir,DecoderConfig config) {

        boolean cont;

        /*Disable components*/
        ParallelGUIUpdater.setComponentsEnabled(decodingInvocationComponents, false);
        ParallelGUIUpdater.setComponentsEnabled(decodingCancelationComponents, true);
        
        /*Single*/
        if (multiple==false) {
            try {
                d.decodeFile(outdir, this, decoder,config);
                ParallelGUIUpdater.updateSlider(jslNavigation,(int)decoder.getCurrentSample());
            }
            catch(Exception e) {
                e.printStackTrace();
            }
        }
        /*Multiple*/
        else {
            try {
                do {
                    cont=d.decodeFile(outdir,this,decoder,config);
                    ParallelGUIUpdater.updateSlider(jslNavigation,(int)decoder.getCurrentSample());
                }
                while (cont==true);
            }
            catch (Exception e) {
               e.printStackTrace();
            }
        }
        
        /*Enable components*/
        ParallelGUIUpdater.setComponentsEnabled(decodingInvocationComponents, true);
        ParallelGUIUpdater.setComponentsEnabled(decodingCancelationComponents, false);
        
    }

    /** Handle stopping of the decoder*/
    private void handleStop(java.awt.event.ActionEvent evt) {

        /*Window is closed, only stop*/
        if (evt==null) {
            decoder.requestStop(false);
            return;
        }

        /*Stop button is pressed - WAVE*/
        if (decoder instanceof WavePulseDecoder) {
            decoder.requestStop(false);
        }
        
        /*Stop button for Audio input - SHIFT means emergency STOP*/
        else {
            boolean b=false;
            if ( (evt.getModifiers() & ActionEvent.SHIFT_MASK)!=0) b=true;
            decoder.requestStop(b);

            /*Instruct user to detach the decoder*/
            if (b==true) {
                JOptionPane.showMessageDialog(this, "<HTML>Emergency stop of the decoding was requested.<BR>Decoder must be <U>detached</U> before it can be used again!", "Emergency stop", JOptionPane.WARNING_MESSAGE);
            }
        }

    }

    public Object[] getPersistenceData() {
        Object[] pData = new Object[12];

        /*Bounds and frame state*/
        pData[0]=getBounds();
        pData[1]=new Integer(getExtendedState());

        /*File chooser and text field directories*/
        pData[2]=fcDecodeInput.getCurrentDirectory().getAbsolutePath();
        pData[3]=fcDecodeOutput.getCurrentDirectory().getAbsolutePath();

        pData[4]=jtfOutDir.getText();
        pData[5]=jtfWaveFile.getText();

        /*Decoder configuration*/
        DecoderConfig d = new DecoderConfig();
        flushAll(d);
        pData[6]=d;

        /*Pulse decoder and file decoder*/
        pData[7]=new Integer(jcoSignalSource.getSelectedIndex());
        pData[8]=new Integer(jcoSourceChannel.getSelectedIndex());
        pData[9]=new Integer(jcoDecoders.getSelectedIndex());
        pData[10]=new Integer(jcoSampleRate.getSelectedIndex());
        pData[11]=Boolean.valueOf(jcbMonitorMode.isSelected());
        

        return pData;

    }

    public void setPersistenceData(Object[] data) throws Exception {

        /*Bounds and frame state*/
        setBounds((Rectangle)data[0]);
        setExtendedState(((Integer)data[1]).intValue());

        /*File chooser and text field directories*/
        fcDecodeInput.setCurrentDirectory(new File((String)data[2]));
        fcDecodeOutput.setCurrentDirectory(new File((String)data[3]));

        jtfOutDir.setText((String)data[4]);
        jtfWaveFile.setText((String)data[5]);
        
        /*Pulse decoder and file decoder*/
        jcoSignalSource.setSelectedIndex(((Integer)data[7]).intValue());
        jcoSourceChannel.setSelectedIndex(((Integer)data[8]).intValue());
        jcoDecoders.setSelectedIndex(((Integer)data[9]).intValue());
        jcoSampleRate.setSelectedIndex(((Integer)data[10]).intValue());
        jcbMonitorMode.setSelected(((Boolean)data[11]).booleanValue());
        
        /*Decoders' configuration - volatile, so the last one*/
        DecoderConfig d = (DecoderConfig)data[6];
        suckAll(d);

    }

    public String getPersistenceId() {
        return getClass().getName();
    }

    public void setPersistenceDefaults() {
        DecoderConfig d = new DecoderConfig();
        suckAll(d);
        jcoSignalSource.setSelectedIndex(0);
        jcoDecoders.setSelectedIndex(0);
        
    }
    
    private void handleComponentDependencies() {
        
        if (!jcoSignalSource.isEnabled()) return;
        
        String s = (String)jcoSignalSource.getSelectedItem();
        if (s.equalsIgnoreCase("WAVE file")) {
            setWaveInputSourceComponentsEnabled(true);
        }
        else {
            setWaveInputSourceComponentsEnabled(false);
        }
    }
    
    private void setWaveInputSourceComponentsEnabled(boolean b) {
        jtfWaveFile.setEnabled(b);
        jbtBrowseWave.setEnabled(b);
        jlbWave.setEnabled(b);
        jcoSampleRate.setEnabled(!b);
        jlbSampleRate.setEnabled(!b);
    }

    public void impulse(boolean fromExtThread) {

        if (fromExtThread == false) {
            impulsePhase=(impulsePhase+1)%4;
            jlbImpulse.setText(impulseStrings[impulsePhase]);
        } else {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    impulsePhase=(impulsePhase+1)%4;
                    jlbImpulse.setText(impulseStrings[impulsePhase]);
                }
            });
        }

    }

    
    
}
