package com.blazemeter.jmeter.xmpp.actions;

import com.blazemeter.jmeter.xmpp.JMeterXMPPSampler;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.NullOutputStream;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smackx.filetransfer.*;

import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;

public class SendFileXEP0096 extends AbstractXMPPAction implements FileTransferListener, ConnectionListener {
    private static final Logger log = LoggingManager.getLoggerForClass();

    public static final java.lang.String FILE_RECIPIENT = "file_recipient";
    public static final String FILE_PATH = "file_path";
    private static final long WAITING_CYCLES = 10;
    private JTextField recipient;
    private JTextField path;
    private FileTransferManager mgr;

    @Override
    public String getLabel() {
        return "Send File (XEP-0096)";
    }

    @Override
    public SampleResult perform(JMeterXMPPSampler sampler, SampleResult res) throws Exception {
        String recipient = sampler.getPropertyAsString(FILE_RECIPIENT);
        String filePath = sampler.getPropertyAsString(FILE_PATH);

        res.setSamplerData("Recipient: " + recipient + "\r\nFile: " + filePath + "\r\n");

        OutgoingFileTransfer transfer = mgr.createOutgoingFileTransfer(recipient);

        transfer.sendFile(new File(filePath), filePath);
        waitForTransfer(transfer, sampler.getXMPPConnection().getPacketReplyTimeout());
        res.setResponseData(("Bytes sent: " + transfer.getBytesSent()).getBytes());
        return res;
    }

    private void waitForTransfer(FileTransfer transfer, long timeout) throws SmackException, InterruptedException {
        double prevProgress = 0;
        long counter = 0;
        Thread.sleep(timeout / WAITING_CYCLES / 10); // optimistic
        while (!transfer.isDone()) {
            if (transfer.getStatus().equals(FileTransfer.Status.error)) {
                throw new SmackException(transfer.getError().toString(), transfer.getException());
            } else {
                log.debug("Status: " + transfer.getStatus() + " " + transfer.getProgress());
            }
            if (transfer.getProgress() <= prevProgress) {
                if (counter >= WAITING_CYCLES) {
                    throw new SmackException("File transfer timed out");
                }
                counter++;
                Thread.sleep(timeout / WAITING_CYCLES);
            } else {
                counter = 0;
                prevProgress = transfer.getProgress();
            }
        }

        if (transfer.getProgress() == 0) {
            throw new SmackException("No data transferred");
        }
    }

    @Override
    public void addUI(JComponent panel, GridBagConstraints labelConstraints, GridBagConstraints editConstraints) {
        addToPanel(panel, labelConstraints, 0, 0, new JLabel("Recipient: ", JLabel.RIGHT));
        addToPanel(panel, editConstraints, 1, 0, recipient = new JTextField(20));

        addToPanel(panel, labelConstraints, 0, 1, new JLabel("File to send: ", JLabel.RIGHT));
        addToPanel(panel, editConstraints, 1, 1, path = new JTextField(20));

        addToPanel(panel, labelConstraints, 1, 2, new JLabel("Note: you must use full JID as recipient, like user@server/resource)", JLabel.LEFT));
    }

    @Override
    public void clearGui() {
        recipient.setText("");
        path.setText("");
    }

    @Override
    public void setSamplerProperties(JMeterXMPPSampler sampler) {
        sampler.setProperty(FILE_RECIPIENT, recipient.getText());
        sampler.setProperty(FILE_PATH, path.getText());
    }

    @Override
    public void setGuiFieldsFromSampler(JMeterXMPPSampler sampler) {
        recipient.setText(sampler.getPropertyAsString(FILE_RECIPIENT));
        path.setText(sampler.getPropertyAsString(FILE_PATH));
    }

    @Override
    public void fileTransferRequest(FileTransferRequest request) {
        final IncomingFileTransfer transfer = request.accept();

        Thread transferThread = new Thread(new Runnable() {
            public void run() {
                try {
                    OutputStream os = new NullOutputStream();
                    InputStream is = transfer.recieveFile();
                    log.debug("Reading from stream: " + is.available());
                    IOUtils.copy(is, os);
                    log.debug("Left in stream: " + is.available());
                } catch (Exception e) {
                    log.error("Failed incoming file transfer", e);
                }
            }
        });
        transferThread.start();
    }

    @Override
    public void connected(XMPPConnection connection) {
        this.mgr = FileTransferManager.getInstanceFor(connection);
        mgr.addFileTransferListener(this);
    }

    @Override
    public void authenticated(XMPPConnection connection) {

    }

    @Override
    public void connectionClosed() {

    }

    @Override
    public void connectionClosedOnError(Exception e) {

    }

    @Override
    public void reconnectingIn(int seconds) {

    }

    @Override
    public void reconnectionSuccessful() {

    }

    @Override
    public void reconnectionFailed(Exception e) {

    }
}
