package TestingSystem;
import Common.Constants;
import Common.Global;
import Common.Utils.Utils;
import Common.Utils.Validators.ShellParser;
import GlobalData.Machine.Machine;
import GlobalData.RemoteFile.RemoteFile;
import GlobalData.User.User;
import Visual_DVM_2021.Passes.PassException;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelShell;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Vector;
public class UserConnection {
    public ChannelSftp sftpChannel = null;
    public ChannelShell shellChannel = null;
    //--
    JSch jsch = null;
    Session session = null;
    //---
    PipedInputStream in = null;
    PipedOutputStream out = null;
    //---
    PipedOutputStream pin = null;
    PipedInputStream pout = null;
    InputStreamReader fromServer = null;
    //---
    public UserConnection(Machine machine, User user) throws Exception {
        session = (jsch = new JSch()).getSession(user.login, machine.address, machine.port);
        session.setPassword(user.password);
        session.setConfig("StrictHostKeyChecking", "no");
        session.connect(0);
        //-->
        //создать канал для файлов
        sftpChannel = (ChannelSftp) session.openChannel("sftp");
        sftpChannel.connect();
        //-->
        //создать канал для команд
        shellChannel = (ChannelShell) session.openChannel("shell");
        in = new PipedInputStream();
        out = new PipedOutputStream();
        shellChannel.setInputStream(in);
        shellChannel.setOutputStream(out);
        pin = new PipedOutputStream(in);
        pout = new PipedInputStream(out);
        shellChannel.connect();
        //-
        fromServer = new InputStreamReader(pout);
        ShellParser.setUserName(user.login);
        ShellParser.ReadInvitation(fromServer); //прочитать первое приглашение от машины.
    }
    public void Disconnect() {
        if (in != null) {
            try {
                in.close();
            } catch (Exception exception) {
                Global.Log.PrintException(exception);
            }
        }
        if (out != null) {
            try {
                out.close();
            } catch (Exception exception) {
                Global.Log.PrintException(exception);
            }
        }
        if (pin != null) {
            try {
                pin.close();
            } catch (Exception exception) {
                Global.Log.PrintException(exception);
            }
        }
        if (pout != null) {
            try {
                pout.close();
            } catch (Exception exception) {
                Global.Log.PrintException(exception);
            }
        }
        if (fromServer != null) {
            try {
                fromServer.close();
            } catch (Exception exception) {
                Global.Log.PrintException(exception);
            }
        }
        if (sftpChannel != null) sftpChannel.disconnect();
        if (shellChannel != null) shellChannel.disconnect();
        if (session != null) session.disconnect();
        //----------------------
        sftpChannel = null;
        shellChannel = null;
        jsch = null;
        session = null;
        //---
        in = null;
        out = null;
        //---
        pin = null;
        pout = null;
        fromServer = null;
        System.gc();
    }
    //--

    //из за мусора результатом пользоваться в общем случае невозможно.
    public String ShellCommand(String command) throws Exception {
        StringBuilder result = new StringBuilder();
        //       System.out.println("command=" + Utils.Brackets(command));
        pin.write((command + "\r\n").getBytes());
        ShellParser.ReadInvitation(fromServer); //первое приглашение после эхо. возможен мусор.
        result.append(ShellParser.getCommandResult(fromServer)); //возможный результат и второе приглашение.
//        System.out.println("answer=" + Utils.Brackets(result));
        //в реалиях визуалиазтора нас интересует только ПОСЛЕДНЯЯ СТРОКА ОТВЕТА.
        String[] data = result.toString().split("\n");
        //  System.out.println("res="+Utils.Brackets(res));
        return (data.length > 0) ? data[data.length - 1] : result.toString();
    }
    //--
    //тут имя файла короткое.
    public boolean Exists(String folder, String name) throws Exception {
        Vector<ChannelSftp.LsEntry> files = sftpChannel.ls(folder);
        for (ChannelSftp.LsEntry file : files) {
            file.getAttrs().getSize();
            if (file.getFilename().equals(name)) {
                return true;
            }
        }
        return false;
    }
    //--
    public void getSingleFile(String src, String dst) throws Exception {
        sftpChannel.get(src, dst);
    }
    public long getFileKBSize(String path) throws Exception{
        long size = sftpChannel.lstat(path).getSize();
        return size/1024;
    }
    public boolean getSingleFile(RemoteFile src, File dst, int maxSize) throws Exception {
        if (Exists(src.parent, src.name)) {
            if ((maxSize == 0)  || getFileKBSize(src.full_name) <= maxSize) {
                getSingleFile(src.full_name, dst.getAbsolutePath());
                return true;
            } else {
                Utils.WriteToFile(dst, "Размер файла превышает " + maxSize + " KB.\n" + "Файл не загружен. Его можно просмотреть на машине по адресу\n" + Utils.Brackets(src.full_name));
            }
        }
        return false;
    }
    public void putSingleFile(File src, RemoteFile dst) throws Exception {
        sftpChannel.put(src.getAbsolutePath(), dst.full_name);
    }
    //-
    public void MKDIR(RemoteFile dir) throws Exception {
        if (!Exists(dir.parent, dir.name)) sftpChannel.mkdir(dir.full_name);
    }
    //-
    public void RMDIR(String dir) throws Exception {
        if (!dir.isEmpty() && !dir.equals("/") && !dir.equals("\\") && !dir.equals("*")) {
            ShellCommand("rm -rf " + Utils.DQuotes(dir));
        } else throw new PassException("Недопустимый путь для удаления папки " + Utils.DQuotes(dir));
    }
    //-
    public void SynchronizeSubDirsR(File local_dir, RemoteFile remote_dir) throws Exception {
        File[] local_subdirs = local_dir.listFiles(File::isDirectory);
        File[] local_files = local_dir.listFiles(File::isFile);
        //------------------------------------------------------------------------
        LinkedHashMap<String, RemoteFile> remote_subdirs = new LinkedHashMap<>();
        LinkedHashMap<String, RemoteFile> remote_files = new LinkedHashMap<>();
        Vector<ChannelSftp.LsEntry> files = sftpChannel.ls(remote_dir.full_name);
        for (ChannelSftp.LsEntry file : files) {
            if (file.getAttrs().isDir()) {
                if (!file.getFilename().equals(".") && !file.getFilename().equals(".."))
                    remote_subdirs.put(file.getFilename(), new RemoteFile(remote_dir.full_name, file.getFilename(), true));
            } else {
                RemoteFile rf = new RemoteFile(remote_dir.full_name, file.getFilename());
                rf.updateTime = RemoteFile.convertUpdateTime(file.getAttrs().getMTime());
                remote_files.put(file.getFilename(), rf);
            }
        }
        if (local_subdirs != null) {
            for (File lsd : local_subdirs) {
                if (!lsd.getName().equals(Constants.data)) {
                    RemoteFile rsd = null;
                    if (!remote_subdirs.containsKey(lsd.getName()))
                        sftpChannel.mkdir((rsd = new RemoteFile(remote_dir.full_name, lsd.getName(), true)).full_name);
                    else rsd = remote_subdirs.get(lsd.getName());
                    SynchronizeSubDirsR(lsd, rsd);
                }
            }
        }
        if (local_files != null) {
            for (File lf : local_files) {
                RemoteFile rf = null;
                if (!remote_files.containsKey(lf.getName())) {
                    rf = new RemoteFile(remote_dir.full_name, lf.getName());
                    putSingleFile(lf, rf);
                } else {
                    rf = remote_files.get(lf.getName());
                    if (lf.lastModified() > rf.updateTime) {
                        putSingleFile(lf, rf);
                    }
                }
            }
        }
    }
    /*
    public void copy(RemoteFile src, RemoteFile dst) throws Exception {
        ShellCommand("cp " + Utils.DQuotes(src.full_name) + " " + Utils.DQuotes(dst.full_name));
    }
     */
    //-------
    public void writeToFile(String text, RemoteFile dst) throws Exception {
        sftpChannel.put(new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)), dst.full_name);
        sftpChannel.chmod(0777, dst.full_name);
    }
    public String readFromFile(RemoteFile src) throws Exception {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        sftpChannel.get(src.full_name, outputStream);
        return outputStream.toString(StandardCharsets.UTF_8.name());
    }
}
