package FTP;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.io.CopyStreamEvent;
import org.apache.commons.net.io.CopyStreamListener;

import java.io.*;

@SuppressWarnings("unused")
public class FTPUpload {
    private final String host;
    private final int port;
    private final String username;
    private final String password;
    private final String remoteDirectory;
    private final boolean passiveMode;

    /**
     * Initialize FTP Upload using Active Mode
     * @param host FTP Host
     * @param port FTP Port
     * @param username FTP Username
     * @param password FTP Password
     * @param remoteDirectory Remote Directory
     */
    public FTPUpload(String host, int port, String username, String password, String remoteDirectory){
        this.host = host;
        this.port = port;
        this.username = username;
        this.password = password;
        this.remoteDirectory = remoteDirectory;
        this.passiveMode = false;
    }

    /**
     * Initialize FTP Upload
     * @param host FTP Host
     * @param port FTP Port
     * @param username FTP Username
     * @param password FTP Password
     * @param remoteDirectory Remote Directory
     * @param passiveMode Passive Mode
     */
    public FTPUpload(String host, int port, String username, String password, String remoteDirectory, boolean passiveMode){
        this.host = host;
        this.port = port;
        this.username = username;
        this.password = password;
        this.remoteDirectory = remoteDirectory;
        this.passiveMode = passiveMode;
    }

    public void UploadFile(FTPUploadEvent event, String... files) {
        if (files.length!=0){
            int total = files.length;
            var success = 0;
            var failed = 0;
            FTPClient ftp = new FTPClient();
            try {
                ftp.connect(host, port);
                if (event!=null) event.uploadLog("Connected to " + host + ":" + port);
                ftp.login(username, password);
                if (event!=null) event.uploadLog("Logged in as " + username);
                if (passiveMode) {
                    ftp.enterLocalPassiveMode();
                    if (event!=null) event.uploadLog("Passive Mode Enabled");
                }
                ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
                if (event!=null) event.uploadLog("Setting File Type to Binary");
                ftp.changeWorkingDirectory(remoteDirectory);
                if (event!=null) event.uploadLog("Changed Working Directory to " + remoteDirectory);
                for (String file : files) {
                    try{
                        File localFile = new File(file);
                        if (localFile.exists()) {
                            FileInputStream input = new FileInputStream(localFile);
                            //ProgressInputStream pis = new ProgressInputStream(file, input, localFile.length(), event);
                            if (event!=null) event.onUploadStarted(file);
                            ftp.setCopyStreamListener(new CopyStreamListener() {
                                @Override
                                public void bytesTransferred(CopyStreamEvent copyStreamEvent) {
                                }

                                @Override
                                public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, long streamSize) {
                                    if (event!=null) event.onUploadProgress(file, totalBytesTransferred, streamSize);
                                }
                            });
                            ftp.storeFile(localFile.getName(), input);
                            input.close();
                            if (event!=null) event.onUploadSuccess(file);
                            success++;
                        } else {
                            if (event!=null) event.uploadLog("File " + file + " not found");
                            failed++;
                        }
                    } catch (Exception e){
                        if (event!=null) event.onUploadFailed(file);
                        failed++;
                    }

                }
                ftp.logout();
                if (event!=null) event.uploadLog("Logged out from FTP Server");
            } catch (IOException e) {
                if (event!=null) event.uploadLog("Failed to upload file, Msg: " + e.getMessage());
            } finally {
                if (ftp.isConnected()) {
                    try {
                        ftp.disconnect();
                        if (event!=null) event.uploadLog("Disconnected from FTP Server");
                    } catch (IOException e) {
                        if (event!=null) event.uploadLog("Failed to disconnect from FTP Server, Msg: " + e.getMessage());
                    }
                }
                if (event!=null) event.onUploadFinished(total, success, failed, files);
            }
        } else {
            if (event!=null) event.onUploadFinished(0, 0, 0, new String[]{});
        }

    }

    static class ProgressInputStream extends FilterInputStream{
        private final long totalbytes;
        private long bytesread = 0;
        private final FTPUploadEvent event;
        private final String filename;

        protected ProgressInputStream(String filename, InputStream in, long totalbytes, FTPUploadEvent event) {
            super(in);
            this.totalbytes = totalbytes;
            this.event = event;
            this.filename = filename;
        }

        @Override
        public int read() throws IOException {
            int result = in.read();
            if (result != -1) {
                bytesread++;
                if (event!=null) event.onUploadProgress(filename, bytesread, totalbytes);
            }
            return result;
        }

        @Override
        public int read(byte[] bb, int offset, int len) throws IOException {
            int result = in.read(bb, offset, len);
            if (result != -1) {
                bytesread += result;
                if (event!=null) event.onUploadProgress(filename, bytesread, totalbytes);
            }
            return result;
        }
    }
}
