/*
 * Copyright (c) 2014 Paul Scherrer Institute. All rights reserved.
 */
package ch.psi.pshell.swing;

import ch.psi.pshell.device.Device;
import ch.psi.pshell.device.Motor;
import ch.psi.utils.State;
import ch.psi.utils.swing.SwingUtils;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.SpinnerNumberModel;

/**
 *
 */
public class MotorPanel extends DevicePanel {

    public MotorPanel() {
        initComponents();
        FocusListener jogFocusListener = new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                if ((e.isTemporary()) && (e.getSource() instanceof JButton)) {
                    if (jogging) {
                        stop();
                    }
                }
            }
        };
        buttonJogPos.addFocusListener(jogFocusListener);
        buttonJogNeg.addFocusListener(jogFocusListener);

        textState.setBackground(TEXT_READONLY_BACKGROUND_COLOR);
    }

    @Override
    public Motor getDevice() {
        return (Motor) super.getDevice();
    }

    @Override
    public void setDevice(Device device) {
        super.setDevice(device);
        readout.setDevice(device);
        if (device != null) {
            onDeviceStateChanged(device.getState(), null);
            onDeviceStatusChanged(((Motor) device).takeStatus());
            double min = getDevice().getMinSpeed();
            double max = getDevice().getMaxSpeed();
            double def = getDevice().getDefaultSpeed();
            def = Math.max(min, def);
            def = Math.min(max, def);
            spinnerSpeed.setModel(new SpinnerNumberModel(def, min, max, 1.0));
            //In a different thread because can be blocking
            new Thread(() -> {
                try {
                    spinnerSpeed.setValue(getDevice().getSpeed());
                } catch (Exception ex) {
                }
            }).start();

        } else {
            textState.setText("");
        }
    }

    protected void setSpeed(double speed) throws IOException {
        Double current = getDevice().getVelocity().take();
        if ((current == null) || (current != speed)) {
            getDevice().getVelocity().writeAsync(speed);
        }
    }

    protected void moveRel(double offset) throws IOException {
        Double destination = getDevice().take();
        if (destination != null) {
            destination += offset;
            getDevice().writeAsync(destination).handle((ok, ex) -> {
                if ((ex != null) && (ex instanceof IOException)) {
                    SwingUtils.showException(this, (Exception) ex);
                }
                return ok;
            });

        }
    }

    boolean jogging;

    protected void jog(boolean positive) throws IOException, InterruptedException {
        getDevice().startJog(positive);
        jogging = true;
    }

    protected void reference() {
        new Thread(() -> {
            try {
                getDevice().reference();
            } catch (Exception ex) {
            }
        }).start();
    }

    protected void stop() {
        jogging = false;
        new Thread(() -> {
            try {
                getDevice().stop();
            } catch (Exception ex) {
            }
        }).start();
    }

    @Override
    protected void onDeviceSpeedChanged(Double value) {
        spinnerSpeed.setValue(value);
    }

    @Override
    protected void onDeviceStateChanged(State state, State former) {
        if ((state != State.Ready) || (state != State.Busy)) {
            textState.setText(state.toString());
        }
    }

    @Override
    protected void onDeviceStatusChanged(Object value) {
        textState.setText((value == null) ? "" : value.toString());
    }

    public void setDecimals(int decimals) {
        readout.setDecimals(decimals);
    }

    public int getDecimals() {
        return readout.getDecimals();
    }

    @Override
    public void setEnabled(boolean value) {
        buttonJogNeg.setEnabled(value);
        buttonJogPos.setEnabled(value);
        buttonBack.setEnabled(value);
        buttonFor.setEnabled(value);
        buttonReset.setEnabled(value);
        buttonStop.setEnabled(value);
        spinnerStep.setEnabled(value);
        spinnerSpeed.setEnabled(value);
        readout.setEnabled(value);
        if (!value) {
            textState.setText("");
        }
    }

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

        buttonStop = new javax.swing.JButton();
        buttonReset = new javax.swing.JButton();
        buttonFor = new javax.swing.JButton();
        buttonBack = new javax.swing.JButton();
        readout = new ch.psi.pshell.swing.MotorReadoutPanel();
        buttonJogNeg = new javax.swing.JButton();
        buttonJogPos = new javax.swing.JButton();
        jLabel1 = new javax.swing.JLabel();
        spinnerStep = new javax.swing.JSpinner();
        spinnerSpeed = new javax.swing.JSpinner();
        jLabel2 = new javax.swing.JLabel();
        textState = new javax.swing.JTextField();

        setBorder(javax.swing.BorderFactory.createTitledBorder(""));

        buttonStop.setText("Stop");
        buttonStop.setToolTipText("Stop Movement");
        buttonStop.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                buttonStopActionPerformed(evt);
            }
        });

        buttonReset.setText("Home");
        buttonReset.setToolTipText("Start Homing");
        buttonReset.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                buttonResetActionPerformed(evt);
            }
        });

        buttonFor.setText(">|");
        buttonFor.setToolTipText("Step Forward");
        buttonFor.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                buttonForActionPerformed(evt);
            }
        });

        buttonBack.setText("|<");
        buttonBack.setToolTipText("Step Backwad");
        buttonBack.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                buttonBackActionPerformed(evt);
            }
        });

        buttonJogNeg.setText("<<");
        buttonJogNeg.setToolTipText("Jog Backward");
        buttonJogNeg.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mousePressed(java.awt.event.MouseEvent evt) {
                buttonJogNegMousePressed(evt);
            }
            public void mouseReleased(java.awt.event.MouseEvent evt) {
                buttonJogNegMouseReleased(evt);
            }
        });

        buttonJogPos.setText(">>");
        buttonJogPos.setToolTipText("Jog Forward");
        buttonJogPos.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mousePressed(java.awt.event.MouseEvent evt) {
                buttonJogPosMousePressed(evt);
            }
            public void mouseReleased(java.awt.event.MouseEvent evt) {
                buttonJogPosMouseReleased(evt);
            }
        });

        jLabel1.setText("Step size:");

        spinnerStep.setModel(new javax.swing.SpinnerNumberModel(1.0d, 0.001d, 10000.0d, 1.0d));

        spinnerSpeed.setModel(new javax.swing.SpinnerNumberModel(1.0d, 0.001d, 10000.0d, 1.0d));
        spinnerSpeed.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                spinnerSpeedStateChanged(evt);
            }
        });

        jLabel2.setText("Speed:");

        textState.setEditable(false);
        textState.setHorizontalAlignment(javax.swing.JTextField.CENTER);

        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(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addComponent(buttonJogNeg)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(buttonBack)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(readout, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(buttonFor)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(buttonJogPos)
                        .addGap(8, 8, 8))
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(buttonReset)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(buttonStop))
                            .addComponent(textState))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                                .addComponent(jLabel2)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(spinnerSpeed, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                                .addComponent(jLabel1)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(spinnerStep, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                        .addContainerGap())))
        );

        layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {buttonReset, buttonStop});

        layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {buttonBack, buttonFor, buttonJogNeg, buttonJogPos});

        layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {spinnerSpeed, spinnerStep});

        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(buttonBack)
                        .addComponent(buttonJogNeg))
                    .addComponent(readout, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(buttonFor)
                        .addComponent(buttonJogPos)))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
                    .addComponent(buttonReset)
                    .addComponent(buttonStop)
                    .addComponent(spinnerStep, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel1))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
                    .addComponent(textState, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel2)
                    .addComponent(spinnerSpeed, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap())
        );

        layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {buttonBack, buttonFor, buttonJogNeg, buttonJogPos, readout});

    }// </editor-fold>//GEN-END:initComponents

    private void buttonBackActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonBackActionPerformed
        try {
            double step = (Double) spinnerStep.getValue();
            moveRel(-step);
        } catch (Exception ex) {
            showException(ex);
        }
    }//GEN-LAST:event_buttonBackActionPerformed

    private void buttonForActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonForActionPerformed
        try {
            double step = (Double) spinnerStep.getValue();
            moveRel(step);
        } catch (Exception ex) {
            showException(ex);
        }

    }//GEN-LAST:event_buttonForActionPerformed

    private void buttonResetActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonResetActionPerformed
        try {
            reference();
        } catch (Exception ex) {
            showException(ex);
        }

    }//GEN-LAST:event_buttonResetActionPerformed

    private void buttonStopActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonStopActionPerformed
        try {
            stop();
        } catch (Exception ex) {
            showException(ex);
        }

    }//GEN-LAST:event_buttonStopActionPerformed

    private void buttonJogPosMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_buttonJogPosMousePressed
        try {
            jog(true);
        } catch (Exception ex) {
            showException(ex);
        }
    }//GEN-LAST:event_buttonJogPosMousePressed

    private void buttonJogPosMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_buttonJogPosMouseReleased
        stop();
    }//GEN-LAST:event_buttonJogPosMouseReleased

    private void buttonJogNegMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_buttonJogNegMousePressed
        try {
            jog(false);
        } catch (Exception ex) {
            showException(ex);
        }
    }//GEN-LAST:event_buttonJogNegMousePressed

    private void buttonJogNegMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_buttonJogNegMouseReleased
        stop();
    }//GEN-LAST:event_buttonJogNegMouseReleased

    private void spinnerSpeedStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spinnerSpeedStateChanged
        try {
            setSpeed((Double) spinnerSpeed.getValue());
        } catch (Exception ex) {
            showException(ex);
        }
    }//GEN-LAST:event_spinnerSpeedStateChanged

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton buttonBack;
    private javax.swing.JButton buttonFor;
    private javax.swing.JButton buttonJogNeg;
    private javax.swing.JButton buttonJogPos;
    private javax.swing.JButton buttonReset;
    private javax.swing.JButton buttonStop;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private ch.psi.pshell.swing.MotorReadoutPanel readout;
    private javax.swing.JSpinner spinnerSpeed;
    private javax.swing.JSpinner spinnerStep;
    private javax.swing.JTextField textState;
    // End of variables declaration//GEN-END:variables
}
