package turgen;

import java.awt.Rectangle;
import java.io.*;
import javax.swing.*;
import java.util.*;
import turgen.dtb.DOS2Binary;

/**
 * Dialog for merging DOS 2 binaries
 */
public class MergerDialog extends javax.swing.JDialog implements UIPersistor {

    /** Current DOS2 binary*/
    private DOS2Binary ae;
    /** Indicates whether current DTB was analyzed*/
    private boolean analyzed=false;
    
    /**New instance */
    public MergerDialog() {
        setModal(true);
        initComponents();
        
    }

    
    
    /** 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;

        fcMergeInput = new javax.swing.JFileChooser();
        fcMergeOutput = new javax.swing.JFileChooser();
        pButtons = new javax.swing.JPanel();
        btnClose = new javax.swing.JButton();
        pMain = new javax.swing.JPanel();
        pFiles = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        jtfInput = new javax.swing.JTextField();
        jtfOutput = new javax.swing.JTextField();
        btnBrowseInput = new javax.swing.JButton();
        btnBrowseOutput = new javax.swing.JButton();
        pSegments = new javax.swing.JPanel();
        pListing = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        jlsSegments = new javax.swing.JList();
        pControlArea = new javax.swing.JPanel();
        pExtraCode = new javax.swing.JPanel();
        jLabel4 = new javax.swing.JLabel();
        jtfExtraAddress = new javax.swing.JTextField();
        jLabel5 = new javax.swing.JLabel();
        jtfExtraLength = new javax.swing.JTextField();
        jcbNoExtra = new javax.swing.JCheckBox();
        pFiller = new javax.swing.JPanel();
        pActionButtons = new javax.swing.JPanel();
        btnAnalyze = new javax.swing.JButton();
        btnMerge = new javax.swing.JButton();
        btnClean = new javax.swing.JButton();

        fcMergeOutput.setDialogType(javax.swing.JFileChooser.SAVE_DIALOG);

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setTitle("Merge segments of binary file");

        btnClose.setForeground(java.awt.Color.red);
        btnClose.setText("Close");
        btnClose.setToolTipText("Close this dialog");
        btnClose.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnCloseActionPerformed(evt);
            }
        });
        pButtons.add(btnClose);

        getContentPane().add(pButtons, java.awt.BorderLayout.SOUTH);

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

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

        jLabel1.setText("Input file:");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        pFiles.add(jLabel1, gridBagConstraints);

        jLabel2.setText("Output File:");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        pFiles.add(jLabel2, gridBagConstraints);

        jtfInput.setColumns(24);
        jtfInput.setToolTipText("Input binary file whose segments will be merged");
        jtfInput.setMinimumSize(jtfInput.getPreferredSize());
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.ipadx = 5;
        gridBagConstraints.weightx = 0.8;
        gridBagConstraints.weighty = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
        pFiles.add(jtfInput, gridBagConstraints);

        jtfOutput.setColumns(24);
        jtfOutput.setToolTipText("Output binary file");
        jtfOutput.setMinimumSize(jtfOutput.getPreferredSize());
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.ipadx = 5;
        gridBagConstraints.weightx = 0.8;
        gridBagConstraints.weighty = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
        pFiles.add(jtfOutput, gridBagConstraints);

        btnBrowseInput.setMnemonic('b');
        btnBrowseInput.setText("Browse");
        btnBrowseInput.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnBrowseInputActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.weighty = 0.1;
        gridBagConstraints.insets = new java.awt.Insets(3, 2, 3, 2);
        pFiles.add(btnBrowseInput, gridBagConstraints);

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

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 4, 0);
        pMain.add(pFiles, gridBagConstraints);

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

        pListing.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "List of segments:"));
        pListing.setLayout(new java.awt.BorderLayout(4, 0));

        jScrollPane1.setPreferredSize(new java.awt.Dimension(230, 131));

        jlsSegments.setToolTipText("List of segments");
        jScrollPane1.setViewportView(jlsSegments);

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

        pSegments.add(pListing, java.awt.BorderLayout.CENTER);

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

        pExtraCode.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Jump segments replacement code"));
        pExtraCode.setLayout(new java.awt.GridBagLayout());

        jLabel4.setText("Replacement code & RUN address:");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 0.5;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 4, 0);
        pExtraCode.add(jLabel4, gridBagConstraints);

        jtfExtraAddress.setColumns(8);
        jtfExtraAddress.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N
        jtfExtraAddress.setText("00000");
        jtfExtraAddress.setToolTipText("<HTML>The code that replaces jump segments will start at this address.<BR>This will be also run address.<BR> Choose address that is not covered by any segment</HTML>");
        jtfExtraAddress.setMaximumSize(jtfExtraAddress.getPreferredSize());
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 0.5;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 4, 0);
        pExtraCode.add(jtfExtraAddress, gridBagConstraints);

        jLabel5.setText("Maximum extra code length:");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 4, 0);
        pExtraCode.add(jLabel5, gridBagConstraints);

        jtfExtraLength.setEditable(false);
        jtfExtraLength.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N
        jtfExtraLength.setText("0");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 4, 0);
        pExtraCode.add(jtfExtraLength, gridBagConstraints);

        jcbNoExtra.setMnemonic('n');
        jcbNoExtra.setText("Do not append replacement code");
        jcbNoExtra.setToolTipText("<HTML>Do not append replacement code. <BR>\nThis is useful only for binary files that do not contain<BR>\nINIT or RUNINIT segments</HTML>");
        jcbNoExtra.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 4, 0);
        pExtraCode.add(jcbNoExtra, gridBagConstraints);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        pExtraCode.add(pFiller, gridBagConstraints);

        pControlArea.add(pExtraCode, java.awt.BorderLayout.CENTER);

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

        btnAnalyze.setMnemonic('a');
        btnAnalyze.setText("Analyze");
        btnAnalyze.setToolTipText("Analyze the input file and populate the list of segments");
        btnAnalyze.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnAnalyzeActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 0.1;
        pActionButtons.add(btnAnalyze, gridBagConstraints);

        btnMerge.setForeground(java.awt.Color.blue);
        btnMerge.setMnemonic('m');
        btnMerge.setText("Merge !");
        btnMerge.setToolTipText("Merge segments of the analyzed binary file");
        btnMerge.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnMergeActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 0.1;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
        pActionButtons.add(btnMerge, gridBagConstraints);

        btnClean.setForeground(new java.awt.Color(0, 157, 0));
        btnClean.setMnemonic('c');
        btnClean.setText("Clean");
        btnClean.setToolTipText("Clean the dialog");
        btnClean.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnCleanActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.weightx = 0.1;
        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
        pActionButtons.add(btnClean, gridBagConstraints);

        pControlArea.add(pActionButtons, java.awt.BorderLayout.SOUTH);

        pSegments.add(pControlArea, java.awt.BorderLayout.EAST);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        pMain.add(pSegments, gridBagConstraints);

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

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

    private void btnCloseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCloseActionPerformed
        this.setVisible(false);
        this.dispose();
    }//GEN-LAST:event_btnCloseActionPerformed

    private void btnMergeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnMergeActionPerformed
        File f = new File(jtfOutput.getText());
        try {
            ae.merge(f.getAbsolutePath(),jtfExtraAddress.getText(), !jcbNoExtra.isSelected(),false);
        }
        catch(Exception e) {
           
           JOptionPane.showMessageDialog(null, Utils.getTitledExceptionMessage("Unable to merge binary file", e),"Unable to merge binary file",JOptionPane.ERROR_MESSAGE);
           return;
        }
        JOptionPane.showMessageDialog(null, "Binary file merged. Use an emulator to test the binary file.","Binary file merged",JOptionPane.INFORMATION_MESSAGE);
        
    }//GEN-LAST:event_btnMergeActionPerformed

    private void btnCleanActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCleanActionPerformed
        this.clean();
    }//GEN-LAST:event_btnCleanActionPerformed

    private void btnAnalyzeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnAnalyzeActionPerformed
       executeAnalyze();
    }//GEN-LAST:event_btnAnalyzeActionPerformed

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

    private void btnBrowseInputActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnBrowseInputActionPerformed
        if(fcMergeInput.showOpenDialog(this)==JFileChooser.CANCEL_OPTION) return;
        File f=fcMergeInput.getSelectedFile();
        if (f!=null) {
            jtfInput.setText(f.getAbsolutePath());
        }
    }//GEN-LAST:event_btnBrowseInputActionPerformed
    
    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new MergerDialog().setVisible(true);
            }
        });
    }
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton btnAnalyze;
    private javax.swing.JButton btnBrowseInput;
    private javax.swing.JButton btnBrowseOutput;
    private javax.swing.JButton btnClean;
    private javax.swing.JButton btnClose;
    private javax.swing.JButton btnMerge;
    private javax.swing.JFileChooser fcMergeInput;
    private javax.swing.JFileChooser fcMergeOutput;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JCheckBox jcbNoExtra;
    private javax.swing.JList jlsSegments;
    private javax.swing.JTextField jtfExtraAddress;
    private javax.swing.JTextField jtfExtraLength;
    javax.swing.JTextField jtfInput;
    javax.swing.JTextField jtfOutput;
    private javax.swing.JPanel pActionButtons;
    private javax.swing.JPanel pButtons;
    private javax.swing.JPanel pControlArea;
    private javax.swing.JPanel pExtraCode;
    private javax.swing.JPanel pFiles;
    private javax.swing.JPanel pFiller;
    private javax.swing.JPanel pListing;
    private javax.swing.JPanel pMain;
    private javax.swing.JPanel pSegments;
    // End of variables declaration//GEN-END:variables
    
    /** Dialog reset*/
    void clean() {
        analyzed=false;
        jtfInput.setEditable(true);
        btnBrowseInput.setEnabled(true);
        btnMerge.setEnabled(false);
        btnAnalyze.setEnabled(true);
        jtfExtraLength.setText("");
        ae=null;
        jlsSegments.setListData(new Vector());
    }
    
    /**Analyze input file*/
    void executeAnalyze() {
         File f = new File(jtfInput.getText());
        /*Is that existing file ?*/
        if (f.exists() && f.isFile()) {
            ae = new DOS2Binary(f.getAbsolutePath());
            try {
                ae.analyzeFromFile();
                analyzed=true;
            }
            catch(Exception e) {
                
                e.printStackTrace(); /**/
                JOptionPane.showMessageDialog(null, Utils.getTitledExceptionMessage("Unable to analyze binary file", e),"Unable to analyze binary file",JOptionPane.ERROR_MESSAGE);
                analyzed=false;
                return;
            }
            /*Length of extra code*/
            this.jtfExtraLength.setText(Integer.toString(ae.getExtraLength()));
            /*Setup JList with segments*/
            this.jlsSegments.setListData(ae.getListing());
            /*Name of input file is not editable*/
            this.jtfInput.setEditable(false);
            /*Analyzing not available*/
            this.btnAnalyze.setEnabled(false);
            /*Possible to merge*/
            this.btnMerge.setEnabled(true);
            /*Not possible to browse input file*/
            this.btnBrowseInput.setEnabled(false);
        }
        else {
             JOptionPane.showMessageDialog(null, "Input binary file does not exist","Input binary file",JOptionPane.ERROR_MESSAGE);
        }
    }
    
    void updateFileDialogUI() {
        SwingUtilities.updateComponentTreeUI(fcMergeInput);
        SwingUtilities.updateComponentTreeUI(fcMergeOutput);
    }

    public Object[] getPersistenceData() {

        Object pData[] = new Object[3];

        /*Bounds*/
        pData[0]=getBounds();

        /*File chooser directories*/
        pData[1]=fcMergeInput.getCurrentDirectory().getAbsolutePath();
        pData[2]=fcMergeOutput.getCurrentDirectory().getAbsolutePath();

        return pData;
    }

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

        /*Bounds */
        setBounds((Rectangle)data[0]);
        /*File chooser directories*/
        fcMergeInput.setCurrentDirectory(new File((String)data[1]));
        fcMergeOutput.setCurrentDirectory(new File((String)data[2]));
    }

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

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