package the.bytecode.club.bytecodeviewer.gui.hexviewer;

import java.awt.event.KeyEvent;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.InputMismatchException;
import java.util.Objects;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.exbin.bined.CaretMovedListener;
import org.exbin.bined.CodeAreaCaretPosition;
import org.exbin.bined.DataChangedListener;
import org.exbin.bined.swing.basic.CodeArea;
import org.exbin.auxiliary.binary_data.BinaryData;

/**
 * Values side panel.
 *
 * @author hajdam
 */
@ParametersAreNonnullByDefault
public class ValuesPanel extends javax.swing.JPanel {

    public static final int UBYTE_MAX_VALUE = 255;
    public static final int SWORD_MIN_VALUE = -32768;
    public static final int SWORD_MAX_VALUE = 32767;
    public static final int UWORD_MAX_VALUE = 65535;
    public static final long UINT_MAX_VALUE = 4294967295L;
    public static final BigInteger ULONG_MAX_VALUE = new BigInteger("4294967295");
    public static final BigInteger BIG_INTEGER_BYTE_MASK = BigInteger.valueOf(255);
    public static final String VALUE_OUT_OF_RANGE = "Value is out of range";
    public static int CACHE_SIZE = 250;

    private CodeArea codeArea;
    private long dataPosition;
    private DataChangedListener dataChangedListener;
    private CaretMovedListener caretMovedListener;

    private final byte[] valuesCache = new byte[CACHE_SIZE];
    private final ByteBuffer byteBuffer = ByteBuffer.wrap(valuesCache);
    private final ValuesUpdater valuesUpdater = new ValuesUpdater();

    private javax.swing.JRadioButton bigEndianRadioButton;
    private javax.swing.JCheckBox binaryCheckBox0;
    private javax.swing.JCheckBox binaryCheckBox1;
    private javax.swing.JCheckBox binaryCheckBox2;
    private javax.swing.JCheckBox binaryCheckBox3;
    private javax.swing.JCheckBox binaryCheckBox4;
    private javax.swing.JCheckBox binaryCheckBox5;
    private javax.swing.JCheckBox binaryCheckBox6;
    private javax.swing.JCheckBox binaryCheckBox7;
    private javax.swing.JLabel binaryLabel;
    private javax.swing.JLabel byteLabel;
    private javax.swing.JTextField byteTextField;
    private javax.swing.JLabel characterLabel;
    private javax.swing.JTextField characterTextField;
    private javax.swing.JLabel doubleLabel;
    private javax.swing.JTextField doubleTextField;
    private javax.swing.ButtonGroup endianButtonGroup;
    private javax.swing.JLabel floatLabel;
    private javax.swing.JTextField floatTextField;
    private javax.swing.JLabel intLabel;
    private javax.swing.JTextField intTextField;
    private javax.swing.ButtonGroup integerSignButtonGroup;
    private javax.swing.JSeparator jSeparator1;
    private javax.swing.JRadioButton littleEndianRadioButton;
    private javax.swing.JLabel longLabel;
    private javax.swing.JTextField longTextField;
    private javax.swing.JRadioButton signedRadioButton;
    private javax.swing.JLabel stringLabel;
    private javax.swing.JTextField stringTextField;
    private javax.swing.JRadioButton unsignedRadioButton;
    private javax.swing.JLabel wordLabel;
    private javax.swing.JTextField wordTextField;

    public ValuesPanel() {
        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() {

        endianButtonGroup = new javax.swing.ButtonGroup();
        integerSignButtonGroup = new javax.swing.ButtonGroup();
        binaryLabel = new javax.swing.JLabel();
        binaryCheckBox0 = new javax.swing.JCheckBox();
        binaryCheckBox1 = new javax.swing.JCheckBox();
        binaryCheckBox2 = new javax.swing.JCheckBox();
        binaryCheckBox3 = new javax.swing.JCheckBox();
        binaryCheckBox4 = new javax.swing.JCheckBox();
        binaryCheckBox5 = new javax.swing.JCheckBox();
        binaryCheckBox6 = new javax.swing.JCheckBox();
        binaryCheckBox7 = new javax.swing.JCheckBox();
        byteLabel = new javax.swing.JLabel();
        byteTextField = new javax.swing.JTextField();
        wordLabel = new javax.swing.JLabel();
        wordTextField = new javax.swing.JTextField();
        intLabel = new javax.swing.JLabel();
        intTextField = new javax.swing.JTextField();
        longLabel = new javax.swing.JLabel();
        longTextField = new javax.swing.JTextField();
        floatLabel = new javax.swing.JLabel();
        floatTextField = new javax.swing.JTextField();
        doubleLabel = new javax.swing.JLabel();
        doubleTextField = new javax.swing.JTextField();
        characterLabel = new javax.swing.JLabel();
        characterTextField = new javax.swing.JTextField();
        stringLabel = new javax.swing.JLabel();
        stringTextField = new javax.swing.JTextField();
        jSeparator1 = new javax.swing.JSeparator();
        bigEndianRadioButton = new javax.swing.JRadioButton();
        littleEndianRadioButton = new javax.swing.JRadioButton();
        signedRadioButton = new javax.swing.JRadioButton();
        unsignedRadioButton = new javax.swing.JRadioButton();

        setMaximumSize(new java.awt.Dimension(246, 447));
        setMinimumSize(new java.awt.Dimension(246, 447));

        binaryLabel.setText("Binary");

        binaryCheckBox0.addActionListener(this::binaryCheckBox0ActionPerformed);

        binaryCheckBox1.addActionListener(this::binaryCheckBox1ActionPerformed);

        binaryCheckBox2.addActionListener(this::binaryCheckBox2ActionPerformed);

        binaryCheckBox3.addActionListener(this::binaryCheckBox3ActionPerformed);

        binaryCheckBox4.addActionListener(this::binaryCheckBox4ActionPerformed);

        binaryCheckBox5.addActionListener(this::binaryCheckBox5ActionPerformed);

        binaryCheckBox6.addActionListener(this::binaryCheckBox6ActionPerformed);

        binaryCheckBox7.addActionListener(this::binaryCheckBox7ActionPerformed);

        byteLabel.setText("Byte");

        byteTextField.setEditable(false);
        byteTextField.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyReleased(java.awt.event.KeyEvent evt) {
                byteTextFieldKeyReleased(evt);
            }
        });

        wordLabel.setText("Word");

        wordTextField.setEditable(false);
        wordTextField.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyReleased(java.awt.event.KeyEvent evt) {
                wordTextFieldKeyReleased(evt);
            }
        });

        intLabel.setText("Integer");

        intTextField.setEditable(false);
        intTextField.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyReleased(java.awt.event.KeyEvent evt) {
                intTextFieldKeyReleased(evt);
            }
        });

        longLabel.setText("Long");

        longTextField.setEditable(false);
        longTextField.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyReleased(java.awt.event.KeyEvent evt) {
                longTextFieldKeyReleased(evt);
            }
        });

        floatLabel.setText("Float");

        floatTextField.setEditable(false);
        floatTextField.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyReleased(java.awt.event.KeyEvent evt) {
                floatTextFieldKeyReleased(evt);
            }
        });

        doubleLabel.setText("Double");

        doubleTextField.setEditable(false);
        doubleTextField.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyReleased(java.awt.event.KeyEvent evt) {
                doubleTextFieldKeyReleased(evt);
            }
        });

        characterLabel.setText("Character");

        characterTextField.setEditable(false);
        characterTextField.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyReleased(java.awt.event.KeyEvent evt) {
                characterTextFieldKeyReleased(evt);
            }
        });

        stringLabel.setText("String");

        stringTextField.setEditable(false);
        stringTextField.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyReleased(java.awt.event.KeyEvent evt) {
                stringTextFieldKeyReleased(evt);
            }
        });

        jSeparator1.setOrientation(javax.swing.SwingConstants.VERTICAL);

        endianButtonGroup.add(bigEndianRadioButton);
        bigEndianRadioButton.setSelected(true);
        bigEndianRadioButton.setText("BE");
        bigEndianRadioButton.setToolTipText("Big Endian");
        bigEndianRadioButton.addChangeListener(this::bigEndianRadioButtonStateChanged);

        endianButtonGroup.add(littleEndianRadioButton);
        littleEndianRadioButton.setText("LE");
        littleEndianRadioButton.setToolTipText("Little Endian");
        littleEndianRadioButton.addChangeListener(this::littleEndianRadioButtonStateChanged);

        integerSignButtonGroup.add(signedRadioButton);
        signedRadioButton.setSelected(true);
        signedRadioButton.setText("Sig");
        signedRadioButton.setToolTipText("Signed Integers");
        signedRadioButton.addChangeListener(this::signedRadioButtonStateChanged);

        integerSignButtonGroup.add(unsignedRadioButton);
        unsignedRadioButton.setText("Uns");
        unsignedRadioButton.setToolTipText("Unsigned Integers");
        unsignedRadioButton.addChangeListener(this::unsignedRadioButtonStateChanged);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addGroup(layout.createSequentialGroup()
                                .addContainerGap()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                        .addGroup(layout.createSequentialGroup()
                                                .addComponent(bigEndianRadioButton)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(littleEndianRadioButton)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 2, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(signedRadioButton)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(unsignedRadioButton))
                                        .addComponent(binaryLabel)
                                        .addGroup(layout.createSequentialGroup()
                                                .addComponent(binaryCheckBox0)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(binaryCheckBox1)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(binaryCheckBox2)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(binaryCheckBox3)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(binaryCheckBox4)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(binaryCheckBox5)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(binaryCheckBox6)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(binaryCheckBox7))
                                        .addGroup(layout.createSequentialGroup()
                                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                                        .addComponent(byteTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 234, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                        .addComponent(wordTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 234, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                        .addComponent(intTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 234, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                        .addComponent(longTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 234, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                        .addComponent(byteLabel)
                                                        .addComponent(wordLabel)
                                                        .addComponent(intLabel)
                                                        .addComponent(longLabel))
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                                        .addComponent(characterTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 234, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                        .addComponent(floatTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 234, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                        .addComponent(floatLabel)
                                                        .addComponent(doubleLabel)
                                                        .addComponent(doubleTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 234, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                        .addComponent(characterLabel)
                                                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                                                .addComponent(stringTextField, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 234, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                                .addComponent(stringLabel)))))
                                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addGroup(layout.createSequentialGroup()
                                .addContainerGap()
                                .addComponent(binaryLabel)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                                        .addComponent(binaryCheckBox0)
                                        .addComponent(binaryCheckBox1)
                                        .addComponent(binaryCheckBox2)
                                        .addComponent(binaryCheckBox3)
                                        .addComponent(binaryCheckBox4)
                                        .addComponent(binaryCheckBox5)
                                        .addComponent(binaryCheckBox6)
                                        .addComponent(binaryCheckBox7))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                                        .addGroup(layout.createSequentialGroup()
                                                .addComponent(byteLabel)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(byteTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(wordLabel)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(wordTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(intLabel)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(intTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(longLabel)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(longTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                                        .addGroup(layout.createSequentialGroup()
                                                .addComponent(floatLabel)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(floatTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(doubleLabel)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(doubleTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(characterLabel)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(characterTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(stringLabel)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(stringTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                                .addComponent(bigEndianRadioButton)
                                                .addComponent(littleEndianRadioButton))
                                        .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 28, javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                                .addComponent(signedRadioButton)
                                                .addComponent(unsignedRadioButton)))
                                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
    }// </editor-fold>//GEN-END:initComponents

    private void littleEndianRadioButtonStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_littleEndianRadioButtonStateChanged
        updateValues();
    }//GEN-LAST:event_littleEndianRadioButtonStateChanged

    private void bigEndianRadioButtonStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_bigEndianRadioButtonStateChanged
        updateValues();
    }//GEN-LAST:event_bigEndianRadioButtonStateChanged

    private void signedRadioButtonStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_signedRadioButtonStateChanged
        updateValues();
    }//GEN-LAST:event_signedRadioButtonStateChanged

    private void unsignedRadioButtonStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_unsignedRadioButtonStateChanged
        updateValues();
    }//GEN-LAST:event_unsignedRadioButtonStateChanged

    private void binaryCheckBox0ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_binaryCheckBox0ActionPerformed
        if (!valuesUpdater.isUpdateInProgress() && ((valuesCache[0] & 0x80) > 0 != binaryCheckBox0.isSelected())) {
            valuesCache[0] = (byte) (valuesCache[0] ^ 0x80);
            modifyValues(1);
        }
    }//GEN-LAST:event_binaryCheckBox0ActionPerformed

    private void binaryCheckBox1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_binaryCheckBox1ActionPerformed
        if (!valuesUpdater.isUpdateInProgress() && ((valuesCache[0] & 0x40) > 0 != binaryCheckBox1.isSelected())) {
            valuesCache[0] = (byte) (valuesCache[0] ^ 0x40);
            modifyValues(1);
        }
    }//GEN-LAST:event_binaryCheckBox1ActionPerformed

    private void binaryCheckBox2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_binaryCheckBox2ActionPerformed
        if (!valuesUpdater.isUpdateInProgress() && ((valuesCache[0] & 0x20) > 0 != binaryCheckBox2.isSelected())) {
            valuesCache[0] = (byte) (valuesCache[0] ^ 0x20);
            modifyValues(1);
        }
    }//GEN-LAST:event_binaryCheckBox2ActionPerformed

    private void binaryCheckBox3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_binaryCheckBox3ActionPerformed
        if (!valuesUpdater.isUpdateInProgress() && ((valuesCache[0] & 0x10) > 0 != binaryCheckBox3.isSelected())) {
            valuesCache[0] = (byte) (valuesCache[0] ^ 0x10);
            modifyValues(1);
        }
    }//GEN-LAST:event_binaryCheckBox3ActionPerformed

    private void binaryCheckBox4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_binaryCheckBox4ActionPerformed
        if (!valuesUpdater.isUpdateInProgress() && ((valuesCache[0] & 0x8) > 0 != binaryCheckBox4.isSelected())) {
            valuesCache[0] = (byte) (valuesCache[0] ^ 0x8);
            modifyValues(1);
        }
    }//GEN-LAST:event_binaryCheckBox4ActionPerformed

    private void binaryCheckBox5ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_binaryCheckBox5ActionPerformed
        if (!valuesUpdater.isUpdateInProgress() && ((valuesCache[0] & 0x4) > 0 != binaryCheckBox5.isSelected())) {
            valuesCache[0] = (byte) (valuesCache[0] ^ 0x4);
            modifyValues(1);
        }
    }//GEN-LAST:event_binaryCheckBox5ActionPerformed

    private void binaryCheckBox6ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_binaryCheckBox6ActionPerformed
        if (!valuesUpdater.isUpdateInProgress() && ((valuesCache[0] & 0x2) > 0 != binaryCheckBox6.isSelected())) {
            valuesCache[0] = (byte) (valuesCache[0] ^ 0x2);
            modifyValues(1);
        }
    }//GEN-LAST:event_binaryCheckBox6ActionPerformed

    private void binaryCheckBox7ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_binaryCheckBox7ActionPerformed
        if (!valuesUpdater.isUpdateInProgress() && ((valuesCache[0] & 0x1) > 0 != binaryCheckBox7.isSelected())) {
            valuesCache[0] = (byte) (valuesCache[0] ^ 0x1);
            modifyValues(1);
        }
    }//GEN-LAST:event_binaryCheckBox7ActionPerformed

    private void byteTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_byteTextFieldKeyReleased
        if (evt.getKeyCode() == KeyEvent.VK_ENTER && isEditable()) {
            try {
                int intValue = Integer.parseInt(byteTextField.getText());
                if (isSigned()) {
                    if (intValue < Byte.MIN_VALUE || intValue > Byte.MAX_VALUE) {
                        throw new NumberFormatException(VALUE_OUT_OF_RANGE);
                    }
                } else {
                    if (intValue < 0 || intValue > UBYTE_MAX_VALUE) {
                        throw new NumberFormatException(VALUE_OUT_OF_RANGE);
                    }
                }

                valuesCache[0] = (byte) intValue;
                modifyValues(1);
                updateValues();
            } catch (NumberFormatException ex) {
                showException(ex);
            }
        }
    }//GEN-LAST:event_byteTextFieldKeyReleased

    private void wordTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_wordTextFieldKeyReleased
        if (evt.getKeyCode() == KeyEvent.VK_ENTER && isEditable()) {
            try {
                int intValue = Integer.parseInt(wordTextField.getText());
                if (isSigned()) {
                    if (intValue < SWORD_MIN_VALUE || intValue > SWORD_MAX_VALUE) {
                        throw new NumberFormatException(VALUE_OUT_OF_RANGE);
                    }
                } else {
                    if (intValue < 0 || intValue > UWORD_MAX_VALUE) {
                        throw new NumberFormatException(VALUE_OUT_OF_RANGE);
                    }
                }

                if (getByteOrder() == ByteOrder.LITTLE_ENDIAN) {
                    valuesCache[0] = (byte) (intValue & 0xff);
                    valuesCache[1] = (byte) ((intValue >> 8) & 0xff);
                } else {
                    valuesCache[0] = (byte) ((intValue >> 8) & 0xff);
                    valuesCache[1] = (byte) (intValue & 0xff);
                }
                modifyValues(2);
                updateValues();
            } catch (NumberFormatException ex) {
                showException(ex);
            }
        }
    }//GEN-LAST:event_wordTextFieldKeyReleased

    private void intTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_intTextFieldKeyReleased
        if (evt.getKeyCode() == KeyEvent.VK_ENTER && isEditable()) {
            try {
                long longValue = Long.parseLong(intTextField.getText());
                if (isSigned()) {
                    if (longValue < Integer.MIN_VALUE || longValue > Integer.MAX_VALUE) {
                        throw new NumberFormatException(VALUE_OUT_OF_RANGE);
                    }
                } else {
                    if (longValue < 0 || longValue > UINT_MAX_VALUE) {
                        throw new NumberFormatException(VALUE_OUT_OF_RANGE);
                    }
                }

                if (getByteOrder() == ByteOrder.LITTLE_ENDIAN) {
                    valuesCache[0] = (byte) (longValue & 0xff);
                    valuesCache[1] = (byte) ((longValue >> 8) & 0xff);
                    valuesCache[2] = (byte) ((longValue >> 16) & 0xff);
                    valuesCache[3] = (byte) ((longValue >> 24) & 0xff);
                } else {
                    valuesCache[0] = (byte) ((longValue >> 24) & 0xff);
                    valuesCache[1] = (byte) ((longValue >> 16) & 0xff);
                    valuesCache[2] = (byte) ((longValue >> 8) & 0xff);
                    valuesCache[3] = (byte) (longValue & 0xff);
                }
                modifyValues(4);
                updateValues();
            } catch (NumberFormatException ex) {
                showException(ex);
            }
        }
    }//GEN-LAST:event_intTextFieldKeyReleased

    private void longTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_longTextFieldKeyReleased
        if (evt.getKeyCode() == KeyEvent.VK_ENTER && isEditable()) {
            try {
                ByteOrder byteOrder = getByteOrder();
                if (isSigned()) {
                    long longValue = Long.parseLong(longTextField.getText());

                    byteBuffer.rewind();
                    if (byteBuffer.order() != byteOrder) {
                        byteBuffer.order(byteOrder);
                    }

                    byteBuffer.putLong(longValue);
                } else {
                    BigInteger bigInteger = new BigInteger(longTextField.getText());
                    if (bigInteger.signum() == -1 || bigInteger.compareTo(ULONG_MAX_VALUE) > 0) {
                        throw new NumberFormatException(VALUE_OUT_OF_RANGE);
                    }

                    if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
                        for (int i = 0; i < 7; i++) {
                            BigInteger nextByte = bigInteger.and(BIG_INTEGER_BYTE_MASK);
                            valuesCache[7 - i] = nextByte.byteValue();
                            bigInteger = bigInteger.shiftRight(8);
                        }
                    } else {
                        for (int i = 0; i < 7; i++) {
                            BigInteger nextByte = bigInteger.and(BIG_INTEGER_BYTE_MASK);
                            valuesCache[i] = nextByte.byteValue();
                            bigInteger = bigInteger.shiftRight(8);
                        }
                    }
                }

                modifyValues(8);
                updateValues();
            } catch (NumberFormatException ex) {
                showException(ex);
            }
        }
    }//GEN-LAST:event_longTextFieldKeyReleased

    private void floatTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_floatTextFieldKeyReleased
        if (evt.getKeyCode() == KeyEvent.VK_ENTER && isEditable()) {
            try {
                ByteOrder byteOrder = getByteOrder();
                float floatValue = Float.parseFloat(floatTextField.getText());

                byteBuffer.rewind();
                if (byteBuffer.order() != byteOrder) {
                    byteBuffer.order(byteOrder);
                }

                byteBuffer.putFloat(floatValue);

                modifyValues(4);
                updateValues();
            } catch (NumberFormatException ex) {
                showException(ex);
            }
        }
    }//GEN-LAST:event_floatTextFieldKeyReleased

    private void doubleTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_doubleTextFieldKeyReleased
        if (evt.getKeyCode() == KeyEvent.VK_ENTER && isEditable()) {
            try {
                ByteOrder byteOrder = getByteOrder();
                double doubleValue = Double.parseDouble(doubleTextField.getText());

                byteBuffer.rewind();
                if (byteBuffer.order() != byteOrder) {
                    byteBuffer.order(byteOrder);
                }

                byteBuffer.putDouble(doubleValue);

                modifyValues(8);
                updateValues();
            } catch (NumberFormatException ex) {
                showException(ex);
            }
        }
    }//GEN-LAST:event_doubleTextFieldKeyReleased

    private void characterTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_characterTextFieldKeyReleased
        if (evt.getKeyCode() == KeyEvent.VK_ENTER && isEditable()) {
            try {
                String characterText = characterTextField.getText();
                if (characterText.length() == 0) {
                    throw new InputMismatchException("Empty value not valid");
                }

                if (characterText.length() > 1) {
                    throw new InputMismatchException("Only single character allowed");
                }

                byte[] bytes = characterText.getBytes(codeArea.getCharset());
                System.arraycopy(bytes, 0, valuesCache, 0, bytes.length);

                modifyValues(bytes.length);
                updateValues();
            } catch (InputMismatchException ex) {
                showException(ex);
            }
        }
    }//GEN-LAST:event_characterTextFieldKeyReleased

    private void stringTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_stringTextFieldKeyReleased
        if (evt.getKeyCode() == KeyEvent.VK_ENTER && isEditable()) {
            try {
                String characterText = stringTextField.getText();
                if (characterText.length() == 0) {
                    throw new InputMismatchException("Empty value not valid");
                }

                byte[] bytes = characterText.getBytes(codeArea.getCharset());
                if (bytes.length > CACHE_SIZE) {
                    throw new InputMismatchException("String is too long");
                }
                System.arraycopy(bytes, 0, valuesCache, 0, bytes.length);

                modifyValues(bytes.length);
                updateValues();
            } catch (InputMismatchException ex) {
                showException(ex);
            }
        }
    }//GEN-LAST:event_stringTextFieldKeyReleased

    public void setCodeArea(CodeArea codeArea) {
        this.codeArea = codeArea;
    }

    public void enableUpdate() {
        dataChangedListener = () -> {
            updateEditMode();
            updateValues();
        };
        codeArea.addDataChangedListener(dataChangedListener);
        caretMovedListener = (CodeAreaCaretPosition caretPosition) -> updateValues();
        codeArea.addCaretMovedListener(caretMovedListener);
        updateEditMode();
        updateValues();
    }

    public void disableUpdate() {
        codeArea.removeDataChangedListener(dataChangedListener);
        codeArea.removeCaretMovedListener(caretMovedListener);
    }

    public void updateEditMode() {
        boolean editable = isEditable();
        binaryCheckBox0.setEnabled(editable);
        binaryCheckBox1.setEnabled(editable);
        binaryCheckBox2.setEnabled(editable);
        binaryCheckBox3.setEnabled(editable);
        binaryCheckBox4.setEnabled(editable);
        binaryCheckBox5.setEnabled(editable);
        binaryCheckBox6.setEnabled(editable);
        binaryCheckBox7.setEnabled(editable);
        byteTextField.setEditable(editable);
        wordTextField.setEditable(editable);
        intTextField.setEditable(editable);
        longTextField.setEditable(editable);
        floatTextField.setEditable(editable);
        doubleTextField.setEditable(editable);
        characterTextField.setEditable(editable);
        stringTextField.setEditable(editable);
    }

    public void updateValues() {
        CodeAreaCaretPosition caretPosition = codeArea.getCaretPosition();
        dataPosition = caretPosition.getDataPosition();
        long dataSize = codeArea.getDataSize();

        if (dataPosition < dataSize) {
            int availableData = dataSize - dataPosition >= CACHE_SIZE ? CACHE_SIZE : (int) (dataSize - dataPosition);
            BinaryData contentData = Objects.requireNonNull(codeArea.getContentData());
            contentData.copyToArray(dataPosition, valuesCache, 0, availableData);
            if (availableData < CACHE_SIZE) {
                Arrays.fill(valuesCache, availableData, CACHE_SIZE, (byte) 0);
            }
        }

        valuesUpdater.schedule();
    }

    private void modifyValues(int bytesCount) {
        // Unsupported in this version
    }

    private boolean isSigned() {
        return signedRadioButton.isSelected();
    }

    private boolean isEditable() {
        return codeArea.isEditable();
    }

    private ByteOrder getByteOrder() {
        return littleEndianRadioButton.isSelected() ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
    }

    private void showException(Exception ex) {
        JOptionPane.showMessageDialog(this, ex.getMessage(), "Invalid Input", JOptionPane.ERROR_MESSAGE);
    }

    public enum ValuesPanelField {
        BINARY0,
        BINARY1,
        BINARY2,
        BINARY3,
        BINARY4,
        BINARY5,
        BINARY6,
        BINARY7,
        BYTE,
        WORD,
        INTEGER,
        LONG,
        FLOAT,
        DOUBLE,
        CHARACTER,
        STRING
    }

    @ParametersAreNonnullByDefault
    private class ValuesUpdater {

        private boolean updateInProgress = false;
        private boolean updateTerminated = false;
        private boolean scheduleUpdate = false;
        private boolean clearFields = true;

        private boolean signed;
        private ByteOrder byteOrder;
        private byte[] values;

        private synchronized void schedule() {
            if (updateInProgress) {
                updateTerminated = true;
            }
            if (!scheduleUpdate) {
                scheduleUpdate = true;
                scheduleNextStep(ValuesPanelField.values()[0]);
            }
        }

        private void scheduleNextStep(final ValuesPanelField valuesPanelField) {
            SwingUtilities.invokeLater(() -> updateValue(valuesPanelField));
        }

        public boolean isUpdateInProgress() {
            return updateInProgress;
        }

        private void updateValue(final ValuesPanelField valuesPanelField) {
            if (valuesPanelField.ordinal() == 0) {
                long dataSize = codeArea.getDataSize();
                clearFields = dataPosition >= dataSize;
                byteOrder = littleEndianRadioButton.isSelected() ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
                byteOrder = getByteOrder();
                signed = isSigned();
                values = valuesCache;
                if (clearFields) {
                    values[0] = 0;
                }
                updateStarted();
            }

            if (updateTerminated) {
                stopUpdate();
                return;
            }

            if (clearFields) {
                clearField(valuesPanelField);
            } else {
                updateField(valuesPanelField);
            }

            final ValuesPanelField[] panelFields = ValuesPanelField.values();
            ValuesPanelField lastValue = panelFields[panelFields.length - 1];
            if (valuesPanelField == lastValue) {
                stopUpdate();
            } else {
                SwingUtilities.invokeLater(() -> {
                    ValuesPanelField nextValue = panelFields[valuesPanelField.ordinal() + 1];
                    updateValue(nextValue);
                });
            }
        }

        private void updateField(ValuesPanelField valuesPanelField) {
            switch (valuesPanelField) {
                case BINARY0: {
                    binaryCheckBox0.setSelected((values[0] & 0x80) > 0);
                    break;
                }
                case BINARY1: {
                    binaryCheckBox1.setSelected((values[0] & 0x40) > 0);
                    break;
                }
                case BINARY2: {
                    binaryCheckBox2.setSelected((values[0] & 0x20) > 0);
                    break;
                }
                case BINARY3: {
                    binaryCheckBox3.setSelected((values[0] & 0x10) > 0);
                    break;
                }
                case BINARY4: {
                    binaryCheckBox4.setSelected((values[0] & 0x8) > 0);
                    break;
                }
                case BINARY5: {
                    binaryCheckBox5.setSelected((values[0] & 0x4) > 0);
                    break;
                }
                case BINARY6: {
                    binaryCheckBox6.setSelected((values[0] & 0x2) > 0);
                    break;
                }
                case BINARY7: {
                    binaryCheckBox7.setSelected((values[0] & 0x1) > 0);
                    break;
                }
                case BYTE: {
                    byteTextField.setText(String.valueOf(signed ? values[0] : values[0] & 0xff));
                    break;
                }
                case WORD: {
                    int wordValue = signed
                            ? (byteOrder == ByteOrder.LITTLE_ENDIAN
                                    ? (values[0] & 0xff) | (values[1] << 8)
                                    : (values[1] & 0xff) | (values[0] << 8))
                            : (byteOrder == ByteOrder.LITTLE_ENDIAN
                                    ? (values[0] & 0xff) | ((values[1] & 0xff) << 8)
                                    : (values[1] & 0xff) | ((values[0] & 0xff) << 8));
                    wordTextField.setText(String.valueOf(wordValue));
                    break;
                }
                case INTEGER: {
                    long intValue = signed
                            ? (byteOrder == ByteOrder.LITTLE_ENDIAN
                                    ? (values[0] & 0xffL) | ((values[1] & 0xffL) << 8) | ((values[2] & 0xffL) << 16) | (values[3] << 24)
                                    : (values[3] & 0xffL) | ((values[2] & 0xffL) << 8) | ((values[1] & 0xffL) << 16) | (values[0] << 24))
                            : (byteOrder == ByteOrder.LITTLE_ENDIAN
                                    ? (values[0] & 0xffL) | ((values[1] & 0xffL) << 8) | ((values[2] & 0xffL) << 16) | ((values[3] & 0xffL) << 24)
                                    : (values[3] & 0xffL) | ((values[2] & 0xffL) << 8) | ((values[1] & 0xffL) << 16) | ((values[0] & 0xffL) << 24));
                    intTextField.setText(String.valueOf(intValue));
                    break;
                }
                case LONG: {
                    if (signed) {
                        byteBuffer.rewind();
                        if (byteBuffer.order() != byteOrder) {
                            byteBuffer.order(byteOrder);
                        }

                        longTextField.setText(String.valueOf(byteBuffer.getLong()));
                    } else {
                        long longValue = byteOrder == ByteOrder.LITTLE_ENDIAN
                                ? (values[0] & 0xffL) | ((values[1] & 0xffL) << 8) | ((values[2] & 0xffL) << 16) | ((values[3] & 0xffL) << 24)
                                | ((values[4] & 0xffL) << 32) | ((values[5] & 0xffL) << 40) | ((values[6] & 0xffL) << 48)
                                : (values[7] & 0xffL) | ((values[6] & 0xffL) << 8) | ((values[5] & 0xffL) << 16) | ((values[4] & 0xffL) << 24)
                                | ((values[3] & 0xffL) << 32) | ((values[2] & 0xffL) << 40) | ((values[1] & 0xffL) << 48);
                        BigInteger bigInt1 = BigInteger.valueOf(values[byteOrder == ByteOrder.LITTLE_ENDIAN ? 7 : 0] & 0xffL);
                        BigInteger bigInt2 = bigInt1.shiftLeft(56);
                        BigInteger bigInt3 = bigInt2.add(BigInteger.valueOf(longValue));
                        longTextField.setText(bigInt3.toString());
                    }
                    break;
                }
                case FLOAT: {
                    byteBuffer.rewind();
                    if (byteBuffer.order() != byteOrder) {
                        byteBuffer.order(byteOrder);
                    }

                    floatTextField.setText(String.valueOf(byteBuffer.getFloat()));
                    break;
                }
                case DOUBLE: {
                    byteBuffer.rewind();
                    if (byteBuffer.order() != byteOrder) {
                        byteBuffer.order(byteOrder);
                    }

                    doubleTextField.setText(String.valueOf(byteBuffer.getDouble()));
                    break;
                }
                case CHARACTER: {
                    String strValue = new String(values, codeArea.getCharset());
                    if (strValue.length() > 0) {
                        characterTextField.setText(strValue.substring(0, 1));
                    } else {
                        characterTextField.setText("");
                    }
                    break;
                }
                case STRING: {
                    String strValue = new String(values, codeArea.getCharset());
                    for (int i = 0; i < strValue.length(); i++) {
                        char charAt = strValue.charAt(i);
                        if (charAt == '\r' || charAt == '\n' || charAt == 0) {
                            strValue = strValue.substring(0, i);
                            break;
                        }
                    }
                    stringTextField.setText(strValue);
                    stringTextField.setCaretPosition(0);
                    break;
                }
            }
        }

        private void clearField(ValuesPanelField valuesPanelField) {
            switch (valuesPanelField) {
                case BINARY0: {
                    binaryCheckBox0.setSelected(false);
                    break;
                }
                case BINARY1: {
                    binaryCheckBox1.setSelected(false);
                    break;
                }
                case BINARY2: {
                    binaryCheckBox2.setSelected(false);
                    break;
                }
                case BINARY3: {
                    binaryCheckBox3.setSelected(false);
                    break;
                }
                case BINARY4: {
                    binaryCheckBox4.setSelected(false);
                    break;
                }
                case BINARY5: {
                    binaryCheckBox5.setSelected(false);
                    break;
                }
                case BINARY6: {
                    binaryCheckBox6.setSelected(false);
                    break;
                }
                case BINARY7: {
                    binaryCheckBox7.setSelected(false);
                    break;
                }
                case BYTE: {
                    byteTextField.setText("");
                    break;
                }
                case WORD: {
                    wordTextField.setText("");
                    break;
                }
                case INTEGER: {
                    intTextField.setText("");
                    break;
                }
                case LONG: {
                    longTextField.setText("");
                    break;
                }
                case FLOAT: {
                    floatTextField.setText("");
                    break;
                }
                case DOUBLE: {
                    doubleTextField.setText("");
                    break;
                }
                case CHARACTER: {
                    characterTextField.setText("");
                    break;
                }
                case STRING: {
                    stringTextField.setText("");
                    break;
                }
            }
        }

        private synchronized void updateStarted() {
            updateInProgress = true;
            scheduleUpdate = false;
        }

        private synchronized void stopUpdate() {
            updateInProgress = false;
            updateTerminated = false;
        }
    }
}
