#!/usr/bin/env python3

import os, re, getpass, ipaddress, sys, locale, socket
from ipaddress import ip_address, ip_network, IPv4Address, IPv6Address, IPv4Interface
from datetime import datetime, timezone
from collections import defaultdict
from interactive_asa import ASA
from asa_config import asa_config
from os.path import expanduser
import readline
import contextlib
import io
import warnings
import termios
import tty
import prompt_toolkit
import shutil
import time

#sleep_delay=0.05
sleep_delay=0

MORE_STRING = '<--- More --->'

old_settings = None
old_restore = None

if sys.stdin.isatty() and sys.stdout.isatty():
    old_settings = termios.tcgetattr(sys.stdin.fileno())
    old_restore = old_settings
    tty.setraw(sys.stdin.fileno())

def atexit_termios():
    if old_restore is not None:
        termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old_restore)

import atexit
atexit.register(atexit_termios)

import logging
logger = logging.getLogger(__name__)
handler = logging.StreamHandler(stream=sys.stderr)
logger.addHandler(handler)

def handle_exception(exc_type, exc_value, exc_traceback):
    atexit_termios()
    logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))

sys.excepthook = handle_exception

def asa_getpass(prompt='Password: ', stream=sys.stdout, inpu=sys.stdin, echochar='*'):
    passwd = _raw_input(prompt, stream, inpu, echochar)
    os.write(stream.fileno(), b'\n')
    termios.tcflush(stream.fileno(), termios.TCOFLUSH)
    return passwd

lastgetchar = ''

def _raw_input(prompt="", stream=sys.stdout, inpu=sys.stdin, echochar=None, useprintt=True):
    global lastgetchar
    # This doesn't save the string in the GNU readline history.
    prompt = str(prompt)
    fd = inpu.fileno()
    try:
        if prompt:
            try:
                os.write(stream.fileno(), prompt.encode('ascii'))
            except UnicodeEncodeError:
                # Use replace error handler to get as much as possible printed.
                prompt = prompt.encode(stream.encoding, 'replace')
                prompt = prompt.decode(stream.encoding)
                os.write(stream.fileno(), prompt.encode('ascii'))
            termios.tcflush(stream.fileno(), termios.TCOFLUSH)
#            if useprintt is True:
#                printt('_raw_input() wrote: "{}"'.format(prompt), nostdout=True)
        # NOTE: The Python C API calls flockfile() (and unlock) during readline.
        #line = inpu.readline()
        line = ''
        ch = ''
        while True:
            #ch = raw_inputch(inpu)
            lastlastgetchar = lastgetchar
#            printt('_raw_input() inpu.read(1) ...', nostdout=True)
            ch = os.read(fd, 1).decode('ascii')
#            printt('_raw_input() inpu.read(1) received: "{}"'.format(bytes(ch, 'ascii')), nostdout=True)
            lastgetchar = ch
            chb = bytes(ch, 'ascii')
            if ch == '\b' or chb == b'\x7f':
                if len(line) > 0:
                    line = line[:-1]
                    os.write(stream.fileno(), b'\b \b')
                    termios.tcflush(stream.fileno(), termios.TCOFLUSH)
            elif ch == '\r':
#                printt('_raw_input() received: "{}"'.format(line), nostdout=True)
                return line
            elif ch == '\n':
#                printt('_raw_input() received: "{}"'.format(line), nostdout=True)
                if lastlastgetchar != '\r':
                    if inpu.isatty() and stream.isatty():
                        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
                    return line
            elif chb == b'\0':
                continue
            else:
                line = line + ch
                if echochar is not None:
                    if len(echochar) > 0:
                        os.write(stream.fileno(), echochar.encode('ascii'))
                        termios.tcflush(stream.fileno(), termios.TCOFLUSH)
                else:
                    os.write(stream.fileno(), ch.encode('ascii'))
                    termios.tcflush(stream.fileno(), termios.TCOFLUSH)
    except:
        raise

def raw_inputch(inpu=sys.stdin):
    return os.read(inpu.fileno(), 1).decode('ascii')

SSH_CONN_KEY = 'SSH_CONNECTION'
if len(sys.argv) == 2 or len(sys.argv) == 3:
    local_ip_addr = sys.argv[1]
    remote_ip_addr, remote_port, local_ip_addr, local_port = local_ip_addr, None, local_ip_addr, None
    if len(sys.argv) == 3:
        remote_port, local_port = int(sys.argv[2]), int(sys.argv[2])
else:
    try:
        ssh_conn = os.environ[SSH_CONN_KEY]
        ssh_conn_l = os.environ[SSH_CONN_KEY].split()
        if len(ssh_conn_l) != 4:
           raise Exception('Environment variable {} is not in the expected `address port address port\' format: {}'.format(SSH_CONN_KEY, ssh_conn))
        remote_ip_addr, remote_port, local_ip_addr, local_port = ssh_conn_l
    except KeyError:
        local_ip_addr = '127.0.0.1'
        remote_ip_addr, remote_port, local_ip_addr, local_port = local_ip_addr, 22, local_ip_addr, 22
local_user = getpass.getuser()
# If the username is root, check for a home directory that looks like /home or /Users.
if local_user == 'root':
    homedir = expanduser("~")
    m = re.match("^.*/(?:Users|home)/.*(?<=/)([^/]+)/?", homedir)
    if m:
        local_user = m.group(1)
enable_password = 'asapass'
local_ip_addr_addr = local_ip_addr
local_ip_addr = ipaddress.ip_address(local_ip_addr)
if local_port is None:
    local_port = get_sshd_port.get_sshd_port()
    remote_port = local_port
local_port = int(local_port)
remote_port = int(remote_port)
if local_port < 1 or local_port > 65535 or remote_port < 1 or remote_port > 65535:
    raise Exception('Ports {} and/or {} are not valid.'.format(local_port, remote_port))
if isinstance(local_ip_addr, IPv6Address):
    if local_ip_addr == IPv6Address('::1'):
        local_ip_addr = IPv4Address('127.0.0.1')
    else:
        raise Exception('IPv6 is not supported other than for loopback addresses like `::1\'.')
    local_hostname = 'localhost'
else:
    local_hostname = socket.getfqdn(str(local_ip_addr))
    if local_hostname == str(local_ip_addr):
        raise Exception('Cannot resolve IP address `{}\' to a hostname.'.format(local_ip_addr))
if '.' not in local_hostname:
    local_hostname = '{}_{}'.format(local_hostname, local_port)
else:
    local_hostname = '{}_{}.{}'.format(local_hostname.split('.')[0], local_port, '.'.join(local_hostname.split('.')[1:]))

def flush():
    try:
        sys.stdout.flush()
    except:
        pass

grps = re.search(r"site(\d+)", local_hostname)
if grps:
    siteid = int(grps.group(1))
else:
    siteid = 1
kwds = {}
prefixlen = {}
ntwks_base = {}
ntwks = {}
ifaces = {}
peers = {}
offsets = {}