#!/usr/bin/env python3


import logging
import socket
import telnetlib
import time
from collections import deque


class TelnetRemoteclient:
    r"""
    Class to create telnet connection to remote host for command execution.
    """

    def __init__(
        self, hostname, username, password, port=23, read_timeout=None
    ):
        r"""
        Description of argument(s):

        hostname        Name/IP of the remote (targeting) host
        username        User on the remote host with access to FFCD files
        password        Password for user on remote host
        read_timeout    New read timeout value to override default one
        """

        self.hostname = hostname
        self.username = username
        self.password = password
        self.tnclient = None
        self.port = port
        self.read_timeout = read_timeout

    def tn_remoteclient_login(self):
        is_telnet = True
        try:
            self.tnclient = telnetlib.Telnet(
                self.hostname, self.port, timeout=15
            )
            if b"login:" in self.tnclient.read_until(
                b"login:", timeout=self.read_timeout
            ):
                self.tnclient.write(self.username.encode("utf-8") + b"\n")

                if b"Password:" in self.tnclient.read_until(
                    b"Password:", timeout=self.read_timeout
                ):
                    self.tnclient.write(self.password.encode("utf-8") + b"\n")

                    n, match, pre_match = self.tnclient.expect(
                        [
                            b"Login incorrect",
                            b"invalid login name or password.",
                            rb"\#",
                            rb"\$",
                        ],
                        timeout=self.read_timeout,
                    )
                    if n == 0 or n == 1:
                        logging.error(
                            "\n\tERROR: Telnet Authentication Failed.  Check"
                            " userid and password.\n\n"
                        )
                        is_telnet = False
                    else:
                        # login successful
                        self.fifo = deque()
                else:
                    # Anything else, telnet server is not running
                    logging.error("\n\tERROR: Telnet Connection Refused.\n\n")
                    is_telnet = False
            else:
                is_telnet = False
        except Exception:
            # Any kind of exception, skip telnet protocol
            is_telnet = False

        return is_telnet

    def __del__(self):
        self.tn_remoteclient_disconnect()

    def tn_remoteclient_disconnect(self):
        try:
            self.tnclient.close()
        except Exception:
            # the telnet object might not exist yet, so ignore this one
            pass

    def execute_command(self, cmd, i_timeout=120):
        r"""
        Executes commands on the remote host

        Description of argument(s):
        cmd             Command to run on remote host
        i_timeout       Timeout for command output
                        default is 120 seconds
        """

        # Wait time for command execution before reading the output.
        # Use user input wait time for command execution if one exists.
        # Else use the default 120 sec,
        if i_timeout != 120:
            execution_time = i_timeout
        else:
            execution_time = 120

        # Execute the command and read the command output.
        return_buffer = b""
        try:
            # Do at least one non-blocking read.
            #  to flush whatever data is in the read buffer.
            while self.tnclient.read_very_eager():
                continue

            # Execute the command
            self.tnclient.write(cmd.encode("utf-8") + b"\n")
            time.sleep(execution_time)

            local_buffer = b""
            # Read the command output one block at a time.
            return_buffer = self.tnclient.read_very_eager()
            while return_buffer:
                local_buffer = b"".join([local_buffer, return_buffer])
                time.sleep(3)  # let the buffer fill up a bit
                return_buffer = self.tnclient.read_very_eager()
        except (socket.error, EOFError) as e:
            self.tn_remoteclient_disconnect()

            if str(e).__contains__("Connection reset by peer"):
                msg = e
            elif str(e).__contains__("telnet connection closed"):
                msg = "Telnet connection closed."
            else:
                msg = "Some other issue.%s %s %s\n\n" % (cmd, e.__class__, e)

            logging.error("\t\t ERROR %s " % msg)

        # Return ASCII string data with ending PROMPT stripped
        return local_buffer.decode("ascii", "ignore").replace("$ ", "\n")