package be.iminds.ilabt.jfed.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * SshVersionFinder
 */
public class SshVersionFinder {
    private static final Logger LOG = LoggerFactory.getLogger(SshVersionFinder.class);

    private static class Version {
        final int big;
        final int small;

        public Version(int big, int small) {
            this.big = big;
            this.small = small;
        }
    }

    private static Version parseOpenSshVersion(String versionLine) {
        if (versionLine == null) return null;
        Pattern patternVersion = Pattern.compile("OpenSSH.?([0-9]+)\\.([0-9]+).*");
        Matcher matcherVersion = patternVersion.matcher(versionLine);
        if (versionLine != null && matcherVersion.find()) {
            int bigVersion = Integer.parseInt(matcherVersion.group(1));
            int smallVersion = Integer.parseInt(matcherVersion.group(2));
            return new Version(bigVersion, smallVersion);
        } else
            return null;
    }

    public static Boolean supportsNc(Version version) {
        if (version == null) return null;

        if (version.big > 5)
            return true;
        if (version.big == 5 && version.small >= 4)
            return true;
        return false;
    }

    private static Boolean cachedSupportsNc = null;
    /*
    * This caches the answer, since we don't expect the user to upgrade the OpenSSH version while we are running.
    */
    public static Boolean checkIfVersionSupportsNc() {
        if (cachedSupportsNc != null) {
            LOG.debug("answering SshVersionFinder.checkIfVersionSupportsNc() from cache: "+cachedSupportsNc);
            return cachedSupportsNc;
        }

        Version version = findOpenSshVersion();
        LOG.info("OpenSsh version: "+version);
        cachedSupportsNc = supportsNc(version);
        return cachedSupportsNc;
    }

    /* handles all errors, by returning null */
    public static Version findOpenSshVersion() {
        String versionLine = SshVersionFinder.findOpenSshVersion("-V");
        LOG.info("Read \"ssh -V\" output: \"" + versionLine + "\"");
        Version version = parseOpenSshVersion(versionLine);
        if (version != null)
            return version;

        versionLine = SshVersionFinder.findOpenSshVersion("-v");
        LOG.info("Read \"ssh -v\" output: \"" + versionLine + "\"");
        version = parseOpenSshVersion(versionLine);
        if (version != null)
            return version;
        else {
            LOG.info("OpenSSH version could not be determined");
            return null;
        }
    }

    /* handles all errors, by returning null */
    private static String findOpenSshVersion(String opt) {
        try {
            ArrayList<String> command = new ArrayList<>();
            command.add("ssh");
            command.add(opt);
            Process p = Runtime.getRuntime().exec(command.toArray(new String[command.size()]));

            BufferedReader input = new BufferedReader(new InputStreamReader(p.getErrorStream()));
            p.getOutputStream().close();
            String versionLine = input.readLine();
            input.close();

            try {
                p.waitFor();
            } catch (InterruptedException e) {
                //ignore
            }

            return versionLine;
        } catch (AssertionError e) {
            LOG.error("AssertionError trying to call \"ssh "+opt+"\": " + e.getMessage(), e);
            return null;
        } catch (Exception e) {
            LOG.error("Exception trying to call \"ssh "+opt+"\": " + e.getMessage(), e);
            return null;
        }
    }
}
