
package turgen.tapeimageextractor;

import java.awt.Font;
import java.awt.Rectangle;
import java.awt.event.ItemEvent;
import java.awt.event.MouseEvent;
import java.io.File;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import turgen.DialogManager;
import turgen.UIPersistor;
import turgen.Utils;


public class ExtractorDialog extends javax.swing.JDialog implements UIPersistor,ListSelectionListener,ExtractionReporter {
    
    private static final int MAGNIFICATION_SMALL=0;
    private static final int MAGNIFICATION_MEDIUM=1;
    private static final int MAGNIFICATION_LARGE=2;
    private static final int MAGNIFICATION_EXTRA_LARGE=3;
    private static final int MAGNIFICATION_NONE=-1;
    private final Font[] monospacedFonts;
    private int lastMagnification=MAGNIFICATION_NONE;
    
    private String extractBinaryDirectory;
    private Rectangle extractBinaryBounds;

    /**
     * Creates new form ExtractorFrame
     */
    public ExtractorDialog() {
        
        monospacedFonts = new Font[4];
        monospacedFonts[0]= new Font("Monospaced",Font.PLAIN,12);
        monospacedFonts[1]= new Font("Monospaced",Font.PLAIN,14);
        monospacedFonts[2]= new Font("Monospaced",Font.PLAIN,16);
        monospacedFonts[3]= new Font("Monospaced",Font.PLAIN,18);
                
        setModal(true);
        tapeImageModel = new TapeImageModel();
        initComponents();
        jtbChunks.getSelectionModel().addListSelectionListener(this);
        setSelectionSensitiveControlsEnabled(false);
    }

    /**
     * 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.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        fcOpenTapeImage = new javax.swing.JFileChooser();
        fcSaveData = new javax.swing.JFileChooser();
        bgMagnify = new javax.swing.ButtonGroup();
        fcSaveChunks = new javax.swing.JFileChooser();
        jtbTools = new javax.swing.JToolBar();
        jbtOpen = new javax.swing.JButton();
        jSeparator3 = new javax.swing.JToolBar.Separator();
        jbtSelectAll = new javax.swing.JButton();
        jSeparator4 = new javax.swing.JToolBar.Separator();
        jbtSetExtractionHint = new javax.swing.JButton();
        jbtExtractAsIs = new javax.swing.JButton();
        jSeparator5 = new javax.swing.JToolBar.Separator();
        jbtExtractAndEmbed = new javax.swing.JButton();
        tabTabs = new javax.swing.JTabbedPane();
        jspliChunks = new javax.swing.JSplitPane();
        jspChunks = new javax.swing.JScrollPane();
        jtbChunks = new javax.swing.JTable();
        pChunkData = new javax.swing.JPanel();
        jspChunkData = new javax.swing.JScrollPane();
        jtaChunkData = new javax.swing.JTextArea();
        pChunkDataControls = new javax.swing.JPanel();
        jlbChunkNumber = new javax.swing.JLabel();
        pReport = new javax.swing.JPanel();
        jspReport = new javax.swing.JScrollPane();
        jtaReport = new javax.swing.JTextArea();
        pReportButtons = new javax.swing.JPanel();
        jbtClearReport = new javax.swing.JButton();
        pStatus = new javax.swing.JPanel();
        jlbTapeImageFileName = new javax.swing.JLabel();
        jmbMainMenu = new javax.swing.JMenuBar();
        jmFile = new javax.swing.JMenu();
        jmiOpen = new javax.swing.JMenuItem();
        jmiAppend = new javax.swing.JMenuItem();
        jSeparator8 = new javax.swing.JPopupMenu.Separator();
        jmiSaveChunks = new javax.swing.JMenuItem();
        jSeparator1 = new javax.swing.JPopupMenu.Separator();
        jmiExit = new javax.swing.JMenuItem();
        jmSelect = new javax.swing.JMenu();
        jmiSelectAll = new javax.swing.JMenuItem();
        jmiSelectNone = new javax.swing.JMenuItem();
        jmView = new javax.swing.JMenu();
        jmiMagnifySmall = new javax.swing.JRadioButtonMenuItem();
        jmiMagnifyMedium = new javax.swing.JRadioButtonMenuItem();
        jmiMagnifyLarge = new javax.swing.JRadioButtonMenuItem();
        jmiMagnifyExtraLarge = new javax.swing.JRadioButtonMenuItem();
        jSeparator7 = new javax.swing.JPopupMenu.Separator();
        jmiShowChunkData = new javax.swing.JCheckBoxMenuItem();
        jmChunk = new javax.swing.JMenu();
        jmiSetExtractionHint = new javax.swing.JMenuItem();
        jSeparator2 = new javax.swing.JPopupMenu.Separator();
        jmiExtractAsIs = new javax.swing.JMenuItem();
        jmiExtractToBinaryFile = new javax.swing.JMenuItem();
        jSeparator6 = new javax.swing.JPopupMenu.Separator();
        jmiRevertHints = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setTitle("Tape Image Extractor");

        jtbTools.setFloatable(false);
        jtbTools.setRollover(true);

        jbtOpen.setIcon(new javax.swing.ImageIcon(getClass().getResource("/turgen/resources/action_edit.png"))); // NOI18N
        jbtOpen.setText("Open");
        jbtOpen.setFocusable(false);
        jbtOpen.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        jbtOpen.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        jbtOpen.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onToolOpen(evt);
            }
        });
        jtbTools.add(jbtOpen);
        jtbTools.add(jSeparator3);

        jbtSelectAll.setIcon(new javax.swing.ImageIcon(getClass().getResource("/turgen/resources/action_all.png"))); // NOI18N
        jbtSelectAll.setText("Select all");
        jbtSelectAll.setFocusable(false);
        jbtSelectAll.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        jbtSelectAll.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        jbtSelectAll.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onToolSelectAll(evt);
            }
        });
        jtbTools.add(jbtSelectAll);
        jtbTools.add(jSeparator4);

        jbtSetExtractionHint.setIcon(new javax.swing.ImageIcon(getClass().getResource("/turgen/resources/action_instructions.png"))); // NOI18N
        jbtSetExtractionHint.setText("Set hint");
        jbtSetExtractionHint.setFocusable(false);
        jbtSetExtractionHint.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        jbtSetExtractionHint.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        jbtSetExtractionHint.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onToolSetExtractionHint(evt);
            }
        });
        jtbTools.add(jbtSetExtractionHint);

        jbtExtractAsIs.setIcon(new javax.swing.ImageIcon(getClass().getResource("/turgen/resources/action_image.png"))); // NOI18N
        jbtExtractAsIs.setText("Extract data");
        jbtExtractAsIs.setFocusable(false);
        jbtExtractAsIs.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        jbtExtractAsIs.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        jbtExtractAsIs.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onToolExtractAsIs(evt);
            }
        });
        jtbTools.add(jbtExtractAsIs);
        jtbTools.add(jSeparator5);

        jbtExtractAndEmbed.setIcon(new javax.swing.ImageIcon(getClass().getResource("/turgen/resources/extract_embed_to_binary.png"))); // NOI18N
        jbtExtractAndEmbed.setText("Extract to binary");
        jbtExtractAndEmbed.setFocusable(false);
        jbtExtractAndEmbed.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        jbtExtractAndEmbed.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        jbtExtractAndEmbed.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onToolExtractToBinary(evt);
            }
        });
        jtbTools.add(jbtExtractAndEmbed);

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

        jspliChunks.setDividerSize(8);
        jspliChunks.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);

        jspChunks.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N

        jtbChunks.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N
        jtbChunks.setModel(tapeImageModel);
        jtbChunks.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);
        jtbChunks.setSelectionMode(javax.swing.ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        jtbChunks.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                onChunksTableMouseClicked(evt);
            }
        });
        jspChunks.setViewportView(jtbChunks);

        jspliChunks.setLeftComponent(jspChunks);

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

        jtaChunkData.setEditable(false);
        jtaChunkData.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N
        jtaChunkData.setEnabled(false);
        jspChunkData.setViewportView(jtaChunkData);

        pChunkData.add(jspChunkData, java.awt.BorderLayout.CENTER);

        pChunkDataControls.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 0, 0));
        pChunkDataControls.add(jlbChunkNumber);

        pChunkData.add(pChunkDataControls, java.awt.BorderLayout.NORTH);

        jspliChunks.setRightComponent(pChunkData);

        tabTabs.addTab("Chunks", jspliChunks);

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

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

        pReport.add(jspReport, java.awt.BorderLayout.CENTER);

        jbtClearReport.setText("Clear");
        jbtClearReport.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onClearReport(evt);
            }
        });
        pReportButtons.add(jbtClearReport);

        pReport.add(pReportButtons, java.awt.BorderLayout.SOUTH);

        tabTabs.addTab("Report", pReport);

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

        pStatus.setLayout(new java.awt.BorderLayout());
        pStatus.add(jlbTapeImageFileName, java.awt.BorderLayout.WEST);

        getContentPane().add(pStatus, java.awt.BorderLayout.PAGE_END);

        jmFile.setMnemonic('F');
        jmFile.setText("File");

        jmiOpen.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, java.awt.event.InputEvent.CTRL_MASK));
        jmiOpen.setIcon(new javax.swing.ImageIcon(getClass().getResource("/turgen/resources/document-open.png"))); // NOI18N
        jmiOpen.setMnemonic('O');
        jmiOpen.setText("Open tape image...");
        jmiOpen.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onMenuOpen(evt);
            }
        });
        jmFile.add(jmiOpen);

        jmiAppend.setMnemonic('A');
        jmiAppend.setText("Append tape image...");
        jmiAppend.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onMenuAppend(evt);
            }
        });
        jmFile.add(jmiAppend);
        jmFile.add(jSeparator8);

        jmiSaveChunks.setMnemonic('S');
        jmiSaveChunks.setText("Save chunks...");
        jmiSaveChunks.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onSaveChunks(evt);
            }
        });
        jmFile.add(jmiSaveChunks);
        jmFile.add(jSeparator1);

        jmiExit.setIcon(new javax.swing.ImageIcon(getClass().getResource("/turgen/resources/system-log-out.png"))); // NOI18N
        jmiExit.setMnemonic('x');
        jmiExit.setText("Exit");
        jmiExit.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onMenuExit(evt);
            }
        });
        jmFile.add(jmiExit);

        jmbMainMenu.add(jmFile);

        jmSelect.setMnemonic('S');
        jmSelect.setText("Select");

        jmiSelectAll.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_A, java.awt.event.InputEvent.CTRL_MASK));
        jmiSelectAll.setIcon(new javax.swing.ImageIcon(getClass().getResource("/turgen/resources/action_all_small.png"))); // NOI18N
        jmiSelectAll.setMnemonic('A');
        jmiSelectAll.setText("All chunks");
        jmiSelectAll.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onMenuSelectAll(evt);
            }
        });
        jmSelect.add(jmiSelectAll);

        jmiSelectNone.setMnemonic('N');
        jmiSelectNone.setText("No chunks");
        jmiSelectNone.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onMenuSelectNone(evt);
            }
        });
        jmSelect.add(jmiSelectNone);

        jmbMainMenu.add(jmSelect);

        jmView.setMnemonic('V');
        jmView.setText("View");

        jmiMagnifySmall.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_1, java.awt.event.InputEvent.CTRL_MASK));
        bgMagnify.add(jmiMagnifySmall);
        jmiMagnifySmall.setMnemonic('S');
        jmiMagnifySmall.setSelected(true);
        jmiMagnifySmall.setText("Small font");
        jmiMagnifySmall.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onMagnificationChanged(evt);
            }
        });
        jmView.add(jmiMagnifySmall);

        jmiMagnifyMedium.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_2, java.awt.event.InputEvent.CTRL_MASK));
        bgMagnify.add(jmiMagnifyMedium);
        jmiMagnifyMedium.setMnemonic('M');
        jmiMagnifyMedium.setText("Medium font");
        jmiMagnifyMedium.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onMagnificationChanged(evt);
            }
        });
        jmView.add(jmiMagnifyMedium);

        jmiMagnifyLarge.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_3, java.awt.event.InputEvent.CTRL_MASK));
        bgMagnify.add(jmiMagnifyLarge);
        jmiMagnifyLarge.setMnemonic('L');
        jmiMagnifyLarge.setText("Large font");
        jmiMagnifyLarge.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onMagnificationChanged(evt);
            }
        });
        jmView.add(jmiMagnifyLarge);

        jmiMagnifyExtraLarge.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_4, java.awt.event.InputEvent.CTRL_MASK));
        bgMagnify.add(jmiMagnifyExtraLarge);
        jmiMagnifyExtraLarge.setMnemonic('X');
        jmiMagnifyExtraLarge.setText("Extra Large font");
        jmiMagnifyExtraLarge.setToolTipText("");
        jmiMagnifyExtraLarge.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onMagnificationChanged(evt);
            }
        });
        jmView.add(jmiMagnifyExtraLarge);
        jmView.add(jSeparator7);

        jmiShowChunkData.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_D, java.awt.event.InputEvent.CTRL_MASK));
        jmiShowChunkData.setMnemonic('d');
        jmiShowChunkData.setText("Show chunk data");
        jmiShowChunkData.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {
                onShowChunkDataChanged(evt);
            }
        });
        jmView.add(jmiShowChunkData);

        jmbMainMenu.add(jmView);

        jmChunk.setMnemonic('C');
        jmChunk.setText("Chunks");

        jmiSetExtractionHint.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_H, java.awt.event.InputEvent.CTRL_MASK));
        jmiSetExtractionHint.setIcon(new javax.swing.ImageIcon(getClass().getResource("/turgen/resources/action_instructions_small.png"))); // NOI18N
        jmiSetExtractionHint.setMnemonic('S');
        jmiSetExtractionHint.setText("Set data extraction hint...");
        jmiSetExtractionHint.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onMenuSetExtractionHint(evt);
            }
        });
        jmChunk.add(jmiSetExtractionHint);
        jmChunk.add(jSeparator2);

        jmiExtractAsIs.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_E, java.awt.event.InputEvent.CTRL_MASK));
        jmiExtractAsIs.setIcon(new javax.swing.ImageIcon(getClass().getResource("/turgen/resources/document-save.png"))); // NOI18N
        jmiExtractAsIs.setMnemonic('E');
        jmiExtractAsIs.setText("Extract data...");
        jmiExtractAsIs.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                OnMenuExtractAsIs(evt);
            }
        });
        jmChunk.add(jmiExtractAsIs);

        jmiExtractToBinaryFile.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_B, java.awt.event.InputEvent.CTRL_MASK));
        jmiExtractToBinaryFile.setIcon(new javax.swing.ImageIcon(getClass().getResource("/turgen/resources/extract_embed_to_binary_small.png"))); // NOI18N
        jmiExtractToBinaryFile.setText("Extract data to binary file...");
        jmiExtractToBinaryFile.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onMenuExtractToBinary(evt);
            }
        });
        jmChunk.add(jmiExtractToBinaryFile);
        jmChunk.add(jSeparator6);

        jmiRevertHints.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_BACK_SPACE, java.awt.event.InputEvent.CTRL_MASK));
        jmiRevertHints.setMnemonic('R');
        jmiRevertHints.setText("Reset data extraction hint");
        jmiRevertHints.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onResetExtrationHint(evt);
            }
        });
        jmChunk.add(jmiRevertHints);

        jmbMainMenu.add(jmChunk);

        setJMenuBar(jmbMainMenu);

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

    private void onMenuOpen(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onMenuOpen
        
        openTapeImage(false);
        
    }//GEN-LAST:event_onMenuOpen

    private void onToolOpen(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onToolOpen
        onMenuOpen(evt);
    }//GEN-LAST:event_onToolOpen

    private void onMenuSelectAll(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onMenuSelectAll
        jtbChunks.selectAll();
    }//GEN-LAST:event_onMenuSelectAll

    private void onToolSelectAll(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onToolSelectAll
        onMenuSelectAll(evt);
    }//GEN-LAST:event_onToolSelectAll

    private void onMenuExit(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onMenuExit
        tapeImageModel.clear();
        setVisible(false);
        dispose();
    }//GEN-LAST:event_onMenuExit

    private void onMenuSelectNone(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onMenuSelectNone
        jtbChunks.clearSelection();
    }//GEN-LAST:event_onMenuSelectNone

    private void onMenuSetExtractionHint(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onMenuSetExtractionHint
        
        /*Are there any chunks selected ?*/
        ListSelectionModel lsm = jtbChunks.getSelectionModel();
        if (lsm.isSelectionEmpty()==true) return;
        
        int[] indexes = jtbChunks.getSelectedRows();
        
        int lastHead=-1;
        int lastTail=-1;
        int lastLengthType=-1;
        int lastLengthLo=-1;
        int lastLengthHi=-1;
        int lastExtractible=-1;
        
        int numHeadChanges=0;
        int numTailChanges=0;
        int numLengthTypeChanges=0;
        int numLengthHiChanges=0;
        int numLengthLoChanges=0;
        int numExtractibleChanges=0;
        
        
        for (int i=0;i<indexes.length;i++) {
            ExtractorChunk chunk = tapeImageModel.elementAt(indexes[i]);
            
            int cf,cl,clol,cloh,clt,cex;
            
            HintsCrate hints = chunk.getHintsCrate();
            
            cf=hints.skipHead;
            cl=hints.skipTail;
            clol=hints.lengthOffsetLow;
            cloh=hints.lengthOffsetHigh;
            clt=hints.lengthType;
            cex=hints.extractData?1:0;
            
            if (cf!=lastHead) {
                numHeadChanges++;
                lastHead=cf;
            }
            if (cl!=lastTail) {
                numTailChanges++;
                lastTail=cl;
            }
            if (clt!=lastLengthType) {
                numLengthTypeChanges++;
                lastLengthType=clt;
            }
            if (clol!=lastLengthLo) {
                numLengthLoChanges++;
                lastLengthLo=clol;
            }
            if (cloh!=lastLengthHi) {
                numLengthHiChanges++;
                lastLengthHi=cloh;
            }
            if (cex!=lastExtractible) {
                numExtractibleChanges++;
                lastExtractible=cex;
            }
            
        }
        
        HintsCrate.Map map = new HintsCrate.Map(true);
        
        /*Now check the common values*/
        if (numHeadChanges!=1) {
            map.skipHead=false;
        }
        if (numTailChanges!=1) {
            map.skipTail=false;
        }
        if (numLengthTypeChanges!=1) {
            map.lengthType=false;
        }
        if (numLengthLoChanges!=1) {
            map.lengthOffsetLow=false;
        }
        if (numLengthHiChanges!=1) {
            map.lengthOffsetHigh=false;
        }
        if (numExtractibleChanges!=1) {
            map.extractData=false;
        }
        
        HintsCrate newHints = new HintsCrate(lastHead,lastTail,lastLengthLo,lastLengthHi,lastLengthType,lastExtractible==0?false:true);
        
        /*Create the dialog*/
        if (setHintDialog==null) {
            setHintDialog = new SetHintDialog();
            setHintDialog.pack();
            DialogManager.centerContainer(setHintDialog);
        }
        setHintDialog.fillValues(newHints,map);
        setHintDialog.setVisible(true);
        
        HintsCrate crate = setHintDialog.getHintsCrate();
        HintsCrate.Map newMap = setHintDialog.getHintsMap();
        if (crate!=null) {
            tapeImageModel.updateExtractionHints(indexes,crate,newMap);
        }
        /*Restore selection*/
        for(int i=0;i<indexes.length;i++) {
            jtbChunks.getSelectionModel().addSelectionInterval(indexes[i],indexes[i]);
        }
        
    }//GEN-LAST:event_onMenuSetExtractionHint

    private void onToolSetExtractionHint(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onToolSetExtractionHint
        onMenuSetExtractionHint(evt);
    }//GEN-LAST:event_onToolSetExtractionHint

    private void OnMenuExtractAsIs(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_OnMenuExtractAsIs
        /*Are there any chunks selected ?*/
        ListSelectionModel lsm = jtbChunks.getSelectionModel();
        if (lsm.isSelectionEmpty()==true) return;
        
        /*Now we have indexes*/
        int[] indexes = jtbChunks.getSelectedRows();
        
        /*Display file chooser*/
        int result = fcSaveData.showSaveDialog(this);
        if (result!=JFileChooser.APPROVE_OPTION) return;
        
        File f = fcSaveData.getSelectedFile();
        if (f==null) return;
        try {
            jtaReport.setText("");
            ExtractionReportCrate erc = tapeImageModel.extractChunks(indexes, f.getAbsolutePath(),this);
            JOptionPane.showMessageDialog(this, erc.getFormattedMessage());
            
        }
        catch (Exception e) {
            String msg = Utils.getTitledExceptionMessage("Unable to extract chunks", e);
            JOptionPane.showMessageDialog(this, msg);
            e.printStackTrace();
            return;
        }
        
        
        
    }//GEN-LAST:event_OnMenuExtractAsIs

    private void onToolExtractAsIs(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onToolExtractAsIs
        OnMenuExtractAsIs(evt);
    }//GEN-LAST:event_onToolExtractAsIs

    private void onClearReport(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onClearReport
        jtaReport.setText("");
    }//GEN-LAST:event_onClearReport

    private void onMagnificationChanged(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onMagnificationChanged
       int m;
        
        if (evt.getSource()==jmiMagnifySmall) {
            m=MAGNIFICATION_SMALL;
        }
        else if (evt.getSource()==jmiMagnifyMedium) {
            m=MAGNIFICATION_MEDIUM;
        }
        else if (evt.getSource()==jmiMagnifyLarge) {
            m=MAGNIFICATION_LARGE;
        }
        else {
            m=MAGNIFICATION_EXTRA_LARGE;
        }
        onMagnificationChangeImpl(m);
        
        
    }//GEN-LAST:event_onMagnificationChanged

    private void onResetExtrationHint(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onResetExtrationHint
         int[] indexes = jtbChunks.getSelectedRows();
         tapeImageModel.resetHints(indexes);
         /*Restore selection*/
        for(int i=0;i<indexes.length;i++) {
            jtbChunks.getSelectionModel().addSelectionInterval(indexes[i],indexes[i]);
        }
    }//GEN-LAST:event_onResetExtrationHint

    private void onChunksTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_onChunksTableMouseClicked
        if (evt.getClickCount()!=2 || evt.getButton()!=MouseEvent.BUTTON1) return;
            int index =jtbChunks.rowAtPoint(evt.getPoint());
            jtbChunks.getSelectionModel().clearSelection();
            jtbChunks.getSelectionModel().addSelectionInterval(index, index);
            onMenuSetExtractionHint(null);
    }//GEN-LAST:event_onChunksTableMouseClicked

    private void onMenuExtractToBinary(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onMenuExtractToBinary
        if (extractBinaryDialog==null) {
            extractBinaryDialog = new ExtractBinaryDialog(this);
            if (extractBinaryBounds!=null) {
                extractBinaryDialog.setBounds(extractBinaryBounds);
            }
            else {
                extractBinaryDialog.pack();
                DialogManager.centerContainer(extractBinaryDialog);
            }
            extractBinaryDialog.setDirectory(extractBinaryDirectory);
            
        }
        extractBinaryDialog.setTapeImage(tapeImageModel, jtbChunks.getSelectedRows());
        extractBinaryDialog.setVisible(true);
        
    }//GEN-LAST:event_onMenuExtractToBinary

    private void onToolExtractToBinary(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onToolExtractToBinary
        onMenuExtractToBinary(evt);
    }//GEN-LAST:event_onToolExtractToBinary

    private void onShowChunkDataChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_onShowChunkDataChanged
        if (evt.getStateChange()==ItemEvent.SELECTED) {
            jtaChunkData.setEnabled(true);
                displaySelectedChunkData();
            
        }
        else {
            jtaChunkData.setEnabled(false);
            clearChunkData();
        }
    }//GEN-LAST:event_onShowChunkDataChanged

    private void onMenuAppend(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onMenuAppend
        openTapeImage(true);
    }//GEN-LAST:event_onMenuAppend

    private void onSaveChunks(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onSaveChunks
        
        
        ListSelectionModel lsm = jtbChunks.getSelectionModel();
        if (lsm.isSelectionEmpty()==true) return;
        
        /*Display the file chooser*/
        int result = fcSaveChunks.showSaveDialog(this);
        if (result!=JFileChooser.APPROVE_OPTION) return;
        
        
        File f = fcSaveChunks.getSelectedFile();
        if (f==null) return;
        try {
            /*Now we have indexes*/
            int[] indexes = jtbChunks.getSelectedRows();
            tapeImageModel.saveSelectedChunks(f.getAbsolutePath(),indexes);
        }
        catch (Exception e) {
            String msg = Utils.getTitledExceptionMessage("Unable to save tape image chunks", e);
            JOptionPane.showMessageDialog(this, msg);
            e.printStackTrace();
        }
    
    }//GEN-LAST:event_onSaveChunks

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(ExtractorDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(ExtractorDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(ExtractorDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(ExtractorDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new ExtractorDialog().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.ButtonGroup bgMagnify;
    private javax.swing.JFileChooser fcOpenTapeImage;
    private javax.swing.JFileChooser fcSaveChunks;
    private javax.swing.JFileChooser fcSaveData;
    private javax.swing.JPopupMenu.Separator jSeparator1;
    private javax.swing.JPopupMenu.Separator jSeparator2;
    private javax.swing.JToolBar.Separator jSeparator3;
    private javax.swing.JToolBar.Separator jSeparator4;
    private javax.swing.JToolBar.Separator jSeparator5;
    private javax.swing.JPopupMenu.Separator jSeparator6;
    private javax.swing.JPopupMenu.Separator jSeparator7;
    private javax.swing.JPopupMenu.Separator jSeparator8;
    private javax.swing.JButton jbtClearReport;
    private javax.swing.JButton jbtExtractAndEmbed;
    private javax.swing.JButton jbtExtractAsIs;
    private javax.swing.JButton jbtOpen;
    private javax.swing.JButton jbtSelectAll;
    private javax.swing.JButton jbtSetExtractionHint;
    private javax.swing.JLabel jlbChunkNumber;
    private javax.swing.JLabel jlbTapeImageFileName;
    private javax.swing.JMenu jmChunk;
    private javax.swing.JMenu jmFile;
    private javax.swing.JMenu jmSelect;
    private javax.swing.JMenu jmView;
    private javax.swing.JMenuBar jmbMainMenu;
    private javax.swing.JMenuItem jmiAppend;
    private javax.swing.JMenuItem jmiExit;
    private javax.swing.JMenuItem jmiExtractAsIs;
    private javax.swing.JMenuItem jmiExtractToBinaryFile;
    private javax.swing.JRadioButtonMenuItem jmiMagnifyExtraLarge;
    private javax.swing.JRadioButtonMenuItem jmiMagnifyLarge;
    private javax.swing.JRadioButtonMenuItem jmiMagnifyMedium;
    private javax.swing.JRadioButtonMenuItem jmiMagnifySmall;
    private javax.swing.JMenuItem jmiOpen;
    private javax.swing.JMenuItem jmiRevertHints;
    private javax.swing.JMenuItem jmiSaveChunks;
    private javax.swing.JMenuItem jmiSelectAll;
    private javax.swing.JMenuItem jmiSelectNone;
    private javax.swing.JMenuItem jmiSetExtractionHint;
    private javax.swing.JCheckBoxMenuItem jmiShowChunkData;
    private javax.swing.JScrollPane jspChunkData;
    private javax.swing.JScrollPane jspChunks;
    private javax.swing.JScrollPane jspReport;
    private javax.swing.JSplitPane jspliChunks;
    private javax.swing.JTextArea jtaChunkData;
    private javax.swing.JTextArea jtaReport;
    private javax.swing.JTable jtbChunks;
    private javax.swing.JToolBar jtbTools;
    private javax.swing.JPanel pChunkData;
    private javax.swing.JPanel pChunkDataControls;
    private javax.swing.JPanel pReport;
    private javax.swing.JPanel pReportButtons;
    private javax.swing.JPanel pStatus;
    private javax.swing.JTabbedPane tabTabs;
    // End of variables declaration//GEN-END:variables

    
    private final TapeImageModel tapeImageModel;
    private SetHintDialog setHintDialog;
    private ExtractBinaryDialog extractBinaryDialog;
    
    
    public Object[] getPersistenceData() {
        Rectangle r = getBounds();
        Object[] persistenceData = new Object[9];
        persistenceData[0]=r;
        persistenceData[1]=fcOpenTapeImage.getCurrentDirectory().getAbsolutePath();
        
        /*Column sizes in playlist*/
        int cc = jtbChunks.getColumnCount();
        int[] colSizes = new int[cc];
        for(int i=0;i<cc;i++) {
            colSizes[i]=jtbChunks.getColumn(jtbChunks.getColumnName(i)).getWidth();
        }
        persistenceData[2]=colSizes;
        persistenceData[3]=fcSaveData.getCurrentDirectory().getAbsolutePath();
        persistenceData[4]=Integer.valueOf(getMagnification());
        
        if (extractBinaryDialog!=null) {
            persistenceData[5] = extractBinaryDialog.getDirectory();
            persistenceData[6] = extractBinaryDialog.getBounds();
        }
        else {
            persistenceData[5] = null;
            persistenceData[6] = null;
        }
        
        persistenceData[7]=Integer.valueOf(jspliChunks.getDividerLocation());
        persistenceData[8]=fcSaveChunks.getCurrentDirectory().getAbsolutePath();
        
        return persistenceData;
    }

    public void setPersistenceData(Object[] data) throws Exception {
        Rectangle r = (Rectangle)data[0];
        String path1 = (String)data[1];
        int[] colSizes = (int[])data[2];
        String path2 = (String)data[3];
        Integer magnify = (Integer)data[4];
        extractBinaryDirectory = (String)data[5];
        extractBinaryBounds = (Rectangle)data[6];
        
        setBounds(r);
        fcOpenTapeImage.setCurrentDirectory(new File(path1));
        fcSaveData.setCurrentDirectory(new File(path2));
        setMagnification(magnify.intValue());
        
        
        for(int i=0;i<colSizes.length;i++) {
            jtbChunks.getColumn(jtbChunks.getColumnName(i)).setWidth(colSizes[i]);
            jtbChunks.getColumn(jtbChunks.getColumnName(i)).setPreferredWidth(colSizes[i]);
        }
        
        jspliChunks.setDividerLocation(((Integer)data[7]).intValue());
        fcSaveChunks.setCurrentDirectory(new File((String)data[8]));
        
    }

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

    public void setPersistenceDefaults() {
        pack();
        DialogManager.centerContainer(this);
    }

    public void updateFileDialogUI() {
        fcOpenTapeImage.updateUI();
        fcSaveData.updateUI();
    }
    
    private void setSelectionSensitiveControlsEnabled(boolean b) {
        jmiExtractAsIs.setEnabled(b);
        jmiSetExtractionHint.setEnabled(b);
        jbtExtractAsIs.setEnabled(b);
        jbtSetExtractionHint.setEnabled(b);
        jmiRevertHints.setEnabled(b);
        jmiExtractToBinaryFile.setEnabled(b);
        jbtExtractAndEmbed.setEnabled(b);
    }

    public void valueChanged(ListSelectionEvent e) {
        
        if (e.getValueIsAdjusting()) return;
        
        if (jtbChunks.getSelectionModel().isSelectionEmpty()) {
            setSelectionSensitiveControlsEnabled(false);
        }
        else {
            setSelectionSensitiveControlsEnabled(true);
        }
        
        if (jmiShowChunkData.isSelected()) {
            displaySelectedChunkData();
        }
    }

    public void addMessage(String s) {
        jtaReport.append(s);
        jtaReport.append(System.getProperty("line.separator"));
    }
    
    private int getMagnification() {
        if (jmiMagnifySmall.isSelected()) return MAGNIFICATION_SMALL;
        if (jmiMagnifyMedium.isSelected()) return MAGNIFICATION_MEDIUM;
        if (jmiMagnifyLarge.isSelected()) return MAGNIFICATION_LARGE;
        return MAGNIFICATION_EXTRA_LARGE;
    }
    
    private void setMagnification(int m) {
        switch (m) {
            case MAGNIFICATION_SMALL: {
                jmiMagnifySmall.setSelected(true);
                break;
            }
            case MAGNIFICATION_MEDIUM: {
                jmiMagnifyMedium.setSelected(true);
                break;
            }
            case MAGNIFICATION_LARGE: {
                jmiMagnifyLarge.setSelected(true);
                break;
            }
            case MAGNIFICATION_EXTRA_LARGE: {
                jmiMagnifyExtraLarge.setSelected(true);
            }
        }
        onMagnificationChangeImpl(m);
    }
    private void onMagnificationChangeImpl(int magnification) {
        if (lastMagnification!=magnification) {
            Font f = monospacedFonts[magnification];
            jtaReport.setFont(f);
            jtbChunks.setFont(f);
            jtaChunkData.setFont(f);
            lastMagnification=magnification;
        }
    }
    
    private void clearChunkData() {
        jtaChunkData.setText("");
        jlbChunkNumber.setText("");
    }

    private void displaySelectedChunkData() {
        
        int[] indexes = jtbChunks.getSelectedRows();
        if (indexes.length==0) {
            jlbChunkNumber.setText("");
            return;
        }
        
        ExtractorChunk chunk = tapeImageModel.elementAt(indexes[0]);
        jtaChunkData.setText(chunk.getHexStringRepresentation());
        jlbChunkNumber.setText(chunk.getChunkName()+" "+chunk.getId());
        
        
    }
   
    private void openTapeImage(boolean append) {
        int result = fcOpenTapeImage.showOpenDialog(this);
        if (result!=JFileChooser.APPROVE_OPTION) return;
        
        clearChunkData();
        
        File f = fcOpenTapeImage.getSelectedFile();
        if (f==null) return;
        try {
            tapeImageModel.openTapeImage(f.getAbsolutePath(),append);
        }
        catch (Exception e) {
            
            String msg = Utils.getTitledExceptionMessage("Unable to open tape image", e);
            JOptionPane.showMessageDialog(this, msg);
            e.printStackTrace();
        }
    }
    
}
