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

import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.CodeAreaUtils;

/**
 * Go-to position panel for binary editor.
 */
@ParametersAreNonnullByDefault
public class GoToBinaryPanel extends javax.swing.JPanel {

    private long cursorPosition;
    private long maxPosition;
    private GoToBinaryPositionMode goToMode = GoToBinaryPositionMode.FROM_START;

    public GoToBinaryPanel() {
        initComponents();

        baseSwitchableSpinnerPanel.setMinimum(0L);
        baseSwitchableSpinnerPanel.addChangeListener((javax.swing.event.ChangeEvent evt) -> updateTargetPosition());
    }

    /**
     * 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() {

        positionTypeButtonGroup = new javax.swing.ButtonGroup();
        currentPositionLabel = new javax.swing.JLabel();
        currentPositionTextField = new javax.swing.JTextField();
        goToPanel = new javax.swing.JPanel();
        fromStartRadioButton = new javax.swing.JRadioButton();
        fromEndRadioButton = new javax.swing.JRadioButton();
        fromCursorRadioButton = new javax.swing.JRadioButton();
        positionLabel = new javax.swing.JLabel();
        baseSwitchableSpinnerPanel = new BaseSwitchableSpinnerPanel();
        targetPositionLabel = new javax.swing.JLabel();
        targetPositionTextField = new javax.swing.JTextField();

        currentPositionLabel.setText("Current Position");

        currentPositionTextField.setEditable(false);
        currentPositionTextField.setText("0");

        goToPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Go To Position"));

        positionTypeButtonGroup.add(fromStartRadioButton);
        fromStartRadioButton.setSelected(true);
        fromStartRadioButton.setText("Position from start");
        fromStartRadioButton.addItemListener(this::fromStartRadioButtonItemStateChanged);

        positionTypeButtonGroup.add(fromEndRadioButton);
        fromEndRadioButton.setText("Position from end");
        fromEndRadioButton.addItemListener(this::fromEndRadioButtonItemStateChanged);

        positionTypeButtonGroup.add(fromCursorRadioButton);
        fromCursorRadioButton.setText("Position relative to cursor");
        fromCursorRadioButton.addItemListener(this::fromCursorRadioButtonItemStateChanged);

        positionLabel.setText("Position");

        javax.swing.GroupLayout goToPanelLayout = new javax.swing.GroupLayout(goToPanel);
        goToPanel.setLayout(goToPanelLayout);
        goToPanelLayout.setHorizontalGroup(
            goToPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(fromStartRadioButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addComponent(fromCursorRadioButton, javax.swing.GroupLayout.DEFAULT_SIZE, 412, Short.MAX_VALUE)
            .addComponent(fromEndRadioButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addGroup(goToPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(goToPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(baseSwitchableSpinnerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addGroup(goToPanelLayout.createSequentialGroup()
                        .addComponent(positionLabel)
                        .addGap(0, 0, Short.MAX_VALUE)))
                .addContainerGap())
        );
        goToPanelLayout.setVerticalGroup(
            goToPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(goToPanelLayout.createSequentialGroup()
                .addComponent(fromStartRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(fromEndRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(fromCursorRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(positionLabel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(baseSwitchableSpinnerPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        targetPositionLabel.setText("Target Position");

        targetPositionTextField.setEditable(false);
        targetPositionTextField.setText("0");

        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)
                    .addComponent(currentPositionTextField)
                    .addComponent(goToPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(targetPositionTextField)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(currentPositionLabel)
                            .addComponent(targetPositionLabel))
                        .addGap(0, 0, Short.MAX_VALUE)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(currentPositionLabel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(currentPositionTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(goToPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(targetPositionLabel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(targetPositionTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
    }// </editor-fold>//GEN-END:initComponents

    private void fromStartRadioButtonItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_fromStartRadioButtonItemStateChanged
        if (fromStartRadioButton.isSelected()) {
            switchGoToMode(GoToBinaryPositionMode.FROM_START);
        }
    }//GEN-LAST:event_fromStartRadioButtonItemStateChanged

    private void fromEndRadioButtonItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_fromEndRadioButtonItemStateChanged
        if (fromEndRadioButton.isSelected()) {
            switchGoToMode(GoToBinaryPositionMode.FROM_END);
        }
    }//GEN-LAST:event_fromEndRadioButtonItemStateChanged

    private void fromCursorRadioButtonItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_fromCursorRadioButtonItemStateChanged
        if (fromCursorRadioButton.isSelected()) {
            switchGoToMode(GoToBinaryPositionMode.FROM_CURSOR);
        }
    }//GEN-LAST:event_fromCursorRadioButtonItemStateChanged

    private void updateTargetPosition() {
        targetPositionTextField.setText(String.valueOf(getTargetPosition()));
    }

    public void initFocus() {
        baseSwitchableSpinnerPanel.initFocus();
    }

    public long getTargetPosition() {
        long absolutePosition;
        long position = getPositionValue();
        switch (goToMode) {
            case FROM_START:
                absolutePosition = position;
                break;
            case FROM_END:
                absolutePosition = maxPosition - position;
                break;
            case FROM_CURSOR:
                absolutePosition = cursorPosition + position;
                break;
            default:
                throw CodeAreaUtils.getInvalidTypeException(goToMode);
        }

        if (absolutePosition < 0) {
            absolutePosition = 0;
        } else if (absolutePosition > maxPosition) {
            absolutePosition = maxPosition;
        }
        return absolutePosition;
    }

    public void setTargetPosition(long absolutePosition) {
        if (absolutePosition < 0) {
            absolutePosition = 0;
        } else if (absolutePosition > maxPosition) {
            absolutePosition = maxPosition;
        }
        switch (goToMode) {
            case FROM_START:
                setPositionValue(absolutePosition);
                break;
            case FROM_END:
                setPositionValue(maxPosition - absolutePosition);
                break;
            case FROM_CURSOR:
                setPositionValue(absolutePosition - cursorPosition);
                break;
            default:
                throw CodeAreaUtils.getInvalidTypeException(goToMode);
        }
        updateTargetPosition();
    }

    public long getCursorPosition() {
        return cursorPosition;
    }

    public void setCursorPosition(long cursorPosition) {
        this.cursorPosition = cursorPosition;
        setPositionValue(cursorPosition);
        currentPositionTextField.setText(String.valueOf(cursorPosition));
    }

    public void setMaxPosition(long maxPosition) {
        this.maxPosition = maxPosition;
        baseSwitchableSpinnerPanel.setMaximum(maxPosition);
        updateTargetPosition();
    }

    public void setSelected() {
        baseSwitchableSpinnerPanel.requestFocusInWindow();
    }

    private void switchGoToMode(GoToBinaryPositionMode goToMode) {
        if (this.goToMode == goToMode) {
            return;
        }

        long absolutePosition = getTargetPosition();
        this.goToMode = goToMode;
        switch (goToMode) {
        case FROM_START:
        case FROM_END: {
                setPositionValue(0L);
                baseSwitchableSpinnerPanel.setMinimum(0L);
                baseSwitchableSpinnerPanel.setMaximum(maxPosition);
                baseSwitchableSpinnerPanel.revalidateSpinner();
                break;
            }
        case FROM_CURSOR: {
                setPositionValue(0L);
                baseSwitchableSpinnerPanel.setMinimum(-cursorPosition);
                baseSwitchableSpinnerPanel.setMaximum(maxPosition - cursorPosition);
                baseSwitchableSpinnerPanel.revalidateSpinner();
                break;
            }
            default:
                throw CodeAreaUtils.getInvalidTypeException(goToMode);
        }
        setTargetPosition(absolutePosition);
    }

    private long getPositionValue() {
        return baseSwitchableSpinnerPanel.getValue();
    }

    private void setPositionValue(long value) {
        baseSwitchableSpinnerPanel.setValue(value);
        updateTargetPosition();
//        positionSpinner.setValue(value);
//        positionSpinner.firePropertyChange(SPINNER_PROPERTY, value, value);
    }

    public void acceptInput() {
        baseSwitchableSpinnerPanel.acceptInput();
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private BaseSwitchableSpinnerPanel baseSwitchableSpinnerPanel;
    private javax.swing.JLabel currentPositionLabel;
    private javax.swing.JTextField currentPositionTextField;
    private javax.swing.JRadioButton fromCursorRadioButton;
    private javax.swing.JRadioButton fromEndRadioButton;
    private javax.swing.JRadioButton fromStartRadioButton;
    private javax.swing.JPanel goToPanel;
    private javax.swing.JLabel positionLabel;
    private javax.swing.ButtonGroup positionTypeButtonGroup;
    private javax.swing.JLabel targetPositionLabel;
    private javax.swing.JTextField targetPositionTextField;
    // End of variables declaration//GEN-END:variables

}
