/*
    Copywrite 2016-2017 Will Winder

    This file is part of Universal Gcode Sender (UGS).

    UGS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    UGS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with UGS.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.willwinder.universalgcodesender.uielements.panels;

import com.willwinder.universalgcodesender.i18n.Localization;
import com.willwinder.universalgcodesender.model.BackendAPI;
import javax.swing.JLabel;
import javax.swing.JPanel;
import com.willwinder.universalgcodesender.Utils;
import com.willwinder.universalgcodesender.listeners.ControllerListener;
import com.willwinder.universalgcodesender.listeners.ControllerStatus;
import com.willwinder.universalgcodesender.listeners.UGSEventListener;
import com.willwinder.universalgcodesender.model.Position;
import com.willwinder.universalgcodesender.model.UGSEvent;
import static com.willwinder.universalgcodesender.model.UGSEvent.ControlState.COMM_SENDING;
import static com.willwinder.universalgcodesender.model.UGSEvent.FileState.FILE_LOADED;
import com.willwinder.universalgcodesender.types.GcodeCommand;
import com.willwinder.universalgcodesender.utils.GUIHelpers;
import com.willwinder.universalgcodesender.utils.GcodeStreamReader;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.Timer;
import net.miginfocom.swing.MigLayout;

/**
 *
 * @author wwinder
 */
public class SendStatusPanel extends JPanel implements UGSEventListener, ControllerListener {
    private static final String AL_RIGHT = "al right";
    private final BackendAPI backend;

    private final JLabel rowsLabel = new JLabel(Localization.getString("mainWindow.swing.rowsLabel"));
    private final JLabel sentRowsLabel = new JLabel(Localization.getString("mainWindow.swing.sentRowsLabel"));
    private final JLabel remainingRowsLabel = new JLabel(Localization.getString("mainWindow.swing.remainingRowsLabel"));
    private final JLabel remainingTimeLabel = new JLabel(Localization.getString("mainWindow.swing.remainingTimeLabel"));
    private final JLabel durationLabel = new JLabel(Localization.getString("mainWindow.swing.durationLabel"));

    private final JLabel rowsValue = new JLabel();
    private final JLabel sentRowsValue = new JLabel();
    private final JLabel remainingRowsValue = new JLabel();
    private final JLabel remainingTimeValue = new JLabel();
    private final JLabel durationValue = new JLabel();

    Timer timer;

    public SendStatusPanel() {
        this(null);
    }
    
    public SendStatusPanel(BackendAPI b) {
        backend = b;
        if (backend != null) {
            backend.addUGSEventListener(this);
            backend.addControllerListener(this);
        }

        initComponents();
        resetSentRowLabels();

        if (backend.isSendingFile()) {
            beginSend();
        }
    }

    private void update() {
        durationValue.setText(Utils.formattedMillis(backend.getSendDuration()));
        setRemainingTime(backend.getSendRemainingDuration());
        sentRowsValue.setText(""+backend.getNumSentRows());
        remainingRowsValue.setText("" + backend.getNumRemainingRows());
    }

    private void beginSend() {
        // Timer for updating duration labels.
        ActionListener actionListener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                java.awt.EventQueue.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            update();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        };

        this.resetTimerLabels();

        if (timer != null){ timer.stop(); }
        timer = new Timer(1000, actionListener);

        // Note: there is a divide by zero error in the timer because it uses
        //       the rowsValueLabel that was just reset.

        try {
            timer.start();
        } catch (Exception e) {
            timer.stop();
            GUIHelpers.displayErrorDialog(e.getMessage());
        }
    }

    private void endSend() {
        setRemainingTime(Utils.formattedMillis(0));
        remainingRowsValue.setText("" + backend.getNumRemainingRows());

        java.awt.EventQueue.invokeLater(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {}

            // Stop the timer after a delay to make sure it is updated.
            if (timer != null && timer.isRunning()) {
                timer.stop();
            }
        });
    }

    private void resetTimerLabels() {
        // Reset labels
        this.durationValue.setText("00:00:00");
        if (this.backend != null && this.backend.isConnected()) {
            setRemainingTime(backend.getSendDuration());
        } else {
            setRemainingTime("--:--:--");
        }
    }

    private void setRemainingTime(long millis) {
        if (millis < 0) {
            setRemainingTime("estimating...");
        } else if (millis == 0) {
            setRemainingTime("--:--:--");
        } else {
            setRemainingTime(Utils.formattedMillis(millis));
        }
    }

    private void setRemainingTime(String text) {
        this.remainingTimeValue.setText(text);
    }

    public String getDuration() {
        return this.durationValue.getText();
    }

    private void resetSentRowLabels() {
        long numRows = 0;
        if (backend.getProcessedGcodeFile() != null) {
            try {
                try (GcodeStreamReader gsr = new GcodeStreamReader(backend.getProcessedGcodeFile())) {
                    numRows = gsr.getNumRows();
                    System.out.println("NUM ROWS: " + numRows);
                }
            } catch (GcodeStreamReader.NotGcodeStreamFile | IOException ex) {}
        }
        // Reset labels
        String totalRows =  String.valueOf(numRows);
        resetTimerLabels();
        this.sentRowsValue.setText("0");
        this.remainingRowsValue.setText(totalRows);
        this.rowsValue.setText(totalRows);
    }

    private void initComponents() {
        // MigLayout... 3rd party layout library.
        setLayout(new MigLayout("fill, wrap 2"));
        add(rowsLabel, AL_RIGHT);
        add(rowsValue);
        add(sentRowsLabel, AL_RIGHT);
        add(sentRowsValue);
        add(remainingRowsLabel, AL_RIGHT);
        add(remainingRowsValue);
        add(remainingTimeLabel, AL_RIGHT);
        add(remainingTimeValue);
        add(durationLabel, AL_RIGHT);
        add(durationValue);
    }

    @Override
    public void UGSEvent(com.willwinder.universalgcodesender.model.UGSEvent evt) {
        // Look for a send beginning.
        if (evt.isStateChangeEvent() && evt.getControlState() == COMM_SENDING) {
            if (backend.isSendingFile()) {
                beginSend();
            }
        }

        // On file loaded event, reset the rows.
        if (evt.isFileChangeEvent() && evt.getFileState() == FILE_LOADED) {
            resetSentRowLabels();
        }
    }

    // Controller events below.

    @Override
    public void controlStateChange(UGSEvent.ControlState state) {
    }

    @Override
    public void fileStreamComplete(String filename, boolean success) {
        endSend();
    }

    @Override
    public void commandSkipped(GcodeCommand command) {
    }

    @Override
    public void commandSent(GcodeCommand command) {
    }

    @Override
    public void commandComplete(GcodeCommand command) {
    }

    @Override
    public void commandComment(String comment) {
    }

    @Override
    public void probeCoordinates(Position p) {
    }

    @Override
    public void messageForConsole(MessageType type, String msg) {
    }

    @Override
    public void statusStringListener(ControllerStatus status) {
    }

    @Override
    public void postProcessData(int numRows) {
    }
}
