#!/usr/bin/env python
from upydevice import ble_scan, BASE_BLE_DEVICE
from binascii import hexlify
# import getpass
# import secrets
# import hashlib
# import string
# import upydev
import time
import sys
import os
import signal
import netifaces
import asyncio
# import nmap
import textwrap
import ast
import socket
import subprocess
import shlex
import webbrowser
import argparse
import shutil
import upydev
# from upydev import upip_host
from upydev.style_customs import one_darkStyle
from datetime import datetime, date, timedelta
from sys import platform as _platform
# from cryptography.hazmat.backends import default_backend
# from cryptography.hazmat.primitives.asymmetric import rsa
# from cryptography.hazmat.primitives import serialization
# from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from pygments.styles import get_style_by_name, get_all_styles
from pygments import highlight
from pygments.style import Style
from pygments.lexers import Python3Lexer
from pygments.formatters import Terminal256Formatter
from prompt_toolkit import PromptSession
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.auto_suggest import ConditionalAutoSuggest
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.filters import Condition
from prompt_toolkit.application import run_in_terminal, in_terminal
from prompt_toolkit.styles import Style
from prompt_toolkit.formatted_text import HTML
from prompt_toolkit import print_formatted_text
from prompt_toolkit.shortcuts import (radiolist_dialog, message_dialog,
                                      input_dialog, yes_no_dialog)


_WASPDEV = ["P8", "PineTime", "Pixl.js"]
_IS_WASPDEV = False


def parse_bash_profile():
    ALIASES = {}
    if '.bash_rc' in os.listdir(os.environ['HOME']):
        bash_rc = os.path.join(os.environ['HOME'], '.bash_rc')
        with open(bash_rc, 'r') as bf:
            bf_content = bf.read()
            bf_alias = [alias_line for alias_line in bf_content.splitlines()
                        if alias_line.startswith('alias')]

        for alias in bf_alias:
            alias_key = alias.split('=')[0].replace('alias', '').strip()
            try:
                alias_value = ast.literal_eval(alias.split('=')[1])
            except Exception as e:
                alias_value = alias.split('=')[1].split()[0]
            ALIASES[alias_key] = alias_value

    if '.profile' in os.listdir(os.environ['HOME']):
        profile = os.path.join(os.environ['HOME'], '.profile')
        with open(profile, 'r') as pf:
            pf_content = pf.read()
            pf_alias = [alias_line for alias_line in pf_content.splitlines()
                        if alias_line.startswith('alias')]

        for alias in pf_alias:
            alias_key = alias.split('=')[0].replace('alias', '').strip()
            try:
                alias_value = ast.literal_eval(alias.split('=')[1])
            except Exception as e:
                alias_value = alias.split('=')[1].split()[0]
            ALIASES[alias_key] = alias_value
    return ALIASES


class BLE_REPL_server:
    """
    BLE REPL server class
    """

    def __init__(self, dev=None, port=None, buff=1024, dev_id=None):
        self.uuid = dev.UUID
        self.dev = dev
        self.dev_id = dev_id
        self.buff = b''
        self.raw_buff = b''
        self.message = b''
        self.n_bytes_sent = 0
        self.prompt = b'>>> '
        self.output = None
        self.IO = False
        self.color_style = 'monokai'

    def sh_repl(self, cmd, **kargs):
        self.dev.wr_cmd(cmd, **kargs)

    async def as_sh_repl(self, cmd, **kargs):
        output = await self.dev.as_wr_cmd(cmd, **kargs)
        return output

    def flush_conn(self):
        self.dev.flush()

    def colored(self, msg):
        if self.color_style == 'one_dark':
            result = highlight(msg, Python3Lexer(),
                               Terminal256Formatter(style=one_darkStyle))
            lines = result.split('\n')
            lines_numbered = ["{}{:4}{} {}".format(
                DARK_GRAY, i, CEND, lines[i-1]) for i in range(1, len(lines)+1)]
            my_code_numb = '\n'.join(lines_numbered)
            print(my_code_numb)

        else:
            pygm_style = get_style_by_name(self.color_style)
            result = highlight(msg, Python3Lexer(),
                               Terminal256Formatter(style=pygm_style))
            lines = result.split('\n')
            lines_numbered = ["{}{:4}{} {}".format(
                DARK_GRAY, i, CEND, lines[i-1]) for i in range(1, len(lines)+1)]
            my_code_numb = '\n'.join(lines_numbered)
            print(my_code_numb)

    def do_color(self):
            self.colored(b'\n'.join(self.dev.raw_buff.splitlines()[1:-1]).decode())

    def read_callback_follow_paste(self, sender, data):
        try:
            cmd_filt = bytes(self.dev._cmdstr + '\r\n', 'utf-8')
            self.dev.raw_buff += data
            # self.pipe(data)
            # if not cmd_filt in self.raw_buff:
            #     pass
            # else:
            if not self.dev._cmdfiltered:
                cmd_filt = bytes(self.dev._cmdstr + '\r\n', 'utf-8')
                cmd_filt_pipe = bytes(self.dev._cmdstr + '\n', 'utf-8')
                data = b'' + data
                if cmd_filt in data:
                    data = data.replace(cmd_filt, b'', 1)
                    # data = data.replace(b'\r\n>>> ', b'')
                    self.dev._cmdfiltered = True
                if cmd_filt_pipe in data:
                    data = data.replace(cmd_filt_pipe, b'', 1)
                    self.dev._cmdfiltered = True
            else:
                try:
                    data = b'' + data
                    # data = data.replace(b'\r\n>>> ', b'')
                except Exception as e:
                    pass
            # self.raw_buff += data
            # self._line_buff += data + b'-'
            if self.dev.prompt in data:
                data = data.replace(b'\r', b'').replace(b'\r\n>>> ', b'').replace(
                    b'>>> ', b'').decode('utf-8', 'ignore')
                if data != '':
                    print(data, end='')

            else:
                data = data.replace(b'\r', b'').replace(b'\r\n>>> ', b'').replace(
                    b'>>> ', b'').decode('utf-8', 'ignore')
                print(data, end='')

        except KeyboardInterrupt:
            print('CALLBACK_KBI')
            pass

    async def as_write_read_follow_paste(self, data, rtn_buff=False):
        if not self.dev.is_notifying:
            try:
                await self.dev.ble_client.start_notify(self.dev.readables['Nordic UART TX'], self.read_callback_follow_paste)
                self.dev.is_notifying = True
            except Exception as e:
                pass
        if len(data) > self.dev.len_buffer:
            for i in range(0, len(data), self.dev.len_buffer):
                await self.dev.ble_client.write_gatt_char(self.dev.writeables['Nordic UART RX'], data[i:i+self.dev.len_buffer])
        else:
            await self.dev.ble_client.write_gatt_char(self.dev.writeables['Nordic UART RX'], data)
        while self.dev.prompt not in self.dev.raw_buff:
            try:
                await asyncio.sleep(0)
            except KeyboardInterrupt:
                print('Catch here1')
                data = bytes(self.dev._kbi, 'utf-8')
                await self.dev.ble_client.write_gatt_char(self.dev.writeables['Nordic UART RX'], data)
        if self.dev.is_notifying:
            try:
                await self.dev.ble_client.stop_notify(self.dev.readables['Nordic UART TX'])
                self.dev.is_notifying = False
            except Exception as e:
                pass
        self.dev._cmdfiltered = False
        if rtn_buff:
            return self.dev.raw_buff

    async def as_wr_cmd_paste(self, cmd, silent=False, rtn=True, rtn_resp=False,
                              long_string=False, follow=False, kb=False):
        self.dev.output = None
        self.dev.response = ''
        self.dev.raw_buff = b''
        self.dev.buff = b''
        self.dev._cmdstr = cmd
        self.dev.cmd_finished = False
        # self.flush()
        data = self.dev.fmt_data(cmd)  # make fmt_data
        n_bytes = len(data)
        self.dev.bytes_sent = n_bytes
        # time.sleep(0.1)
        # self.buff = self.read_all()[self.bytes_sent:]
        if follow:
            self.dev.buff = await self.as_write_read_follow_paste(data, rtn_buff=True)
        else:
            self.dev.buff = await self.dev.as_write_read_waitp(data, rtn_buff=True)
        if self.dev.buff == b'':
            # time.sleep(0.1)
            self.dev.buff = self.dev.read_all()
        # print(self.buff)
        # filter command
        if follow:
            silent = True
        cmd_filt = bytes(cmd + '\r\n', 'utf-8')
        self.dev.buff = self.dev.buff.replace(cmd_filt, b'', 1)
        if self.dev._traceback in self.dev.buff:
            long_string = True
        if long_string:
            self.dev.response = self.dev.buff.replace(b'\r', b'').replace(
                b'\r\n>>> ', b'').replace(b'>>> ', b'').decode('utf-8', 'ignore')
        else:
            self.dev.response = self.dev.buff.replace(b'\r\n', b'').replace(
                b'\r\n>>> ', b'').replace(b'>>> ', b'').decode('utf-8', 'ignore')
        if not silent:
            if self.dev.response != '\n' and self.dev.response != '':
                print(self.dev.response)
            else:
                self.dev.response = ''
        if rtn:
            self.dev.get_output()
            if self.dev.output == '\n' and self.dev.output == '':
                self.dev.output = None
            if self.dev.output is None:
                if self.dev.response != '' and self.dev.response != '\n':
                    self.dev.output = self.dev.response
        self.dev.cmd_finished = True
        if rtn_resp:
            return self.dev.output

    async def as_paste_buff(self, cmd, **kargs):
        # print('Here')
        long_command = cmd
        await self.dev.ble_client.write_gatt_char(self.dev.writeables['Nordic UART RX'], b'\x05')
        await asyncio.sleep(1, loop=self.dev.loop)
        # await self.dev.ble_client.write_gatt_char(self.dev.writeables['Nordic UART RX'], b'\x04')
        # print(long_command)
        lines = long_command.split('\n')
        # print(lines)
        for line in lines:
            self.flush_conn()
            await asyncio.sleep(0.2, loop=self.dev.loop)
            data = bytes(line + '\n', 'utf-8')
            if len(data) > self.dev.len_buffer:
                for i in range(0, len(data), self.dev.len_buffer):
                    await self.dev.ble_client.write_gatt_char(self.dev.writeables['Nordic UART RX'], data[i:i+self.dev.len_buffer])
            else:
                await self.dev.ble_client.write_gatt_char(self.dev.writeables['Nordic UART RX'], data)
        output = await self.as_wr_cmd_paste('\x04', **kargs)

        return output
        # output = await self.dev.as_wr_cmd('\x05' + cmd + '\x04', **kargs)
        # return output


class BLE_TOOL:
    def __init__(self, ble_sh_repl, port=None):
        self.host = None
        self.port = port
        self.ble_cp = ble_sh_repl
        self.buff = bytearray(1024*2)
        self.bloc_progress = ["▏", "▎", "▍", "▌", "▋", "▊", "▉"]
        self.columns, self.rows = os.get_terminal_size(0)
        self.cnt_size = 65
        self.bar_size = int((self.columns - self.cnt_size))
        self.pb = False
        self.wheel = ['|', '/', '-', "\\"]

    def start_SOC(self):
        # self.sh_srepl.dev.serial.close()
        # self.serv_soc = Pyboard(self.sh_srepl.dev.serial_port)
        pass

    def stop_SOC(self):
        # self.conn.close()
        # self.serv_soc.close()
        # self.sh_srepl.dev.serial.open()
        # self.sh_srepl.sh_repl("\x03\x0d")
        pass

    def get_pb(self):
        self.columns, self.rows = os.get_terminal_size(0)
        if self.columns > self.cnt_size:
            self.bar_size = int((self.columns - self.cnt_size))
            self.pb = True
        else:
            self.bar_size = 1
            self.pb = False

    def do_pg_bar(self, index, wheel, nb_of_total, speed, time_e, loop_l,
                  percentage, ett):
        l_bloc = self.bloc_progress[loop_l]
        if index == self.bar_size:
            l_bloc = "█"
        sys.stdout.write("\033[K")
        print('▏{}▏{:>2}{:>5} % | {} | {:>5} KB/s | {}/{} s'.format("█" * index + l_bloc + " "*((self.bar_size+1) - len("█" * index + l_bloc)),
                                                                    wheel[index % 4],
                                                                    int((percentage)*100),
                                                                    nb_of_total, speed,
                                                                    str(timedelta(seconds=time_e)).split(
                                                                        '.')[0][2:],
                                                                    str(timedelta(seconds=ett)).split('.')[0][2:]), end='\r')
        sys.stdout.flush()

    def get(self, src, chunk_size=256):  # from Pyboard.py
        self.get_pb()
        cnt = 0
        t_start = time.time()
        # def_chunk = chunk
        self.ble_cp.sh_repl("os.stat('{}')[6]".format(src), silent=True)
        self.ble_cp.dev.get_output()
        sz = self.ble_cp.dev.output
        print("{}  [{:.2f} KB]".format(src, sz / 1024))
        self.ble_cp.flush_conn()
        # self.start_SOC()
        self.ble_cp.sh_repl("f=open('%s','rb');r=f.read" % src, silent=True)
        with open(src, 'wb') as f:
            pass
        with open(src, 'ab') as f:
            while True:
                data = b''
                self.ble_cp.sh_repl("print(r(%u))" % chunk_size,
                                    rtn=True, silent=True)
                if self.ble_cp.buff != b'':
                    pass
                    # msg_b = self.ble_cp.buff.split(b'\n')[1:]
                    # try:
                    #     msg_b[-1] = msg_b[-1].replace(b'>>> ', b'')
                    # except Exception as e:
                    #     pass
                    # self.ble_cp.output = b'\n'.join([m for m in msg_b if m != b''])
                try:
                    data = eval(self.ble_cp.dev.response)
                except Exception as e:
                    print(e)
                    data = b''
                # print(type(data), data)
                if data == b'':
                    break
                f.write(data)
                cnt += len(data)
                loop_index_f = (cnt/sz)*self.bar_size
                loop_index = int(loop_index_f)
                loop_index_l = int(round(loop_index_f-loop_index, 1)*6)
                nb_of_total = "{:.2f}/{:.2f} KB".format(cnt/(1024), sz/(1024))
                percentage = cnt / sz
                t_elapsed = time.time() - t_start
                t_speed = "{:^2.2f}".format((cnt/(1024))/t_elapsed)
                ett = sz / (cnt / t_elapsed)
                if self.pb:
                    self.do_pg_bar(loop_index, self.wheel,
                                   nb_of_total, t_speed, t_elapsed,
                                   loop_index_l, percentage, ett)
        print('\n')
        self.ble_cp.sh_repl("f.close()")

    def put(self, src, chunk_size=256, abs_path=True):  # from Pyboard.py
        self.get_pb()
        sz = os.stat(src)[6]
        cnt = 0
        t_start = time.time()
        if not abs_path:
            src_ori = src
            src = src.split('/')[-1]
        print("{}  [{:.2f} KB]".format(src, sz / 1024))
        self.ble_cp.sh_repl("f=open('%s','wb');w=f.write" % src, silent=True)
        if not abs_path:
            src = src_ori
        with open(src, 'rb') as f:
            while True:
                data = f.read(chunk_size)
                if not data:
                    break
                # if sys.version_info < (3,):
                #     self.serv_soc.exec_('w(b' + repr(data) + ')')
                # else:
                #     self.serv_soc.exec_('w(' + repr(data) + ')')
                # self.ble_cp.sh_repl('w(' + repr(data) + ')', assert_len=False)
                # self.ble_cp.send_message('w(' + repr(data) + ')'+'\r')
                if dev_platform == 'pyboard':
                    self.ble_cp.sh_repl('w(' + repr(data) + ')', silent=True)
                    # time.sleep(0.05)
                else:
                    if len(data) > self.ble_cp.dev.len_buffer:
                        for i in range(0, len(data), self.ble_cp.dev.len_buffer):
                            self.ble_cp.sh_repl('w(' + repr(data[i:i+self.ble_cp.dev.len_buffer]) + ')',
                                                silent=True)
                    else:
                        self.ble_cp.sh_repl('w(' + repr(data) + ')',
                                            silent=True)
                    # time.sleep(0.2)
                # self.ble_cp.sh_repl('len('+ repr(data) + ')'+'\r')
                # print(len(data))
                cnt += len(data)
                loop_index_f = (cnt/sz)*self.bar_size
                loop_index = int(loop_index_f)
                loop_index_l = int(round(loop_index_f-loop_index, 1)*6)
                nb_of_total = "{:.2f}/{:.2f} KB".format(cnt/(1024), sz/(1024))
                percentage = cnt / sz
                t_elapsed = time.time() - t_start
                t_speed = "{:^2.2f}".format((cnt/(1024))/t_elapsed)
                ett = sz / (cnt / t_elapsed)
                if self.pb:
                    self.do_pg_bar(loop_index, self.wheel,
                                   nb_of_total, t_speed, t_elapsed,
                                   loop_index_l, percentage, ett)
        print('\n')
        self.ble_cp.sh_repl("f.close()", silent=True)
        self.ble_cp.flush_conn()


# ARGPARSE HERE TO ACCEPT TARGET AND PASSWORD
parser = argparse.ArgumentParser()
parser.add_argument("-t", help='device uuid, can be "auto" or @name', required=True)
parser.add_argument("-v", help='verbose mode',
                    default=False, action='store_true')
parser.add_argument(
    "-dev", help='device name, default unique_id', required=False)
parser.add_argument("-r", help='reset on exit',
                    default=False, action='store_true')
parser.add_argument("-buff", help='Ble max writing length to use',
                    default=100, type=int)
args = parser.parse_args()


# HANDSHAKE
if args.t != "auto" and "@" not in args.t:
    basedev = BASE_BLE_DEVICE(args.t, name=args.dev, lenbuff=args.buff)
else:
    if args.t == "auto":
        devs = []
        while len(devs) == 0:
            try:
                devs = ble_scan()
            except KeyboardInterrupt:
                sys.exit()
        basedev = BASE_BLE_DEVICE(devs[0], lenbuff=args.buff)
    elif "@" in args.t:
        devs = []
        while len(devs) == 0:
            try:
                devs = ble_scan()
            except KeyboardInterrupt:
                sys.exit()
        bdev = args.t.split("@")[1]
        for dev in devs:
            if bdev in dev.name:
                basedev = BASE_BLE_DEVICE(devs[0], lenbuff=args.buff)
                break

basedev.connect(show_servs=args.v, debug=args.v)
if basedev.name in _WASPDEV:
    basedev.len_buffer = 20  # FIXME: INCREASE MTU SIZE IN NRF52
    _IS_WASPDEV = True
if not basedev.connected:
    print('Device not reachable, try again.')
    sys.exit()
# basedev.get_services()
if args.dev is not None:
    basedev.name = args.dev

time.sleep(0.2)
kbi = basedev.kbi()
time.sleep(0.2)
uid = basedev.wr_cmd("from machine import unique_id; unique_id()",
                     silent=True, rtn_resp=True)
try:
    unique_id = hexlify(uid).decode()
except Exception as e:
    unique_id = uid

time.sleep(0.2)
devp = basedev.wr_cmd("import sys; sys.platform", silent=True, rtn_resp=True)
dev_platform = devp
if args.v:
    print('{}@{}'.format(dev_platform, unique_id))
if basedev.name is None:
    basedev.name = '{}-{}'.format(dev_platform, unique_id)
# ble_cp = SERIAL_REPL_server(dev=espdev, dev_id=unique_id)
# shr_htool = SERIAL_TOOL(sh_srepl=ble_cp)
print('BLE SHELL-REPL connected')
time.sleep(0.2)
basedev.banner()
# ble_cp.sh_repl("\x02", banner=True)
# ble_cp.flush()
print('Use CTRL-x to exit, Use CTRL-s to toggle shell/repl mode\nUse CTRL-k to see more info')
ble_cp = BLE_REPL_server(dev=basedev, dev_id=unique_id)
ble_htool = BLE_TOOL(ble_sh_repl=ble_cp)
# basedev.disconnect()
# PROMT SESSION CONFIGURATION

# Style
style_p = Style.from_dict({
    # User input (default text).
    '':          '#ffffff',

    # Prompt.
    'userpath': 'ansimagenta bold',
    'username': 'ansigreen bold',
    'at':       'ansigreen bold',
    'colon':    '#ffffff',
    'pound':    'ansiblue bold',
    'host':     'ansigreen bold',
    'path':     'ansiblue bold',
})

# SET DEV NAME
host_name = unique_id
if args.dev is not None:
    host_name = args.dev

shell_message = [
    ('class:userpath',    ''),
    ('class:username', dev_platform),
    ('class:at',       '@'),
    ('class:host',     host_name),
    ('class:colon',    ':'),
    ('class:path',     '~ '),
    ('class:pound',    '$ '),
]

d_prompt = '>>> '
# KEYBINDINGS
kb = KeyBindings()
SHELL_ALIASES = parse_bash_profile()
mem_show_rp = {'show': False, 'call': False, 'used': '?',
               'free': '?', 'percent': 0}
dev_path = {'p': ' '}
local_path = {'p': ''}
show_local_path = {'s': False}
status_encryp_msg = {'S': False, 'Toggle': True}
exit_flag = {'exit': False}
encrypted_flag = {'sec': True}
prompt = {'p': '>>> '}
paste_flag = {'p': False}
paste_buffer = {'B': []}
reset_flag = {'R': args.r}
autosuggest = {'A': False}
shell_mode = {'S': False}
frozen_modules = {'FM': [], 'SUB': []}
edit_mode = {'E': False, 'File': ''}
shell_mode_run = {'R': False}
script_is_running = {'R': False, 'script': 'test_code'}
shell_prompt = {'s': shell_message}
shell_commands = ['sz', 'cd', 'mkdir', 'cat', 'head', 'rm', 'rmdir', 'pwd',
                  'run']
custom_sh_cmd_kw = ['df', 'datetime', 'ifconfig', 'ifconfig_t', 'netscan',
                    'apconfig', 'apconfig_t', 'meminfo', 'install',
                    'apscan', 'touch', 'edit', 'srepl',
                    'whoami', 'exit', 'pwdl', 'lsl', 'cdl', 'put', 'get',
                    'catl', 'l_micropython', 'ls', 'python', 'python3', 'vim',
                    'set_localtime', 'tree', 'l_ifconfig',
                    'l_ifconfig_t', 'reload', 'docs', 'flush_soc',
                    'get_rawbuff', 'l_tree', 'view', 'bat', 'rcat',
                    'batl', 'fw', 'flash', 'du', 'ldu', 'dsync', 'upipl',
                    'uping', 'lping', 'pkg_info', 'update_upyutils',
                    'timeit', 'i2c', 'dump_mem', 'git', 'emacs', 'batstyle',
                    'upy-config', 'tig', 'get_services', 'pytest',
                    'rssi']
CRED = '\033[91;1m'
CGREEN = '\33[32;1m'
CEND = '\033[0m'
YELLOW = '\u001b[33m'
BCYAN = '\u001b[36;1m'
ABLUE_bold = '\u001b[34;1m'
MAGENTA_bold = '\u001b[35;1m'
WHITE_ = '\u001b[37m'
LIGHT_GRAY = "\033[0;37m"
DARK_GRAY = "\033[1;30m"
PYGM_SYNTAX = list(get_all_styles()) + ['one_dark']
AUTHMODE_DICT = {0: 'NONE', 1: 'WEP', 2: 'WPA PSK', 3: 'WPA2 PSK',
                    4: 'WPA/WAP2 PSK'}

git_diff_files = {'diff': [], 'commit': '', 'n_commits': 0}
# START IN SHELL MODE
if shell_mode['S']:
    prompt['p'] = d_prompt
    shell_mode['S'] = False
else:
    prompt['p'] = shell_prompt['s']
    shell_mode['S'] = True
    time.sleep(0.5)
    ble_cp.sh_repl('import gc;import os;', silent=True)
    # ble_cp.sh_repl('from upysh import *;print(">>> ");', silent=True)
    time.sleep(1)
    ble_cp.sh_repl('from upysh import *;print(">>> ");', silent=True)
    ble_cp.flush_conn()
    # ble_cp.flush_conn()
    ble_cp.dev.output = None
    # ble_cp.sh_repl("help('modules')", silent=True)
    # frozen_modules['FM'] = ble_cp.dev.output.split()[:-6]
    # ble_cp.flush_conn()
    # ble_cp.flush_conn()
    if _IS_WASPDEV:
        time.sleep(0.5)
        ble_cp.sh_repl('wasp.system.wake()', silent=True)
#
#
# KEYBINDINGS INFO
kb_info = """
Custom keybindings:
- CTRL-x : to exit SHELL-REPL Terminal
- CTRL-p : toggle RAM status right aligned message (USED/FREE)
- CTRL-e : paste mode in repl,(in shell mode set cursor position at the end)/ (edit mode after 'edit' shell command)
- CTRL-d : ends paste mode in repl, (ends edit mode after 'edit' shell command)
          (or soft-reset in repl, CTRL-C to start repl again)
- CTRL-c : KeyboardInterrupt, in normal mode, cancel in paste or edit mode
- CTRL-b : prints MicroPython version and sys platform
- CTRL-r : to flush line buffer
- CTRL-o : to list files in cwd (sz shorcut command)
- CTRL-n : shows mem_info()
- CTRL-y : gc.collect() shortcut command
- CTRL-space : repeats last command
- CTRL-t : runs test_code.py if present
- CTRL-w : flush test_code from sys modules, so it can be run again
- CTRL-a : set cursor position at the beggining
- CTRL-f : toggle autosuggest mode (Fish shell like)(use right arrow to complete)
- CTRL-g : To active listen for device output (Timer or hardware interrupts), CTRL-c to break
- CRTL-s : toggle shell mode to navigate filesystem (see shell commands)
- CTRL-k : prints the custom keybindings (this list) (+ shell commands if in shell mode)
>>> """

shell_commands_info = """
* Autocompletion commands:
     - tab to autocomplete device file / dirs names / raw micropython (repl commands)
     - shift-tab to autocomplete shell commands
     - shift-right to autocomplete local file / dirs names
     - shift-left to toggle local path in prompt

* Device shell commands:
    * upysh commands:
        - sz   : list files and size in bytes
        - head : print the head of a file
        - cat  : prints the content of a file
        - mkdir: make directory
        - cd   : change directory (cd .. to go back one level)
        - pwd  : print working directory
        - rm   : to remove a file
        - rmdir: to remove a directory

    * custom shell commands:
        - ls  : list device files in colored format (same as pressing tab on empty line)(allows "*" wildcard or directories)
        - tree : to print a tree version of filesystem (to see also hidden files/dirs use 'tree -a')
        - run  : to run a 'script.py'
        - df   : to see filesystem flash usage (and SD if already mounted)
        - du   : display disk usage statistics (usage: "du", "du [dir or file]" + '-d' deep level option)
        - meminfo: to see RAM info
        - dump_mem: to do a memory dump
        - whoami : to see user, system and machine info
        - datetime: to see device datetime (if not set, will display uptime)
        - set_localtime : to set the device datetime from the local machine time
        - ifconfig: to see STATION interface configuration (IP, SUBNET, GATEAWAY, DNS)
        - ifconfig_t: to see STATION interface configuration in table format
                      (IP, SUBNET, GATEAWAY, DNS, ESSID, RSSI)
        - netscan: to scan WLANs available, (ESSID, MAC ADDRESS, CHANNEL, RSSI, AUTH MODE, HIDDEN)
        - uping : to make the device send ICMP ECHO_REQUEST packets to network hosts (do 'uping host' to ping local machine)
        - apconfig: to see access POINT (AP) interface configuration (IP, SUBNET, GATEAWAY, DNS)
        - apconfig_t: to see access POINT (AP) interface configuration in table format
                     (SSID, BSSID, CHANNEL, AUTH, IP, SUBNET, GATEAWAY, DNS)
        - install: to install a library into the device with upip.
        - touch  : to create a new file (e.g. touch test.txt)
        - edit   : to edit a file (e.g. edit my_script.py)
        - get    : to get a file from the device (also allows "*" wildcard, 'cwd' or multiple files)
        - put    : to upload a file to the device (also allows "*" wildcard, 'cwd' or multiple files)
        - d_sync : to recursively sync a local directory with the device filesystem
        - srepl  : to enter the Serial Terminal (This needs Picocom)
        - reload : to delete a module from sys.path so it can be imported again.
        - flush_soc: to flush serial in case of wrong output
        - view   : to preview '.pbm' binary image files (image need to be centered and rows = columns)
        - bat    : prints the content of a '.py' file with Python syntax hightlighting
        - batstyle: 'bat' output style; to set: 'batstyle [style]', to see: 'batstyle': current style / 'batstyle -a': all styles
        - rcat   : prints the raw content of a file
        - timeit : to measure execution time of a script/command
        - i2c    : config/scan (config must be used first, i2c config -scl [SCL] -sda [SDA])
        - upy-config: interactive dialog to configure Network (connect to a WLAN or set an AP) or Interafaces (I2C)
        - jupyterc: to run MicroPython upydevice kernel for jupyter console
        - rssi: to get signal strength of the device.
        - exit   : to exit SHELL-REPL Terminal
                   to exit without reset do 'exit -nr'
                   to exit and do hard reset 'exit -hr'

* Local shell commands:
    - pwdl   : to see local path
    - cdl    : to change local directory
    - lsl    : to list local directory
    - catl   : to print the contents of a local file
    - batl   : prints the content of a local '.py' file with Python syntax hightlighting
    - l_micropython: if "micropython" local machine version available in $PATH, runs it.
    - python : switch to local python3 repl
    - vim    : to edit a local file with vim  (e.g. vim script.py)
    - emacs  : to edit a local file with emacs (e.g. emacs script.py)
    - l_ifconfig: to see local machine STATION interface configuration (IP, SUBNET, GATEAWAY, DNS)
    - l_ifconfig_t: to see local machine STATION interface configuration in table format
                  (IP, SUBNET, GATEAWAY, DNS, ESSID, RSSI)
    - docs : to open MicroPython docs site in the default web browser, if a second term
            is passed e.g. 'docs machine' it will open the docs site and search for 'machine'
    - get_rawbuff: to get the raw output of a command (for debugging purpose)
    - get_services: to get device advertised services
    - fw   : + list/get/update/latest firmware e.g (fw list latest, fw get latest) (use option -n [expresion to match])
            e.g. (fw get latest -n spiram, or fw get esp32-idf3-20200114-v1.12-63-g1c849d63a.bin)
    - flash : to flash a firmware file, e.g 'flash esp32-idf3-20200114-v1.12-63-g1c849d63a.bin'
    - ldu   : display local path disk usage statistics (usage: "ldu", "ldu [dir or file]" + '-d' deep level option)
    - upipl : (usage 'upipl' or 'upipl [module]' display available micropython packages that can be installed with install command
    - pkg_info: to see the PGK-INFO file of a module if available at pypi.org or micropython.org/pi
    - lping : to make local machine send ICMP ECHO_REQUEST packets to network hosts (do 'lping dev' to ping the device)
    - update_upyutils: to install 'upydev update_upyutils' scripts in the device
    - git : to call git commands and integrate the git workflow into a project (needs 'git' available in $PATH)
            Use 'git init dev' to initiate device repo
            Use 'git push dev' after a 'git commit ..' or 'git pull' to push the changes to the device.
            Use 'git log dev' to see the latest commit pushed to the device ('git log dev -a' to see all commits)
            Use 'git log host' to see the latest commit in the local repo
            Use 'git status dev' to see if the local repo is ahead of the device repo and track these changes
            Use 'git clone_dev' to clone the local repo into the device
            Use 'git repo' to open the remote repo in the web browser if remote repo exists
            Any other git command will be echoed directly to git
    - tig: to use the 'Text mode interface for git' tool. Must be available in $PATH

* Local shell special commands:
    Commands that start with '%' or not registered will be forwarded to local shell.
"""
#
# TAB OPTIONS FORMATTER


# from @The Data Scientician : https://stackoverflow.com/questions/9535954/printing-lists-as-tabular-data
def print_table(data, cols=4, wide=16, format_SH=False, autocols=True,
                autocol_tab=False, sort=True, autowide=False, max_cols=4):
    '''Prints formatted data on columns of given width.'''
    if sort:
        data.sort(key=str.lower)
    if format_SH:
        if autocols:
            wide_data = max([len(namefile) for namefile in data]) + 2
            if wide_data > wide:
                wide = wide_data
            columns, rows = os.get_terminal_size(0)
            cols = int(columns/(wide))
        data = ['{}{}{}{}'.format(ABLUE_bold, val, CEND, ' '*(wide-len(val)))
                if '.' not in val else val for val in data]
        data = ['{}{}{}{}'.format(MAGENTA_bold, val, CEND, ' '*(wide-len(val)))
                if '.py' not in val and '.mpy' not in val and '.' in val else val for val in data]
        data = ['{}{}{}{}'.format(
            CGREEN, val, CEND, ' '*(wide-len(val))) if '.mpy' in val else val for val in data]
    if autocol_tab:
        data = [namefile if len(namefile) < wide else namefile +
                '\n' for namefile in data]
    if autowide:
        wide_data = max([len(namefile) for namefile in data]) + 2
        if wide_data > wide:
            wide = wide_data
        columns, rows = os.get_terminal_size(0)
        cols = int(columns/(wide))
        if max_cols < cols:
            cols = max_cols
    n, r = divmod(len(data), cols)
    pat = '{{:{}}}'.format(wide)
    line = '\n'.join(pat * cols for _ in range(n))
    last_line = pat * r
    print(line.format(*data))
    print(last_line.format(*data[n*cols:]))


def read_pbm(fname):
    fmt, banner, dims, data = fname.splitlines()[1:]
    data = data.replace(b'>>> ', b'')
    dimensions = [int(dim.decode()) for dim in dims.split(b' ')]
    pixel_blocks = [bin(x).replace('0b', '0'*(8-len(bin(x)[2:]))) for x in data]
    raw_bin_img = ''.join(pixel_blocks)
    img_term = ["█" if int(x) > 0 else " " for x in raw_bin_img]
    term_images = '\n'.join(textwrap.wrap(
        ''.join(img_term[6:]), dimensions[0]*2)[:dimensions[0]])
    img_1 = '\n'.join([line[:dimensions[0]] for line in term_images.split('\n')])
    return dimensions, data, raw_bin_img, img_1


# SHELL COMMANDS HANDLERS


def map_upysh(cmd_inp):
    frst_cmd = cmd_inp.split(' ')[0]
    if len(cmd_inp.split(' ')) > 1:
        scnd_cmd = cmd_inp.split(' ')[1]
        if scnd_cmd != '':
            shell_cmd = "{}('{}')".format(frst_cmd, scnd_cmd)
            if '*' in scnd_cmd and frst_cmd in ['rm', 'cat', 'head', 'rmdir']:
                pattrn = scnd_cmd.replace('*', '')
                shell_cmd = f"__ = list(map({frst_cmd}, [f for f in os.listdir(os.getcwd()) if '{pattrn}' in f]))"
    else:
        shell_cmd = frst_cmd

    if shell_cmd == 'sz':
        shell_cmd = 'ls()'
    if frst_cmd == 'run':
        shell_cmd = 'import {}'.format(scnd_cmd.split('.')[0])
        # make a run interactive mode that do not escape input
        # conditional ENTER, flush buffer, send run command, then CTRL-C can
        # be catched, print in terminal
    if shell_cmd == 'cd':
        shell_cmd = "cd('/')"
    return shell_cmd, frst_cmd


def _dt_format(number):
        rtc_n = str(number)
        if len(rtc_n) == 1:
            rtc_n = "0{}".format(rtc_n)
            return rtc_n
        else:
            return rtc_n


def _ft_datetime(t_now):
    return([_dt_format(i) for i in t_now])


def sortSecond(val):
    return val[1]


# def get_ip():
#     # scanoutput = subprocess.check_output(["ipconfig", "getifaddr", "en0"])
#     # ip = scanoutput.decode('utf-8').split('\n')[0]
#     try:
#         ip = [netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr'] for
#               iface in netifaces.interfaces() if netifaces.AF_INET in
#               netifaces.ifaddresses(iface)][-1]
#         return ip
#     except Exception as e:
#         try:
#             ip_soc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#             ip_soc.connect(('8.8.8.8', 1))
#             local_ip = ip_soc.getsockname()[0]
#             ip_soc.close()
#             return local_ip
#         except Exception as e:
#             return '0.0.0.0'


class LTREE:

    def __repr__(self):
        self.__call__()
        return ""

    def __call__(self, path=".", level=0, is_last=False, is_root=True,
                 carrier="│   ", hidden=False):
        if is_root:
            print('\u001b[34;1m{}\033[0m'.format(path))
        os.chdir(path)
        r_path = path
        path = "."
        if hidden:
            l = os.listdir(path)
        else:
            l = [f for f in os.listdir(path) if not f.startswith('.')]
        nf = len([file for file in l if not os.stat(
            "%s/%s" % (path, file))[0] & 0x4000])
        nd = len(l) - nf
        ns_f, ns_d = 0, 0
        l.sort()
        if len(l) > 0:
            last_file = l[-1]
        else:
            last_file = ''
        for f in l:
            st = os.stat("%s/%s" % (path, f))
            if st[0] & 0x4000:  # stat.S_IFDIR
                print(self._treeindent(level, f, last_file, is_last=is_last,
                                       carrier=carrier) + "  \u001b[34;1m%s\033[0m" % f)
                if f == last_file and level == 0:
                    carrier = "    "
                os.chdir(f)
                level += 1
                lf = last_file == f
                if level > 1:
                    if lf:
                        carrier += "     "
                    else:
                        carrier += "    │"
                ns_f, ns_d = self.__call__(level=level, is_last=lf,
                                           is_root=False, carrier=carrier,
                                           hidden=hidden)
                if level > 1:
                    carrier = carrier[:-5]
                os.chdir('..')
                level += (-1)
                nf += ns_f
                nd += ns_d
            else:
                print(self._treeindent(level, f, last_file,
                                       is_last=is_last, carrier=carrier) + " %s" % (f))
        if is_root:
            nd_str = 'directories'
            nf_str = 'files'
            if nd == 1:
                nd_str = 'directory'
            if nf == 1:
                nf_str = 'file'
            print('\n{} {}, {} {}'.format(nd, nd_str, nf, nf_str))
            if r_path != ".":
                os.chdir('..')
        else:
            return (nf, nd)

    def _treeindent(self, lev, f, lastfile, is_last=False, carrier=None):
        if lev == 0:
            if f != lastfile:
                return "├──"
            else:
                return "└──"
        else:
            if f != lastfile:
                return carrier + "    ├────"
            else:
                return carrier + "    └────"


tree = LTREE()


class DISK_USAGE:

    def __repr__(self):
        self.__call__()
        return ""

    def __call__(self, path=".", dlev=0, max_dlev=0, hidden=False, absp=True):
        if path != ".":
                if not os.stat(path)[0] & 0x4000:
                    print('{:9} {}'.format(
                        self.print_filesys_info(os.stat(path)[6]), path))
                else:
                    if hidden:
                        resp = {
                            path+'/'+dir: os.stat(path+'/'+dir)[6] for dir in os.listdir(path)}
                    else:
                        resp = {
                            path+'/'+dir: os.stat(path+'/'+dir)[6] for dir in os.listdir(path) if not dir.startswith('.')}
                    for dir in resp.keys():

                        if not os.stat(dir)[0] & 0x4000:
                            if absp:
                                print('{:9} {}'.format(
                                    self.print_filesys_info(resp[dir]), dir))
                            else:
                                print('{:9} {}'.format(self.print_filesys_info(
                                    resp[dir]), dir.split('/')[-1]))

                        else:
                            if dlev < max_dlev:
                                dlev += 1
                                self.__call__(path=dir, dlev=dlev,
                                              max_dlev=max_dlev, hidden=hidden)
                                dlev += (-1)
                            else:
                                if absp:
                                    print('{:9} \u001b[34;1m{}\033[0m'.format(
                                        self.print_filesys_info(self.get_dir_size_recursive(dir)), dir))
                                else:
                                    print('{:9} \u001b[34;1m{}\033[0m'.format(self.print_filesys_info(
                                        self.get_dir_size_recursive(dir)), dir.split('/')[-1]))

        else:
            if hidden:
                resp = {path+'/'+dir: os.stat(path+'/'+dir)
                        [6] for dir in os.listdir(path)}
            else:
                resp = {
                    path+'/'+dir: os.stat(path+'/'+dir)[6] for dir in os.listdir(path) if not dir.startswith('.')}
            for dir in resp.keys():

                if not os.stat(dir)[0] & 0x4000:
                    print('{:9} {}'.format(self.print_filesys_info(resp[dir]), dir))

                else:
                    if dlev < max_dlev:
                        dlev += 1
                        self.__call__(path=dir, dlev=dlev,
                                      max_dlev=max_dlev, hidden=hidden)
                        dlev += (-1)
                    else:
                        print('{:9} \u001b[34;1m{}\033[0m'.format(
                            self.print_filesys_info(self.get_dir_size_recursive(dir)), dir))

    def print_filesys_info(self, filesize):
        _kB = 1024
        if filesize < _kB:
            sizestr = str(filesize) + " by"
        elif filesize < _kB**2:
            sizestr = "%0.1f KB" % (filesize / _kB)
        elif filesize < _kB**3:
            sizestr = "%0.1f MB" % (filesize / _kB**2)
        else:
            sizestr = "%0.1f GB" % (filesize / _kB**3)
        return sizestr

    def get_dir_size_recursive(self, dir):
        return sum([os.stat(dir+'/'+f)[6] if not os.stat(dir+'/'+f)[0] & 0x4000 else self.get_dir_size_recursive(dir+'/'+f) for f in os.listdir(dir)])


du = DISK_USAGE()


def get_dir_size_recursive(dir):
    return sum([os.stat(dir+'/'+f)[6] if not os.stat(dir+'/'+f)[0] & 0x4000 else get_dir_size_recursive(dir+'/'+f) for f in os.listdir(dir)])

#


def d_sync_recursive(folder, dev=None, rootdir='./', root_sync_folder=None,
                     show_tree=False):
    t0 = time.time()
    type_file_dict = {True: '<f>', False: '<d>'}
    if folder == root_sync_folder:
        print('DIRECTORY TO SYNC: {}'.format(folder))
        print('DIRECTORY SIZE: {}'.format(
            print_filesys_info(get_dir_size_recursive(folder))))
    if show_tree:
        print('DIRECTORY TREE STRUCTURE:\n')
        tree(path=folder)
    time.sleep(1)
    print('\n')
    print('***** {} *****'.format(folder))
    print('\n')
    directory = folder
    file_list = None
    file_list_abs_path = []
    dir_list_abs_path = []
    current_dir = directory
    # get directory structure:
    print('ROOT DIRECTORY: {}'.format(rootdir))
    print('DIRECTORY TO SYNC: {}'.format(directory))
    print('\n')
    print('CHECKING IF DIRECTORY {} IN: {}'.format(
        directory.split('/')[-1], rootdir))
    if directory.split('/')[-1] in os.listdir(rootdir):
        print('DIRECTORY {} FOUND'.format(directory))
        print('\n')
        print('FILES/DIRS IN DIRECTORY {}:'.format(directory))
        du(path='./{}'.format(directory), absp=False, hidden=True)
        # for file in os.listdir(directory):
        #     print('- {} {} [{}]'.format(type_file_dict[os.path.isfile(os.path.join(current_dir, file))],
        #                            file, print_filesys_info(os.stat(os.path.join(current_dir, file))[6])))
        file_list = os.listdir(directory)
        print('\n')
        for file in file_list:
            if os.path.isfile(os.path.join(current_dir, file)):
                file_list_abs_path.append(os.path.join(current_dir, file))
            elif os.path.isdir(os.path.join(current_dir, file)):
                dir_list_abs_path.append(os.path.join(current_dir, file))
    print('LIST OF FILES TO UPLOAD:')
    for file in file_list_abs_path:
        print('- {}'.format(file.split('/')[-1]))
    print('\n')
    print('LIST OF SUBDIRS TO CREATE:')
    for subdir in dir_list_abs_path:
        print('- {}'.format(subdir.split('/')[-1]))
    print('\n')
    # Now create the root sync dir:
    if './' == rootdir:
        rootdir = ''
    dev_root_list = send_custom_sh_cmd("os.listdir('{}')".format(rootdir))
    if current_dir.split('/')[-1] not in dev_root_list:
        print('\n')
        print('MAKING DIR: {}'.format(current_dir))
        print('\n')
        dev_mkdir = ble_cp.sh_repl("os.mkdir('{}')".format(current_dir))
        print('\n')
    print('UPLOADING FILES TO {}'.format(current_dir))
    if len(file_list_abs_path) > 1:
        for file in file_list_abs_path:
            print('- {}'.format(file))
        ble_htool.start_SOC()
        for file in file_list_abs_path:
            ble_htool.put(file)
            time.sleep(0.2)
        # ble_cp.sh_repl("print('Done!')")
        time.sleep(0.2)
        ble_htool.stop_SOC()
    elif len(file_list_abs_path) == 1:
        file_to_put = file_list_abs_path[0]
        print('- {}'.format(file_to_put))
        ble_htool.start_SOC()
        ble_htool.put(file_to_put)
        # ble_cp.sh_repl("print('Done!')")
        time.sleep(0.2)
        ble_htool.stop_SOC()
    else:
        print('NO FILES IN DIR TO UPLOAD')
    # Now create subdirs:
    print('\n')
    print('MAKING SUBDIRS NOW...')
    for dir_ in dir_list_abs_path:
        print('\n')
        current_dir = dir_
        # Now create the root sync dir:
        try:
            dir_to_sync = directory
            dev_directory = send_custom_sh_cmd("os.listdir('{}')".format(dir_to_sync))
        except Exception as e:
            pass
        if not current_dir.split('/')[-1] in dev_directory:
            print('Creating dir: {}'.format(current_dir))
            dev_mkdir = ble_cp.sh_repl("os.mkdir('{}')".format(current_dir))
        else:
            print('DIRECTORY {} ALREADY EXISTS'.format(current_dir.split('/')[-1]))
    if len(dir_list_abs_path) == 0:
        print('NO MORE SUBDIRS')
    root = directory
    for dir_ in dir_list_abs_path:
        d_sync_recursive(dir_, dev, root)

    if directory == root_sync_folder:
        print('Done in : {:.2f} seconds'.format(time.time()-t0))


def send_custom_sh_cmd(cmd, sh_silent=True, **kargs):
    ble_cp.sh_repl(cmd, silent=True, **kargs)
    if not sh_silent:
        print(ble_cp.dev.output)
    return ble_cp.dev.output


def print_filesys_info(filesize):
    _kB = 1024
    if filesize < _kB:
        sizestr = str(filesize) + " by"
    elif filesize < _kB**2:
        sizestr = "%0.1f KB" % (filesize / _kB)
    elif filesize < _kB**3:
        sizestr = "%0.1f MB" % (filesize / _kB**2)
    else:
        sizestr = "%0.1f GB" % (filesize / _kB**3)
    return sizestr


def get_rprompt():
    if status_encryp_msg['S']:
        if mem_show_rp['call']:
            # RAM = send_custom_sh_cmd(
            #     'from micropython import mem_info;mem_info()', rtn=True,
            #     long_string=True)
            #
            # mem_info = RAM.replace('GC', '\nGC').replace('No', '\nNo').splitlines()[1]
            # mem = {elem.strip().split(':')[0]: int(elem.strip().split(':')[
            #                   1]) for elem in mem_info[4:].split(',')}
            # used_mem = mem['used']/1024
            # free_mem = mem['free']/1024
            # used_mem_s = "{:.3f} KB".format(used_mem)
            # free_mem_s = "{:.3f} KB".format(free_mem)
            # # set used and free
            # mem_show_rp['used'] = used_mem_s
            # mem_show_rp['free'] = free_mem_s
            # total_mem = mem['total']/1024
            # use_percent = round((used_mem/total_mem)*100, 2)
            # mem_show_rp['percent'] = use_percent
            mem_show_rp['call'] = False
        else:
            pass

        if mem_show_rp['percent'] < 85:
            return HTML('<aaa fg="ansiblack" bg="ansiwhite"> RAM </aaa><b><style fg="ansigreen"> USED </style></b><aaa fg="ansiblack" bg="ansiwhite"> {} </aaa><b><style fg="ansigreen"> FREE </style></b><aaa fg="ansiblack" bg="ansiwhite"> {} </aaa>'.format(mem_show_rp['used'], mem_show_rp['free']))
        else:
            return HTML('<aaa fg="ansiblack" bg="ansiwhite"> RAM </aaa><b><style fg="ansired"> USED </style></b><aaa fg="ansiblack" bg="ansiwhite"> {} </aaa><b><style fg="ansired"> FREE </style></b><aaa fg="ansiblack" bg="ansiwhite"> {} </aaa>'.format(mem_show_rp['used'], mem_show_rp['free']))
    else:
        return HTML('<b><style fg="ansiblack"> {} </style></b>'.format(' '*10))


def custom_sh_cmd(cmd_inp):
    # map cmd_inp to command string
    if cmd_inp == 'df':
        resp = send_custom_sh_cmd("os.statvfs('')")
        # resp_sd = send_custom_sh_cmd("os.statvfs('sd')")
        size_info = resp
        total_b = size_info[0] * size_info[2]
        used_b = (size_info[0] * size_info[2]) - (size_info[0] * size_info[3])
        total_mem = print_filesys_info(total_b)
        free_mem = print_filesys_info(size_info[0] * size_info[3])
        used_mem = print_filesys_info(used_b)

        print("{0:12}{1:^12}{2:^12}{3:^12}{4:^12}{5:^12}".format(*['Filesystem',
                                                                   'Size', 'Used',
                                                                   'Avail',
                                                                   'Use%', 'Mounted on']))
        try:
            print('{0:12}{1:^12}{2:^12}{3:^12}{4:>8}{5:>5}{6:12}'.format('Flash', total_mem,
                                                                         used_mem, free_mem,
                                                                         "{:.1f} %".format((used_b/total_b)*100), ' ', '/'))
        except Exception as e:
            pass
        vfs_resp = send_custom_sh_cmd(
            "{dir:os.statvfs(dir) for dir in os.listdir() if os.stat(dir)[0] & 0x4000 and os.statvfs('') != os.statvfs(dir)}")
        for vfs in vfs_resp.keys():
            if vfs_resp[vfs] != resp:
                size_info_sd = vfs_resp[vfs]
                total_b_sd = size_info_sd[0] * size_info_sd[2]
                used_b_sd = (size_info_sd[0] * size_info_sd[2]) - \
                    (size_info_sd[0] * size_info_sd[3])
                total_mem_sd = print_filesys_info(total_b_sd)
                free_mem_sd = print_filesys_info(size_info_sd[0] * size_info_sd[3])
                used_mem_sd = print_filesys_info(used_b_sd)
                print('{0:12}{1:^12}{2:^12}{3:^12}{4:>8}{5:>5}{6:12}'.format(vfs, total_mem_sd,
                                                                             used_mem_sd, free_mem_sd,
                                                                             "{:.1f} %".format((used_b_sd/total_b_sd)*100), ' ', '/{}'.format(vfs)))

    if cmd_inp.split()[0] == 'ldu':
        if len(cmd_inp.split()) == 1:
            du()

        elif len(cmd_inp.split()) == 2:
            if cmd_inp.split()[1] != '-o':
                file = cmd_inp.split()[1]
                du(path='./{}'.format(file))

            elif cmd_inp.split()[1] == '-o':
                resp = {dir: os.stat(dir)[6] for dir in os.listdir()}
                is_dir = {}
                for dir in resp.keys():
                    if resp[dir] != 0:
                        pass
                        is_dir[dir] = 0
                        # print('{:9} ./{}'.format(print_filesys_info(resp[dir]), dir))
                    else:
                        # print('{:9} ./{}'.format('<dir>', dir))
                        resp_dir = sum([os.stat(dir+sdir)[6]
                                        for sdir in os.listdir(dir)])
                        # print('{:9} ./{} <dir>'.format(print_filesys_info(resp_dir), dir))
                        resp[dir] = resp_dir
                        is_dir[dir] = 1

                du_sorted_list = [(dir, resp[dir], is_dir[dir]) for dir in resp.keys()]
                du_sorted_list.sort(key=sortSecond, reverse=True)

                for val in du_sorted_list:
                    if val[2] == 0:
                        print('{:9} ./{}'.format(print_filesys_info(val[1]), val[0]))
                    else:
                        print(
                            '{:9} ./{} <dir>'.format(print_filesys_info(val[1]), val[0]))
        elif len(cmd_inp.split()) == 3:
            if cmd_inp.split()[1] == '-d':
                try:
                    dlevmx = int(cmd_inp.split()[2])
                    du(max_dlev=dlevmx)

                except Exception as e:
                    print(e)

        elif len(cmd_inp.split()) == 4:
            if cmd_inp.split()[2] == '-d':
                try:
                    file = cmd_inp.split()[1]
                    dlevmx = int(cmd_inp.split()[3])
                    du(path='./{}'.format(file), max_dlev=dlevmx)

                except Exception as e:
                    print(e)

    if cmd_inp.split()[0] == 'du':
        if len(cmd_inp.split()) == 1:
            ble_cp.sh_repl('from upysh2 import du;du();gc.collect()', follow=True)
            ble_cp.flush_conn()

        elif len(cmd_inp.split()) == 2:
            if cmd_inp.split()[1] != '-o':
                file = cmd_inp.split()[1]
                ble_cp.sh_repl(
                    "from upysh2 import du;du(path='./{}');gc.collect()".format(file), follow=True)
                ble_cp.flush_conn()

            elif cmd_inp.split()[1] == '-o':
                resp = send_custom_sh_cmd(
                    "{dir:os.stat(dir)[6] for dir in os.listdir()}")
                is_dir = {}
                for dir in resp.keys():
                    if resp[dir] != 0:
                        pass
                        is_dir[dir] = 0
                        # print('{:9} ./{}'.format(print_filesys_info(resp[dir]), dir))
                    else:
                        # print('{:9} ./{}'.format('<dir>', dir))
                        resp_dir = send_custom_sh_cmd(
                            "sum([os.stat('{0}/'+dir)[6] for dir in os.listdir('{0}')])".format(dir))
                        # print('{:9} ./{} <dir>'.format(print_filesys_info(resp_dir), dir))
                        resp[dir] = resp_dir
                        is_dir[dir] = 1

                du_sorted_list = [(dir, resp[dir], is_dir[dir]) for dir in resp.keys()]
                du_sorted_list.sort(key=sortSecond, reverse=True)

                for val in du_sorted_list:
                    if val[2] == 0:
                        print('{:9} ./{}'.format(print_filesys_info(val[1]), val[0]))
                    else:
                        print(
                            '{:9} ./{} <dir>'.format(print_filesys_info(val[1]), val[0]))
        elif len(cmd_inp.split()) == 3:
            if cmd_inp.split()[1] == '-d':
                try:
                    dlev = cmd_inp.split()[2]
                    ble_cp.sh_repl(
                        "from upysh2 import du;du(max_dlev={});gc.collect()".format(dlev), follow=True)
                    ble_cp.flush_conn()

                except Exception as e:
                    print(e)

        elif len(cmd_inp.split()) == 4:
            if cmd_inp.split()[2] == '-d':
                try:
                    file = cmd_inp.split()[1]
                    dlev = cmd_inp.split()[3]
                    ble_cp.sh_repl(
                        "from upysh2 import du;du(path='./{}',max_dlev={});gc.collect()".format(file, dlev), follow=True)
                    ble_cp.flush_conn()

                except Exception as e:
                    print(e)

    if cmd_inp == 'datetime':
        send_custom_sh_cmd("import time;tnow=time.localtime()")
        resp = send_custom_sh_cmd('tnow[:6]')
        print("{}-{}-{}T{}:{}:{}".format(*_ft_datetime(resp)))
#
    if cmd_inp == 'ifconfig':
        sta_isconnected = send_custom_sh_cmd(
            "import network;network.WLAN(network.STA_IF).isconnected()")
        if sta_isconnected:
            ifconf = send_custom_sh_cmd(
                "network.WLAN(network.STA_IF).ifconfig()")
            print(ifconf)

        else:
            print('STA interface not connected')

    if cmd_inp == 'ifconfig_t':
        sta_isconnected = send_custom_sh_cmd(
            "import network;network.WLAN(network.STA_IF).isconnected()")
        if sta_isconnected:
            ifconf = send_custom_sh_cmd(
                "network.WLAN(network.STA_IF).ifconfig()")
            essid = send_custom_sh_cmd(
                "network.WLAN(network.STA_IF).config('essid')")
            signal_rssi = send_custom_sh_cmd(
                "network.WLAN(network.STA_IF).status('rssi')")
            print('=' * 106)
            print('{0:^15} | {1:^15} | {2:^15} | {3:^15} | {4:^15} | {5:^15} '.format(
                'IP', 'SUBNET', 'GATEAWAY', 'DNS', 'SSID', 'RSSI'))
            print('{0:^15} | {1:^15} | {2:^15} | {3:^15} | {4:^15} | {5:^15} '.format(*ifconf,
                                                                                      essid,
                                                                                      signal_rssi))

        else:
            print('STA interface not connected')

    if cmd_inp == 'netscan':
        enable_sta = send_custom_sh_cmd(
            "import network;network.WLAN(network.STA_IF).active(1)")
        if enable_sta:
            scan = send_custom_sh_cmd("network.WLAN(network.STA_IF).scan()")
            print('=' * 110)
            print('{0:^20} | {1:^25} | {2:^10} | {3:^15} | {4:^15} | {5:^10} '.format(
                'SSID', 'BSSID', 'CHANNEL', 'RSSI', 'AUTHMODE', 'HIDDEN'))
            if 'event' in ble_cp.dev.response:
                # print(espdev.response)
                try:
                    scan = ast.literal_eval(ble_cp.dev.response.splitlines()[-1])
                except Exception as e:
                    scan = []
                # print(scan)
            for netscan in scan:
                auth = AUTHMODE_DICT[netscan[4]]
                ap_name = netscan[0].decode()
                if len(ap_name) > 20:
                    ap_name = ap_name[:17] + '...'
                vals = hexlify(netscan[1]).decode()
                bssid = ':'.join([vals[i:i+2] for i in range(0, len(vals), 2)])
                print('{0:^20} | {1:^25} | {2:^10} | {3:^15} | {4:^15} | {5:^10} '.format(
                    ap_name, bssid, netscan[2], netscan[3],
                    auth, str(netscan[5])))

        else:
            print("Can't enable STA interface")

    if cmd_inp == 'apconfig':
        ap_isconnected = send_custom_sh_cmd(
            "import network;network.WLAN(network.AP_IF).active()")
        if ap_isconnected:
            apconf = send_custom_sh_cmd(
                "network.WLAN(network.AP_IF).ifconfig()")
            print(apconf)

        else:
            print('AP interface not connected')

    if cmd_inp == 'apconfig_t':
        ap_isconnected = send_custom_sh_cmd(
            "import network;network.WLAN(network.AP_IF).active()")
        if ap_isconnected:
            apconf = send_custom_sh_cmd(
                "network.WLAN(network.AP_IF).ifconfig()")
            essid = send_custom_sh_cmd(
                "network.WLAN(network.AP_IF).config('essid')")
            channel = send_custom_sh_cmd(
                "network.WLAN(network.AP_IF).config('channel')")
            auth_mode = send_custom_sh_cmd(
                "network.WLAN(network.AP_IF).config('authmode')")
            mac_addrs = ':'.join([unique_id[i:i+2]
                                  for i in range(0, len(unique_id), 2)])
            print('=' * 70)
            print('{0:^15} | {1:^18} | {2:^15} | {3:^15} '.format(
                'SSID', 'BSSID', 'CHANNEL', 'AUTHMODE'))
            print('{0:^15} | {1:^18} | {2:^15} | {3:^15}'.format(essid,
                                                                 mac_addrs,
                                                                 channel,
                                                                 AUTHMODE_DICT[auth_mode]))
            print('=' * 70)
            print('{0:^15} | {1:^15} | {2:^15} | {3:^15} '.format(
                'IP', 'SUBNET', 'GATEAWAY', 'DNS'))
            print('{0:^15} | {1:^15} | {2:^15} | {3:^15}'.format(*apconf))

        else:
            print('AP interface not connected')

    if cmd_inp == 'apscan':
        enable_sta = send_custom_sh_cmd(
            "import network;network.WLAN(network.AP_IF).active(1)")
        if enable_sta:
            scan = send_custom_sh_cmd(
                "network.WLAN(network.AP_IF).status('stations')")
            print('Found {} device/s'.format(len(scan)))
            for dev in scan:
                bytdev = hexlify(dev[0]).decode()
                mac_ad = ':'.join([bytdev[i:i+2] for i in range(0, len(bytdev),
                                                                2)])
                print('MAC: {}'.format(mac_ad))

        else:
            print("Can't enable AP interface")

    if cmd_inp == 'meminfo':
        RAM = send_custom_sh_cmd(
            'from micropython import mem_info;mem_info()')

        mem_info = RAM.replace('\n', '').replace(
            'GC', '\nGC').replace('No', '\nNo').splitlines()[1]
        mem = {elem.strip().split(':')[0]: int(elem.strip().split(':')[
                          1]) for elem in mem_info[4:].split(',')}
        print("{0:12}{1:^12}{2:^12}{3:^12}{4:^12}".format(*['Memory',
                                                            'Size', 'Used',
                                                            'Avail',
                                                            'Use%']))
        total_mem = mem['total']/1024
        used_mem = mem['used']/1024
        free_mem = mem['free']/1024
        total_mem_s = "{:.3f} KB".format(total_mem)
        used_mem_s = "{:.3f} KB".format(used_mem)
        free_mem_s = "{:.3f} KB".format(free_mem)

        print('{0:12}{1:^12}{2:^12}{3:^12}{4:>8}'.format('RAM', total_mem_s,
                                                         used_mem_s, free_mem_s,
                                                         "{:.1f} %".format((used_mem/total_mem)*100)))

    # if cmd_inp == 'dump_mem':
    #     RAM_dump = send_custom_sh_cmd(
    #         'from micropython import mem_info;mem_info(1)')
    #     print(RAM_dump)

#     if cmd_inp.split(' ')[0] == 'install':
#         if dev_platform != 'pyboard':
#             try:
#                 lib = cmd_inp.split(' ')[1]
#                 install_resp = send_custom_sh_cmd(
#                     "import upip;upip.install('{}')".format(lib), sh_silent=False)
#             except Exception as e:
#                 print('Please indicate a library to install')
#         elif dev_platform == 'pyboard':
#             try:
#                 dir_lib = 'lib'
#                 lib = cmd_inp.split(' ')[1]
#                 pckg_content, pckg_dir = upip_host.install_pkg(lib, ".")
#                 # sync local lib to device lib
#                 print('Installing {} to {} :/lib'.format(pckg_dir, dev_platform))
#                 cwd_now = send_custom_sh_cmd('os.getcwd()')
#                 root = send_custom_sh_cmd("os.chdir('/flash')")
#                 d_sync_recursive(dir_lib, show_tree=True, root_sync_folder=".")
#                 rm_lib = input('Do you want to remove local lib? (y/n): ')
#                 if rm_lib == 'y':
#                     shutil.rmtree(dir_lib)
#                 print("Successfully installed {} to {} :/lib".format(pckg_dir, dev_platform))
#                 send_custom_sh_cmd("os.chdir('{}')".format(cwd_now))
#             except Exception as e:
#                 print('Please indicate a library to install')
#
#     if cmd_inp.split(' ')[0] == 'pkg_info':
#         try:
#             lib = cmd_inp.split(' ')[1]
#             upip_host.install_pkg(lib, ".", read_pkg_info=True)
#
#         except Exception as e:
#             print('Please indicate a library to see PKG-INFO')
#
#     if cmd_inp.split(' ')[0] == 'upipl':
#         try:
#             if len(cmd_inp.split()) == 1:
#                 print('Available MicroPython libs (just a random subset):')
#                 list_upylibs = shlex.split('pip search micropython')
#                 result = subprocess.call(list_upylibs)
#             elif len(cmd_inp.split()) == 2:
#                 lib = cmd_inp.split()[1]
#                 print("Available MicroPython libs that match {}:".format(lib))
#                 list_upylibs = shlex.split("pip search {}".format(lib))
#                 result = subprocess.call(list_upylibs)
#         except Exception as e:
#             print(e)
#

    if cmd_inp.split(' ')[0] == 'touch':
        try:
            n_file = cmd_inp.split(' ')[1]
            send_custom_sh_cmd("new_file = open('{}','w')".format(n_file))
            send_custom_sh_cmd("new_file.close()")
        except Exception as e:
            print('Please write the name of the new file')

    if cmd_inp.split(' ')[0] == 'edit':
        try:
            e_file = cmd_inp.split(' ')[1]
            print('Press CTRL-E to edit {}'.format(e_file))
            edit_mode['E'] = True
            edit_mode['File'] = e_file
            prompt['p'] = ""
        except Exception as e:
            print('Please write the name of the new file')

    if cmd_inp.split(' ')[0] == 'rssi':
        try:

            print(f'RSSI: {ble_cp.dev.get_RSSI()} dBm')
        except Exception as e:
            print(e)
            pass
#
#     if cmd_inp.split(' ')[0] == 'srepl':
#         print('<-- Device {} MicroPython -->'.format(dev_platform))
#         print('Use CTRL-a,CTRL-x to exit')
#         try:
#             ble_cp.enable_wrepl_io()
#         except Exception as e:
#             pass
#         serial_repl_cmd_str = 'picocom {} -b115200'.format(args.t)
#         serial_repl_cmd = shlex.split(serial_repl_cmd_str)
#         try:
#             serial_repl = subprocess.call(serial_repl_cmd)
#             try:
#                 ble_cp.enable_wrepl_io()
#             except Exception as e:
#                 pass
#         except KeyboardInterrupt:
#             try:
#                 ble_cp.enable_wrepl_io()
#             except Exception as e:
#                 pass
#             pass
#             print('')
#
    if cmd_inp == 'whoami':
        print('DEVICE: {}, UUID: {} , ID: {}'.format(ble_cp.dev.name,
                                                     ble_cp.dev.UUID,
                                                     unique_id))
        sysinfo = send_custom_sh_cmd('os.uname()')
        dev_info = sysinfo.split("'")
        print('SYSTEM NAME: {}'.format(dev_info[1]))
        print('NODE NAME: {}'.format(dev_info[3]))
        print('RELEASE: {}'.format(dev_info[5]))
        print('VERSION: {}'.format(dev_info[7]))
        print('MACHINE: {}'.format(dev_info[9]))
#         # print(sysinfo)
#
    if cmd_inp == 'pwdl':
        print(os.getcwd())
#
    if cmd_inp.split(' ')[0] == 'cdl':
        if len(cmd_inp.split(' ')) == 1:
            os.chdir(os.environ['HOME'])
        else:
            os.chdir('{}'.format(cmd_inp.split(' ')[1]))
        if os.getcwd() == os.environ['HOME']:
            local_path['p'] = '~:/'
            if not show_local_path['s']:
                local_path['p'] = ''
        elif not show_local_path['s']:
            local_path['p'] = ''
        else:
            local_path['p'] = os.getcwd().split('/')[-1]+':/'

        # SET ROOT USER PATH:
        shell_message = [
            ('class:userpath',    local_path['p']),
            ('class:username', dev_platform),
            ('class:at',       '@'),
            ('class:host',     host_name),
            ('class:colon',    ':'),
            ('class:path',     '~{}'.format(dev_path['p'])),
            ('class:pound',    '$ '),
        ]
        shell_prompt['s'] = shell_message
        prompt['p'] = shell_prompt['s']

    if cmd_inp.split()[0] == 'lsl':
        if len(cmd_inp.split()) == 1:
            list_of_files = os.listdir()
        elif len(cmd_inp.split()) > 1:
            if '*' in cmd_inp.split()[1]:  # re to match
                r_e = cmd_inp.split()[1].replace('*', '')
                list_of_files = [f for f in os.listdir() if r_e in f]
            else:
                list_of_files = os.listdir(cmd_inp.split()[1])
        if list_of_files != []:
            print_table(list_of_files, wide=28, format_SH=True)

    if cmd_inp.split(' ')[0] == 'get':
        try:
            print('File/s to get:')
            file = cmd_inp.split(' ')[1]
            if file == 'cwd':
                ble_cp.sh_repl(
                    "[file for file in os.listdir() if not os.stat(file)[0] & 0x4000]")
                ble_cp.dev.get_output()
                files_to_put = ble_cp.dev.output
                for file in files_to_put:
                    print('- {}'.format(file))
                ble_htool.start_SOC()
                for file in files_to_put:
                    ble_htool.get(file)
                    time.sleep(0.2)
                # ble_cp.sh_repl("print('Done!')")
                time.sleep(0.2)
                ble_htool.stop_SOC()
            elif '*' in file:
                ble_cp.sh_repl("[file for file in os.listdir() if '{}' in file and not os.stat(file)[0] & 0x4000]".format(
                    file.replace('*', '')))
                ble_cp.dev.get_output()
                files_to_put_re = ble_cp.dev.output
                if files_to_put_re is None or files_to_put_re == []:
                    print('No files found that match "{}"'.format(file))
                else:
                    for file in files_to_put_re:
                        print('- {}'.format(file))
                    ble_htool.start_SOC()
                    for file in files_to_put_re:
                        ble_htool.get(file)
                        time.sleep(0.2)
                    # ble_cp.sh_repl("print('Done!')")
                    time.sleep(0.2)
                    ble_htool.stop_SOC()
            elif len(cmd_inp.split(' ')) > 2:
                files_to_put = cmd_inp.split(' ')[1:]
                # files_to_put_dir = [fl for fl in os.listdir() if os.path.isfile(fl) and fl in files_to_put]
                ble_cp.sh_repl(
                    "[file for file in os.listdir() if file in {} and not os.stat(file)[0] & 0x4000]".format(files_to_put))
                ble_cp.dev.get_output()
                files_to_put_dir = ble_cp.dev.output
                if len(files_to_put_dir) != len(files_to_put):
                    print('One or more files not found in cwd')
                else:
                    for file in files_to_put_dir:
                        print('- {}'.format(file))
                    ble_htool.start_SOC()
                    for file in files_to_put_dir:
                        ble_htool.get(file)
                        time.sleep(0.2)
                    # ble_cp.sh_repl("print('Done!')")
                    time.sleep(0.2)
                    ble_htool.stop_SOC()
            else:
                ble_cp.sh_repl(
                    "[file for file in os.listdir() if file in {} and not os.stat(file)[0] & 0x4000]".format([file]))
                ble_cp.dev.get_output()
                files_to_put_dir = ble_cp.dev.output
                if len(files_to_put_dir) == 0:
                    print('File not Found\nIndicate a file to get')
                else:
                    print('- {}'.format(file))
                    # ble_htool.start_SOC()
                    ble_htool.get(file, chunk_size=ble_cp.dev.len_buffer)
                    # ble_cp.sh_repl("print('Done!')")
                    time.sleep(0.2)
                    ble_htool.stop_SOC()
        except Exception as e:
            print(e)
            print('File not Found\nIndicate a file to get')

    if cmd_inp.split(' ')[0] == 'put':
        try:
            print('File/s to put:')
            file = cmd_inp.split(' ')[1]
            if file == 'cwd':
                files_to_put = [fl for fl in os.listdir() if os.path.isfile(fl)]
                for file in files_to_put:
                    print('- {}'.format(file))
                ble_htool.start_SOC()
                for file in files_to_put:
                    ble_htool.put(file)
                    time.sleep(0.2)
                # ble_cp.sh_repl("print('Done!')")
                time.sleep(0.2)
                ble_htool.stop_SOC()
            elif '*' in file:
                files_to_put = [fl for fl in os.listdir() if os.path.isfile(fl)]
                files_to_put_re = [
                    fl for fl in files_to_put if file.replace('*', '') in fl]
                for file in files_to_put_re:
                    print('- {}'.format(file))
                ble_htool.start_SOC()
                for file in files_to_put_re:
                    ble_htool.put(file)
                    time.sleep(0.2)
                # ble_cp.sh_repl("print('Done!')")
                time.sleep(0.2)
                ble_htool.stop_SOC()
            elif len(cmd_inp.split(' ')) > 2:
                files_to_put = cmd_inp.split(' ')[1:]
                files_to_put_dir = [fl for fl in os.listdir(
                ) if os.path.isfile(fl) and fl in files_to_put]
                if len(files_to_put_dir) != len(files_to_put):
                    print('One or more files not found in cwd')
                else:
                    for file in files_to_put_dir:
                        print('- {}'.format(file))
                    ble_htool.start_SOC()
                    for file in files_to_put_dir:
                        ble_htool.put(file)
                        time.sleep(0.2)
                    # ble_cp.sh_repl("print('Done!')")
                    time.sleep(0.2)
                    ble_htool.stop_SOC()
            else:
                files_to_put_dir = [fl for fl in os.listdir(
                ) if os.path.isfile(fl) and fl in [file]]
                if len(files_to_put_dir) == 0:
                    print('File not Found\nIndicate a valid file to put')
                else:
                    print('- {}'.format(file))
                    # ble_htool.start_SOC()
                    ble_htool.put(file, chunk_size=ble_cp.dev.len_buffer)
                    # ble_cp.sh_repl("print('Done!')")
                    time.sleep(0.2)
                    ble_htool.stop_SOC()
        except Exception as e:
            # print(e)
            print('File not Found\nIndicate a valid file to put')
#
    if cmd_inp.split(' ')[0] == 'catl':
        try:
            file = cmd_inp.split(' ')[1]
            try:
                with open(file, 'r') as catfile:
                    content = catfile.read()
                    print(content)
            except Exception as e:
                print(e)
                pass

        except Exception as e:
            print('Indicate a file to show')

    if cmd_inp.split(' ')[0] == 'dsync':
        if len(cmd_inp.split()) == 1:  # root_dir
            dir_to_sync = os.getcwd().split('/')[-1]
            # print('Directory to sync: {}'.format(dir_to_sync))
            os.chdir('..')
            d_sync_recursive(dir_to_sync, show_tree=True, root_sync_folder=dir_to_sync)
            os.chdir(dir_to_sync)
        elif len(cmd_inp.split()) == 2:
            dir_to_sync = cmd_inp.split()[-1]
            # print('Directory to sync: {}'.format(dir_to_sync))
            d_sync_recursive(dir_to_sync, show_tree=True, root_sync_folder=dir_to_sync)

    if cmd_inp.split(' ')[0] == 'l_micropython':
        print('<-- Local machine MicroPython -->')
        try:
            lmpy = 'micropython'
            lmpy_cmd = shlex.split(lmpy)
            old_action = signal.signal(signal.SIGINT, signal.SIG_IGN)

            def preexec_function(action=old_action):
                signal.signal(signal.SIGINT, action)
            try:
                l_mpy = subprocess.call(lmpy_cmd, preexec_fn=preexec_function)
                signal.signal(signal.SIGINT, old_action)
            except Exception as e:
                print(e)
                print('Local MicroPython not found')
        except Exception as e:
            print(e)
            pass
#
#     if cmd_inp.split(' ')[0] == 'jupyterc':
#
#         print('Use CTRL-D to exit, Use %lsmagic to see custom magic commands')
#         jupyter_cmd_str = 'jupyter console --kernel=micropython-upydevice'
#
#         jupyter_cmd = shlex.split(jupyter_cmd_str)
#         old_action = signal.signal(signal.SIGINT, signal.SIG_IGN)
#
#         def preexec_function(action=old_action):
#             signal.signal(signal.SIGINT, action)
#
#         try:
#             jupyter_console = subprocess.call(jupyter_cmd, preexec_fn=preexec_function)
#             signal.signal(signal.SIGINT, old_action)
#
#         except KeyboardInterrupt:
#             pass
#             print('')
#
    if cmd_inp.split()[0] == 'ls':
        if len(cmd_inp.split()) == 1:
            dev_files = send_custom_sh_cmd('os.listdir();gc.collect()')
        elif len(cmd_inp.split()) > 1:
            if '*' in cmd_inp.split()[1]:  # re to match
                r_e = cmd_inp.split()[1].replace('*', '')
                dev_files = send_custom_sh_cmd(
                    "[f for f in os.listdir() if '{}' in f];gc.collect()".format(r_e))
            else:
                dev_files = send_custom_sh_cmd(
                    "os.listdir('{}');gc.collect()".format(cmd_inp.split()[1]))
        try:
            print_table(dev_files, wide=28, format_SH=True)
        except Exception as e:
            pass

    if cmd_inp.split(' ')[0].startswith('python'):
        print('<-- Local machine Python -->')
        print('Use Ctrl-D to exit')
        try:
            lpy = cmd_inp.split(' ')[0]
            lpy_cmd = shlex.split(lpy)
            old_action = signal.signal(signal.SIGINT, signal.SIG_IGN)

            def preexec_function(action=old_action):
                signal.signal(signal.SIGINT, action)
            try:
                l_py = subprocess.call(lpy_cmd, preexec_fn=preexec_function)
                signal.signal(signal.SIGINT, old_action)
            except Exception as e:
                print(e)
                print('Local Python not found')
        except Exception as e:
            print(e)
            pass

    if cmd_inp.split(' ')[0] == 'vim':
        print('<-- Vim editor -->')
        print('Use I to insert; (ESC , :w) to write; (ESC ,:q) to exit')
        time.sleep(1)
        try:
            file_to_edit = cmd_inp.split(' ')[1]
            lvim = 'vim {}'.format(file_to_edit)
            lvim_cmd = shlex.split(lvim)
            try:
                l_vim = subprocess.call(lvim_cmd)
            except Exception as e:
                print(e)
                print('Vim editor not found')
        except Exception as e:
            print('Indicate a file to edit')
            pass

    if 'emacs' in cmd_inp.split(' ')[0]:
        print('<-- Emacs editor -->')
        print('Use Ctrl-x-Ctrl-s to Save,  Ctrl-x-Ctrl-c to exit')
        time.sleep(1)
        try:
            file_to_edit = cmd_inp.split(' ')[1]
            lemacs = 'emacs {} -nw'.format(file_to_edit)
            lemacs_cmd = shlex.split(lemacs)
            old_action = signal.signal(signal.SIGINT, signal.SIG_IGN)

            def preexec_function(action=old_action):
                signal.signal(signal.SIGINT, action)
            try:
                l_emacs = subprocess.call(lemacs_cmd, preexec_fn=preexec_function)
                signal.signal(signal.SIGINT, old_action)
            except Exception as e:
                print(e)
                print('Local Emacs not found')
        except Exception as e:
            print(e)
            pass

    if cmd_inp == 'set_localtime':
        print('Setting local time: {}'.format(
            datetime.now().strftime("%Y-%m-%d T %H:%M:%S")))
        rtc_cmd = "from machine import RTC;rtc = RTC();"
        wkday = date.weekday(date.today())
        datetime_local = [val for val in datetime.now().timetuple()[:-3]]
        datetime_tuple = datetime_local[:3]
        datetime_tuple.append(wkday)
        datetime_final = datetime_tuple + datetime_local[3:] + [0]
        utc_zone_cmd = "rtc.datetime(({}, {}, {}, {}, {}, {}, {}+3, {}))".format(
            *datetime_final)
        settime_cmd = rtc_cmd + utc_zone_cmd
        send_custom_sh_cmd(settime_cmd)
#
    if cmd_inp.split(' ')[0] == 'tree':
        if len(cmd_inp.split(' ')) > 1:
            if cmd_inp.split(' ')[1] == '-a':
                ble_cp.sh_repl(
                    'from upysh2 import tree;tree(hidden=True);gc.collect()', follow=True)

            else:
                if len(cmd_inp.split(' ')) == 2:
                    dir = cmd_inp.split(' ')[1]
                    ble_cp.sh_repl(
                        "from upysh2 import tree;tree(path='{}');gc.collect()".format(dir), follow=True)
                    ble_cp.flush_conn()

                else:
                    if cmd_inp.split(' ')[2] == '-a':
                        dir = cmd_inp.split(' ')[1]
                        ble_cp.sh_repl("from upysh2 import tree;tree(path='{}', hidden=True);gc.collect()".format(
                            dir), follow=True)
                        ble_cp.flush_conn()

        else:
            # send_custom_sh_cmd('from upysh2 import tree;tree()', sh_silent=False)
            ble_cp.sh_repl('from upysh2 import tree;tree();gc.collect()', follow=True)
        ble_cp.flush_conn()
#
    if cmd_inp == 'l_ifconfig':

        ifconfig_local = [netifaces.ifaddresses(iface)[netifaces.AF_INET][0]
                          for iface in netifaces.interfaces() if
                          netifaces.AF_INET in netifaces.ifaddresses(iface)][-1]
        gateaway = list(netifaces.gateways()['default'].values())[0][0]
        print("('{}', '{}', '{}', '{}')".format(ifconfig_local['addr'],
                                                ifconfig_local['netmask'],
                                                gateaway,
                                                ifconfig_local['broadcast']))

    if cmd_inp == 'l_ifconfig_t':
        if _platform == "linux" or _platform == "linux2":
            # linux
            return ''
        elif _platform == "darwin":
            # MAC OS X
            scanoutput = subprocess.check_output(["airport", "-I"])
            wifi_info = [data.strip()
                         for data in scanoutput.decode('utf-8').split('\n')]
            wifi_info_dic = {data.split(':')[0]: data.split(
                ':')[1].strip() for data in wifi_info[:-1]}
        ssid = wifi_info_dic['SSID']
        rssi = wifi_info_dic['agrCtlRSSI']
        ifconfig_local = [netifaces.ifaddresses(iface)[netifaces.AF_INET][0]
                          for iface in netifaces.interfaces() if
                          netifaces.AF_INET in netifaces.ifaddresses(iface)][-1]
        gateaway = list(netifaces.gateways()['default'].values())[0][0]
        print('=' * 106)
        print('{0:^15} | {1:^15} | {2:^15} | {3:^15} | {4:^15} | {5:^15} '.format(
            'IP', 'SUBNET', 'GATEAWAY', 'DNS', 'SSID', 'RSSI'))
        print('{0:^15} | {1:^15} | {2:^15} | {3:^15} | {4:^15} | {5:^15} '.format(ifconfig_local['addr'],
                                                                                  ifconfig_local['netmask'],
                                                                                  gateaway,
                                                                                  ifconfig_local['broadcast'],
                                                                                  ssid,
                                                                                  rssi))
    if cmd_inp == 'get_services':
        ble_cp.dev.get_services()

    if cmd_inp.split(' ')[0] == 'reload':
        try:
            module = cmd_inp.split(' ')[1].split('.py')[0]
        except Exception as e:
            print('Indicate a module to reload')
        ble_cp.dev.output = None
        reload_cmd = "import sys;del(sys.modules['{}']);gc.collect()".format(module)
        ble_cp.sh_repl(reload_cmd)
        if ble_cp.dev.output is not None:
            print(ble_cp.dev.output)
#
    if cmd_inp.split(' ')[0] == 'docs':
        if len(cmd_inp.split(' ')) > 1:
            key_word = cmd_inp.split(' ')[1]
            search = 'http://docs.micropython.org/en/latest/search.html?q={}&check_keywords=yes&area=default'.format(
                key_word)
            webbrowser.open(search)
        else:
            webbrowser.open('http://docs.micropython.org/en/latest/')

    if cmd_inp == 'flush_soc':
        ble_cp.flush_conn()
#
#     if cmd_inp == 'update_upyutils':
#         print('Updating device upyutils...')
#         # upylog.py
#         upylog = '{}/upylog.py'.format(upydev.__path__[0]+'/upyutils_dir')
#         # sync_tool.py
#         upynotify = '{}/upynotify.py'.format(upydev.__path__[0]+'/upyutils_dir')
#         # upysh2.py
#         upysh2 = '{}/upysh2.py'.format(upydev.__path__[0]+'/upyutils_dir')
#         # uping.py
#         uping = '{}/uping.py'.format(upydev.__path__[0]+'/upyutils_dir')
#         # time_it.py
#         time_it = '{}/time_it.py'.format(upydev.__path__[0]+'/upyutils_dir')
#         upyutils_scripts = [uping, upysh2, upynotify, upylog, time_it]
#         cwd_now = send_custom_sh_cmd("os.getcwd()")
#         if dev_platform == 'pyboard':
#             send_custom_sh_cmd("cd('/')")
#             send_custom_sh_cmd("cd('/flash')")
#         else:
#             send_custom_sh_cmd("cd('/')")
#         send_custom_sh_cmd("cd('lib')")
#         dir_lib = send_custom_sh_cmd("os.getcwd()")
#         print(dir_lib)
#         for file in upyutils_scripts:
#             print('- {}'.format(file))
#         ble_htool.start_SOC()
#         for file in upyutils_scripts:
#             ble_htool.put(file, abs_path=False)
#             time.sleep(0.2)
#         # ble_cp.sh_repl("print('Done!')")
#         time.sleep(0.2)
#         ble_htool.stop_SOC()
#         send_custom_sh_cmd("cd('..')")
#         send_custom_sh_cmd("cd('{}')".format(cwd_now))
#
    if cmd_inp == 'get_rawbuff':
        print(ble_cp.dev.raw_buff)

    if cmd_inp.split()[0] == 'l_tree':
        try:
            if len(cmd_inp.split(' ')) > 1:
                if cmd_inp.split(' ')[1] == '-a':
                    tree(hidden=True)
                else:
                    if len(cmd_inp.split(' ')) == 2:
                        dir = cmd_inp.split(' ')[1]
                        tree(path=dir)
                    else:
                        if cmd_inp.split(' ')[2] == '-a':
                            dir = cmd_inp.split(' ')[1]
                            tree(path=dir, hidden=True)
            else:
                tree()
        except Exception as e:
            print(e)
            pass
#
#     if cmd_inp.split(' ')[0] == 'view':
#         try:
#             img_file = cmd_inp.split(' ')[1]
#             if img_file.endswith('.pbm'):
#                 ble_cp.sh_repl("cat('{}')".format(img_file), dec=False)
#                 dimensions, data, raw_bin_img, img_1 = read_pbm(ble_cp.raw_buff)
#                 print("{} dimensions: {}x{}".format(img_file, *dimensions))
#                 print(img_1)
#             else:
#                 print('Indicate a ".pbm" file to view')
#         except Exception as e:
#             print(e)
#             print('Indicate a ".pbm" file to view')
#
    if cmd_inp.split(' ')[0] == 'batstyle':
        if len(cmd_inp.split()) == 1:
            print('Current Color Syntax Style: {}'.format(ble_cp.color_style))
        elif len(cmd_inp.split()) == 2:
            if cmd_inp.split()[1] == '-a':
                print('Styles availables:')
                print_table(PYGM_SYNTAX)
            else:
                style_to_set = cmd_inp.split()[1]
                if style_to_set in PYGM_SYNTAX:
                    ble_cp.color_style = style_to_set
                    print('Style {} configured'.format(style_to_set))
                else:
                    print('Style not available')

    if cmd_inp.split(' ')[0] == 'bat':
        try:
            color_file = cmd_inp.split(' ')[1]
            if color_file.endswith('.py'):
                ble_cp.sh_repl("cat('{}')".format(color_file), silent=True)
                ble_cp.do_color()
            else:
                print('Indicate a ".py" file to view')
        except Exception as e:
            print(e)
            print('Indicate a ".py" file to view')

    if cmd_inp.split(' ')[0] == 'batl':
        try:
            color_file = cmd_inp.split(' ')[1]
            if color_file.endswith('.py'):
                try:
                    with open(color_file, 'r') as catfile:
                        content = catfile.read()
                        if ble_cp.color_style == 'one_dark':
                            result = highlight(content, Python3Lexer(
                            ), Terminal256Formatter(style=one_darkStyle))
                            lines = result.split('\n')
                            lines_numbered = ["{}{:4}{} {}".format(
                                DARK_GRAY, i, CEND, lines[i-1]) for i in range(1, len(lines)+1)]
                            my_code_numb = '\n'.join(lines_numbered)
                            print(my_code_numb)

                        else:
                            pygm_style = get_style_by_name(ble_cp.color_style)
                            result = highlight(content, Python3Lexer(
                            ), Terminal256Formatter(style=pygm_style))
                            lines = result.split('\n')
                            lines_numbered = ["{}{:4}{} {}".format(
                                DARK_GRAY, i, CEND, lines[i-1]) for i in range(1, len(lines)+1)]
                            my_code_numb = '\n'.join(lines_numbered)
                            print(my_code_numb)
                except Exception as e:
                    print(e)
                    pass

        except Exception as e:
            print('Indicate a ".py" file to view')

    if cmd_inp.split()[0] == 'pytest':
        try:
            # setup conftest.py
            if 'setup' in cmd_inp.split():
                shutil.copy(os.path.join(upydev.__path__[0], 'conftest.py'), '.')
                shutil.copy(os.path.join(upydev.__path__[0], 'pytest.ini'), '.')
            else:
                ble_cp.dev.disconnect()
                pytest_cmd = shlex.split(cmd_inp)
                if '--dev' not in pytest_cmd and '-h' not in pytest_cmd:
                    pytest_cmd += ['--dev', host_name]
                old_action = signal.signal(signal.SIGINT, signal.SIG_IGN)

                def preexec_function(action=old_action):
                    signal.signal(signal.SIGINT, action)
                try:
                    pytest = subprocess.call(pytest_cmd, preexec_fn=preexec_function)
                    signal.signal(signal.SIGINT, old_action)
                except Exception as e:
                    print(e)
                ble_cp.dev.connect()
                time.sleep(0.2)
                ble_cp.sh_repl('import os;import gc;from upysh import *', silent=True)
                ble_cp.flush_conn()
        except Exception as e:
            print(e)
            pass
#
#     if cmd_inp.split(' ')[0] == 'rcat':
#         try:
#             raw_file = cmd_inp.split(' ')[1]
#             ble_cp.sh_repl("cat('{}')".format(raw_file), dec=False)
#             print(b''.join(ble_cp.raw_buff.splitlines()[1:-1]).replace(b'>>> ', b''))
#         except Exception as e:
#             print(e)
#
#     if cmd_inp.split()[0] == 'fw':
#         dev_p = dev_platform
#         if dev_p == 'pyboard':
#             dev_p = 'pyb'
#         kw_cmd = cmd_inp.split()
#         if len(kw_cmd) < 2:
#             print('Indicate an operation: list/get/update')
#         else:
#             if 'get' in kw_cmd and len(kw_cmd) < 3:
#                 print("Indicate a firmware file to get or 'latest'")
#             else:
#                 if 'update' in cmd_inp:
#                     cmd_inp = cmd_inp.replace('latest', '')
#                     kw_cmd = cmd_inp.replace('update', 'get latest').split()
#                     print('Updating device firmware...')
#                 md = ' '.join(kw_cmd[1:])
#                 fw_cmd = 'fw -md {} -b {}'.format(md, dev_p)
#                 upy_fw_cmd = 'upydev ' + fw_cmd
#                 # print(upy_fw_cmd)
#                 get_fw_cmd = shlex.split(upy_fw_cmd)
#                 try:
#                     fw_req = subprocess.call(get_fw_cmd)
#                     if 'update' in cmd_inp:
#                         kw_cmd = cmd_inp.replace('update', 'list latest').split()
#                         md = ' '.join(kw_cmd[1:])
#                         fw_cmd = 'fw -md {} -b {}'.format(md, dev_p)
#                         upy_fw_cmd = 'upydev ' + fw_cmd
#                         # print(upy_fw_cmd)
#                         get_fw_cmd = shlex.split(upy_fw_cmd)
#                         fw_req = subprocess.check_output(get_fw_cmd)
#                         # print(fw_req.decode())
#                         resp = fw_req.decode()
#                         if len(resp.splitlines()) == 1:
#                             pass
#                         else:
#                             fw_file = fw_req.decode().splitlines()[1][2:]
#
#                             try:
#                                 print('File to flash: {}'.format(fw_file))
#                                 resp = input('Do you wish to continue? (y/n): ')
#                                 if dev_platform != 'pyboard':
#                                     if resp == 'y':
#                                         bin_file = fw_file
#                                         ble_cp.enable_wrepl_io()
#                                         upy_fw_cmd = 'upydev flash -port {} -f {}'.format(args.t, bin_file)
#                                         # print(upy_fw_cmd)
#                                         flash_fw_cmd = shlex.split(upy_fw_cmd)
#
#                                         try:
#                                             fw_flash = subprocess.call(flash_fw_cmd)
#                                         except Exception as e:
#                                             # ble_cp.enable_wrepl_io()
#                                             print(e)
#                                         time.sleep(0.2)
#                                         ble_cp.enable_wrepl_io()
#                                         ble_cp.flush_conn()
#                                         ble_cp.flush_conn()
#                                         time.sleep(1)
#                                         ble_cp.sh_repl('import gc;import os;from upysh import *;gc.collect()', silent=True)
#                                         ble_cp.flush_conn()
#                                     else:
#                                         print('Operation cancelled')
#                                 elif dev_platform == 'pyboard':
#                                     if resp == 'y':
#                                         bin_file = fw_file
#                                         # ble_cp.enable_wrepl_io()
#                                         # print('Warning: Unmount the pyboard first')
#                                         # dfu_mode = input('Put the pyboard in DFU mode, need help? (y/n): ')
#                                         # if dfu_mode == 'y':
#                                         #     webbrowser.open('https://github.com/micropython/micropython/wiki/Pyboard-Firmware-Update')
#                                         # else:
#                                         #     pass
#                                         dfu_mode = input('Warning: Unmount the pyboard first if mounted (but DO NOT DISCONNECT), press any key to continue.')
#                                         print('Enabling DFU mode in pyboard, DO NOT DISCONNECT...')
#                                         ble_cp.send_message('import pyb;pyb.bootloader()\r')
#                                         ble_cp.enable_wrepl_io()
#                                         flash_tool = input('Select a tool to flash pydfu.py/dfu-util: (0/1) ')
#                                         if flash_tool == '0':
#                                             print('Using pydfu.py...')
#                                             pydfu_fw_cmd = 'pydfu -u {}'.format(bin_file)
#                                             # print(upy_fw_cmd)
#                                             flash_fw_cmd = shlex.split(pydfu_fw_cmd)
#
#                                             try:
#                                                 fw_flash = subprocess.call(flash_fw_cmd)
#                                             except Exception as e:
#                                                 # ble_cp.enable_wrepl_io()
#                                                 print(e)
#                                             print('Flashing firmware finished successfully!')
#                                             time.sleep(1)
#                                             resp = input('Press any key to continue')
#                                         else:
#                                             print('Using dfu-util...')
#                                             dfu_fw_cmd = 'sudo dfu-util --alt 0 -D {}'.format(bin_file)
#                                             # print(upy_fw_cmd)
#                                             flash_fw_cmd = shlex.split(dfu_fw_cmd)
#
#                                             try:
#                                                 fw_flash = subprocess.call(flash_fw_cmd)
#                                             except Exception as e:
#                                                 # ble_cp.enable_wrepl_io()
#                                                 print(e)
#                                             print('Flashing firmware finished successfully, RESET the pyboard now')
#                                             time.sleep(1)
#                                             resp = input('Press any key to continue')
#
#                                         time.sleep(0.2)
#                                         ble_cp.enable_wrepl_io()
#                                         ble_cp.flush_conn()
#                                         ble_cp.flush_conn()
#                                         time.sleep(1)
#                                         ble_cp.sh_repl('import gc;import os;from upysh import *;gc.collect()', silent=True)
#                                         ble_cp.flush_conn()
#                                     else:
#                                         print('Operation cancelled')
#
#                             except Exception as e:
#                                 print(e)
#                                 print('Operation cancelled')
#
#                     # print(fw_req.decode())
#                 except Exception as e:
#                     print(e)
#
#     if cmd_inp.split()[0] == 'flash':
#         kw_cmd = cmd_inp.split()
#         bin_file = kw_cmd[1]
#         # ble_cp.enable_wrepl_io()
#         if dev_platform != 'pyboard':
#             ble_cp.enable_wrepl_io()
#             upy_fw_cmd = 'upydev flash -port {} -f {}'.format(args.t, bin_file)
#             # print(upy_fw_cmd)
#             flash_fw_cmd = shlex.split(upy_fw_cmd)
#
#             try:
#                 fw_flash = subprocess.call(flash_fw_cmd)
#             except Exception as e:
#                 # ble_cp.enable_wrepl_io()
#                 print(e)
#         elif dev_platform == 'pyboard':
#             # ble_cp.enable_wrepl_io()
#             # print('Warning: Unmount the pyboard first if mounted, ready? (y/n)')
#             dfu_mode = input('Warning: Unmount the pyboard first if mounted (but DO NOT DISCONNECT), press any key to continue.')
#             print('Enabling DFU mode in pyboard, DO NOT DISCONNECT...')
#             ble_cp.send_message('import pyb;pyb.bootloader()\r')
#             ble_cp.enable_wrepl_io()
#             # dfu_mode = input('Put the pyboard in DFU mode, need help? (y/n): ')
#             # if dfu_mode == 'y':
#             #     webbrowser.open('https://github.com/micropython/micropython/wiki/Pyboard-Firmware-Update')
#             # else:
#             #     pass
#             flash_tool = input('Select a tool to flash pydfu.py/dfu-util: (0/1) ')
#             if flash_tool == '0':
#                 print('Using pydfu.py...')
#                 pydfu_fw_cmd = 'pydfu -u {}'.format(bin_file)
#                 # print(upy_fw_cmd)
#                 flash_fw_cmd = shlex.split(pydfu_fw_cmd)
#
#                 try:
#                     fw_flash = subprocess.call(flash_fw_cmd)
#                 except Exception as e:
#                     # ble_cp.enable_wrepl_io()
#                     print(e)
#                 print('Flashing firmware finished successfully!')
#                 time.sleep(1)
#                 resp = input('Press any key to continue')
#             else:
#                 print('Using dfu-util...')
#                 dfu_fw_cmd = 'sudo dfu-util --alt 0 -D {}'.format(bin_file)
#                 # print(upy_fw_cmd)
#                 flash_fw_cmd = shlex.split(dfu_fw_cmd)
#
#                 try:
#                     fw_flash = subprocess.call(flash_fw_cmd)
#                 except Exception as e:
#                     # ble_cp.enable_wrepl_io()
#                     print(e)
#                 print('Flashing firmware finished successfully, RESET the pyboard now.')
#                 time.sleep(1)
#                 resp = input('Press any key to continue')
#         time.sleep(0.2)
#         ble_cp.enable_wrepl_io()
#         ble_cp.flush_conn()
#         ble_cp.flush_conn()
#         time.sleep(1)
#         ble_cp.sh_repl('import gc;import os;from upysh import *;gc.collect()', silent=True)
#         ble_cp.flush_conn()
#
#     if cmd_inp.split(' ')[0] == 'uping':
#         try:
#             ping_dir = cmd_inp.split(' ')[1]
#             if ping_dir == 'host':
#                 ping_dir = ifconfig_local = netifaces.ifaddresses('en0')[netifaces.AF_INET][0]['addr']
#             inp = "import uping;uping.ping('{}', loop=True, rtn=False)".format(ping_dir)
#             try:
#                 ble_cp.sh_repl(inp, follow=True)
#                 # if ble_cp.dev.output is not None:
#                 #     print(ble_cp.dev.output)
#             except KeyboardInterrupt:
#                 time.sleep(0.2)
#                 ble_cp.sh_repl('\x03', silent=True,
#                                 traceback=True)  # KBI
#                 time.sleep(0.2)
#                 for i in range(1):
#                     ble_cp.sh_repl('\x0d', silent=True)
#                     ble_cp.flush_conn()
#         except Exception as e:
#             print("Indicate a host to ping, e.g google.com")
#
#     if cmd_inp.split(' ')[0] == 'lping':
#         try:
#             ping_dir = cmd_inp.split(' ')[1]
#             if ping_dir == 'dev':
#                 sta_isconnected = send_custom_sh_cmd(
#                     "import network;network.WLAN(network.STA_IF).isconnected()")
#                 if sta_isconnected:
#                     ifconf = send_custom_sh_cmd(
#                         "network.WLAN(network.STA_IF).ifconfig()")
#                     ping_dir = ifconf[0]
#             ping_cmd_str = 'ping {}'.format(ping_dir)
#             ping_cmd = shlex.split(ping_cmd_str)
#             old_action = signal.signal(signal.SIGINT, signal.SIG_IGN)
#
#             def preexec_function(action=old_action):
#                 signal.signal(signal.SIGINT, action)
#             try:
#                 do_ping = subprocess.call(ping_cmd, preexec_fn=preexec_function)
#                 signal.signal(signal.SIGINT, old_action)
#             except Exception as e:
#                 print(e)
#         except Exception as e:
#             print("Indicate a host to ping, e.g google.com")
#
#     if cmd_inp.split(' ')[0] == 'timeit':
#         try:
#             script_to_time = cmd_inp.split(' ')[1]
#             if script_to_time.endswith('.py') or script_to_time.endswith('.mpy'):
#                 print('Timing script: {}'.format(script_to_time))
#                 script_to_time = script_to_time.replace('.py', '').replace('.mpy', '')
#                 inp = "from time_it import tzero, tdiff, result;t_0 = tzero();import {0};diff=tdiff(t_0);result('{0}',diff);".format(script_to_time)
#             else:
#                 cmd_to_time = ' '.join(cmd_inp.split()[1:])
#                 inp = "from time_it import tzero, tdiff, result;t_0 = tzero();{0};diff=tdiff(t_0);result('{0}',diff, cmd=True);".format(cmd_to_time)
#
#             try:
#                 ble_cp.sh_repl(inp, follow=True)
#                 # if ble_cp.dev.output is not None:
#                 #     print(ble_cp.dev.output)
#             except KeyboardInterrupt:
#                 time.sleep(0.2)
#                 ble_cp.sh_repl('\x03', silent=True,
#                                 traceback=True)  # KBI
#                 time.sleep(0.2)
#                 for i in range(1):
#                     ble_cp.sh_repl('\x0d', silent=True)
#                     ble_cp.flush_conn()
#         except Exception as e:
#             # print(e)
#             print("Indicate a script/command to measure execution time")
#
#     if cmd_inp.split()[0] == 'i2c':
#         try:
#             i2c_mode = cmd_inp.split()[1]
#             if i2c_mode == 'config':
#                 if dev_platform == 'pyboard':
#                     scl = cmd_inp.split()[3]
#                     sda = cmd_inp.split()[5]
#                     ble_cp.sh_repl("from machine import I2C;i2c = I2C(scl='{}', sda='{}')".format(scl,sda))
#                 else:
#                     scl = cmd_inp.split()[3]
#                     sda = cmd_inp.split()[5]
#                     ble_cp.sh_repl("from machine import Pin, I2C;i2c = I2C(scl=Pin({}), sda=Pin({}))".format(scl,sda))
#             elif i2c_mode == 'scan':
#                 i2c_devs = send_custom_sh_cmd("i2c.scan()")
#                 if len(i2c_devs) > 0:
#                     print('Found {} devices/addrs:'.format(len(i2c_devs)))
#                     print('int:', i2c_devs)
#                     print('Hex:', [hex(dev) for dev in i2c_devs])
#                 else:
#                     print('No device found')
#         except Exception as e:
#             print('Indicate i2c mode (config/scan)')
#
    if cmd_inp.split()[0] == 'git':
        cmd_git = shlex.split(cmd_inp)
        if 'push' in cmd_git and 'dev' in cmd_git:  # push changes to "dev repo"
            # Parse changed files
            # try:
            #     latest_commit = shlex.split('git show --name-only --oneline HEAD')
            #     commit_sha = subprocess.check_output(latest_commit).decode().split()[0]
            #     files_diff = shlex.split('git ls-tree --name-only -r {}'.format(commit_sha))
            #     files_to_push = subprocess.check_output(files_diff).decode().splitlines()
            # except Exception as e:
            #     print(e)
            files_to_push = []
            for file_tp in git_diff_files['diff']:
                if file_tp not in files_to_push:
                    files_to_push.append(file_tp)
            # files_to_push = git_diff_files['diff']
            if len(files_to_push) == 0:
                print('Everything up-to-date with {}'.format(dev_platform))
            else:
                # Parse changed files
                print('Changes to push:')
                latest_commit = shlex.split(
                    'git show -{} --name-only --oneline HEAD'.format(git_diff_files['n_commits']))
                subprocess.call(latest_commit)
                files_to_put_dir = [fl for fl in files_to_push if os.path.isfile(fl)]
                dirs_to_put_dir = ['/'.join(dir.split('/')[:-1])
                                   for dir in files_to_put_dir]
                # dirs_to_put_dir = [fl for fl in os.listdir() if not os.path.isfile(fl) and fl in files_to_push]
                # print(files_to_put_dir)
                # print(dirs_to_put_dir)
                log_cmd = shlex.split('git log -{}'.format(git_diff_files['n_commits']))
                log_to_dev = subprocess.check_output(log_cmd)
                hash_cmd = shlex.split(
                    'git log -{} --pretty=format:"%H"'.format(git_diff_files['n_commits']))
                hash_to_dev = subprocess.check_output(hash_cmd)

                cwd_now = send_custom_sh_cmd("os.getcwd()")
                if len(files_to_put_dir) > 0:
                    if dev_platform == 'pyboard':
                        send_custom_sh_cmd("cd('/')")
                        send_custom_sh_cmd("cd('/flash')")
                    else:
                        send_custom_sh_cmd("cd('/')")
                if len(dirs_to_put_dir) > 0:
                    print('New directories to make:')
                    order_dirs = [(len(dir.split('/')), dir)
                                  for dir in dirs_to_put_dir if dir != '']
                    order_dirs.sort()
                    # check if already in dev:
                    for _dir in order_dirs:
                        if '/' in _dir[1]:
                            dir_in_dev = send_custom_sh_cmd("os.listdir('{}')".format(
                                '/'+'/'.join(_dir[1].split('/')[:-1])))
                        else:
                            dir_in_dev = send_custom_sh_cmd("os.listdir()")
                        if _dir[1].split('/')[-1] not in dir_in_dev:
                            if _dir[1] != '':
                                print('- {}'.format(_dir[1]))
                                send_custom_sh_cmd("os.mkdir('{}')".format(_dir[1]))
                    # for _dir in order_dirs:
                    #     send_custom_sh_cmd("os.mkdir('{}')".format(_dir[1]))

                if len(files_to_put_dir) > 0:
                    print('Files to push:')
                for file in files_to_put_dir:
                    print('- {}'.format(file))
                ble_htool.start_SOC()
                for file in files_to_put_dir:
                    ble_htool.put(file)
                    time.sleep(0.2)
                # ble_cp.sh_repl("print('Done!')")
                time.sleep(0.2)
                ble_htool.stop_SOC()
                # Save commit log in dev
                # log_to_dev += b'---'*10+b'\n'
                print('\nUpdating dev .gitlog.', end='')
                send_custom_sh_cmd(
                    "gitlog = open('.gitlog', 'ab+');gitlog.seek(0);gl=gitlog.read();gitlog.seek(0);gitlog.write({});gitlog.write(gl);gitlog.close()".format(log_to_dev))
                time.sleep(0.1)
                print('\rUpdating dev .gitlog..', end='')
                send_custom_sh_cmd(
                    "gitlog = open('.githash', 'ab+');gitlog.seek(0);gl=gitlog.read();gitlog.seek(0);gitlog.write({});gitlog.write(gl);gitlog.close()".format(hash_to_dev+b'\n'))
                send_custom_sh_cmd("cd('{}')".format(cwd_now))
                print('\rUpdating dev .gitlog..', end='')
                git_diff_files['diff'] = []
                git_diff_files['n_commits'] = 0
                print('\r\r', end='')
        elif 'commit' in cmd_git:
            status_cmd = shlex.split('git status -s')
            files_to_push = [file for file in subprocess.check_output(
                status_cmd).decode().split('\n') if file != '']
            files_to_pushdev = [file.split()[1] for file in files_to_push if file.split()[
                                           0] == 'M' or file.split()[0] == 'A']
            if len(git_diff_files['diff']) == 0:
                git_diff_files['diff'] = files_to_pushdev
                if len(files_to_pushdev) > 0:
                    git_diff_files['n_commits'] = 1
            else:
                git_diff_files['diff'] = git_diff_files['diff'] + \
                    [file for file in files_to_pushdev if file not in git_diff_files['diff']]
                if len(files_to_pushdev) > 0:
                    git_diff_files['n_commits'] += 1
            # print(git_diff_files)
            subprocess.call(cmd_git)

        elif 'pull' in cmd_git:
            subprocess.call(cmd_git)
            # git_pull_diff_cmd = shlex.split('git show --name-only --oneline FETCH_HEAD')
            # files_to_pushdev = [file for file in subprocess.check_output(git_pull_diff_cmd).decode().split('\n')[1:] if file != '']
            # git_diff_files['diff'] = files_to_pushdev
            cmd_git = shlex.split('git log --pretty=format:"%H"')
            local_gitlog = subprocess.check_output(cmd_git).decode().split()
            log_dev_head = send_custom_sh_cmd("cat('.githash')"+';gc.collect()')
            # print(log_dev_head)
            log_info_list = log_dev_head.split()
            if local_gitlog == log_info_list:
                pass
            else:
                if len(git_diff_files['diff']) > 0:  # tracked changes
                    cmd_git_l = shlex.split(
                        'git log --name-status  --pretty=format:"%H"')
                    local_gitlog_files = subprocess.check_output(cmd_git_l).decode()
                    commit_dict = {c.split('\n')[0]: [s for s in c.replace('\t', '\n').split()[
                                           1:]] for c in local_gitlog_files.split('\n\n')}
                    for commit in commit_dict.keys():
                        if commit not in log_info_list:
                            if any([file not in git_diff_files['diff'] for file in commit_dict[commit][1::2]]):
                                git_diff_files['diff'] += [file for file in commit_dict[commit]
                                                           [1::2] if file not in git_diff_files['diff']]
                    git_diff_files['n_commits'] = int(
                        len(local_gitlog) - len(log_info_list))

                else:  # untracked changes (outside shell)
                    cmd_git_l = shlex.split(
                        'git log --name-status  --pretty=format:"%H"')
                    local_gitlog = subprocess.check_output(cmd_git_l).decode()
                    commit_dict = {c.split('\n')[0]: [s for s in c.replace(
                        '\t', '\n').split()[1:]] for c in local_gitlog.split('\n\n')}
                    for commit in commit_dict.keys():
                        if commit not in log_info_list:
                            git_diff_files['diff'] += commit_dict[commit][1::2]
                            git_diff_files['n_commits'] += 1

            # print(status_msg)

        elif 'clone_dev' in cmd_git:
            print('File/s to put:')
            file = 'cwd'
            if file == 'cwd':
                files_to_put = [fl for fl in os.listdir() if os.path.isfile(
                    fl) and not fl.startswith('.')]
                for file in files_to_put:
                    print('- {}'.format(file))
                ble_htool.start_SOC()
                for file in files_to_put:
                    ble_htool.put(file)
                    time.sleep(0.2)
                # ssl_cp.ssl_repl("print('Done!')")
                time.sleep(0.2)
                ble_htool.stop_SOC()

            dirs_to_sync = [fl for fl in os.listdir() if os.path.isdir(fl)
                            and not fl.startswith('.')]
            print('Dir/s to sync:')
            for _dir in dirs_to_sync:
                print('- {}'.format(_dir))
                # print('Directory to sync: {}'.format(dir_to_sync))
                d_sync_recursive(_dir, show_tree=True, root_sync_folder=_dir)

        elif 'log' in cmd_git and 'dev' in cmd_git:
            try:
                if '-a' in cmd_git:
                    # ble_cp.sh_repl("cat('.gitlog')"+';gc.collect()', follow=True)
                    log_dev_head = send_custom_sh_cmd("cat('.gitlog')"+';gc.collect()')
                    log_dev_head = log_dev_head.replace(
                        '\n ', '\n\n ').replace('\ncommit', '\n\ncommit')
                    log_dev_head = log_dev_head.replace('\n\ncommit', '\n\n{}commit'.format(
                        YELLOW)).replace('\nAuthor: ', '{}\nAuthor: '.format(CEND))
                    last_commit_header = log_dev_head.split('\n')[0]
                    if 'Traceback (most recent call last)' in last_commit_header:
                        print('Device {} repo not initiated'.format(dev_platform))
                    else:
                        log_dev_head = log_dev_head.replace(last_commit_header, '')
                        head_dev = ' ({}HEAD -> {}master{} {}'.format(BCYAN, CGREEN,
                                                                      CEND+YELLOW+','+BCYAN+CRED, dev_platform+CEND+YELLOW)
                        commit = "{}{}){}".format(
                            YELLOW, last_commit_header + head_dev, CEND)
                        print(commit, end='')
                        print(log_dev_head)
                else:
                    log_dev_head = send_custom_sh_cmd("head('.gitlog')"+';gc.collect()',
                                                      long_string=True,
                                                      rtn=True)
                    # print(log_dev_head)
                    log_info_list = log_dev_head.split(
                        '\ncommit ')[0].replace('\n    ', '\n\n    ', 1)
                    # print(log_info_list)
                    commit_body = '\n'.join(log_info_list.split('\n')[1:])
                    commit_head = log_info_list.split('\n')[0]
                    if 'Traceback (most recent call last)' in commit_head:
                        print('Device {} repo not initiated'.format(dev_platform))
                    else:
                        head_dev = ' ({}HEAD -> {}master{} {}'.format(BCYAN, CGREEN,
                                                                      CEND+YELLOW+','+BCYAN+CRED, dev_platform+CEND+YELLOW)
                        commit = "{}{}){}".format(YELLOW, commit_head + head_dev, CEND)
                        print(commit)
                        print(commit_body)
            except Exception as e:
                print('No commits in dev log to show')

        elif 'log' in cmd_git and 'host' in cmd_git:
            cmd_git = shlex.split('git log -1')
            subprocess.call(cmd_git)

        elif 'init' in cmd_git and 'dev' in cmd_git:
            cwd_now = send_custom_sh_cmd("os.getcwd()")
            if dev_platform == 'pyboard':
                send_custom_sh_cmd("cd('/')")
                send_custom_sh_cmd("cd('/flash')")
            else:
                send_custom_sh_cmd("cd('/')")
            is_gitlog = "'.gitlog' in os.listdir()"
            is_init = send_custom_sh_cmd(is_gitlog)
            if is_init:
                print('Device {} repo already initiated'.format(dev_platform))
            else:
                send_custom_sh_cmd("gitlog = open('.gitlog', 'w');gitlog.close()")
                send_custom_sh_cmd("gitlog = open('.githash', 'w');gitlog.close()")
                print('Device {} repo initiated successfully!'.format(dev_platform))

            send_custom_sh_cmd("cd('{}')".format(cwd_now))

        elif 'status' in cmd_git and 'dev' in cmd_git:
            cmd_git = shlex.split('git log --pretty=format:"%H"')
            local_gitlog = subprocess.check_output(cmd_git).decode().split()
            try:
                log_dev_head = send_custom_sh_cmd("cat('.githash')"+';gc.collect()')
                # print(log_dev_head)
                log_info_list = log_dev_head.split()
                if 'Traceback' in log_info_list and 'ENOENT' in log_info_list:
                    log_info_list = []
            except Exception as e:
                log_info_list = []

            if local_gitlog == log_info_list:
                status_msg = "On branch master\nYour branch is up to date with '{}'.\n\nnothing to commit, working tree clean".format(
                    dev_platform)
            else:
                if len(git_diff_files['diff']) > 0:  # tracked changes
                    cmd_git_l = shlex.split(
                        'git log --name-status  --pretty=format:"%H"')
                    local_gitlog_files = subprocess.check_output(cmd_git_l).decode()
                    commit_dict = {c.split('\n')[0]: [s for s in c.replace('\t', '\n').split()[
                                           1:]] for c in local_gitlog_files.split('\n\n')}
                    for commit in commit_dict.keys():
                        if commit not in log_info_list:
                            if any([file not in git_diff_files['diff'] for file in commit_dict[commit][1::2]]):
                                git_diff_files['diff'] += [file for file in commit_dict[commit]
                                                           [1::2] if file not in git_diff_files['diff']]
                    git_diff_files['n_commits'] = int(
                        len(local_gitlog) - len(log_info_list))

                    status_msg = "On branch master\nYour branch is ahead of '{0}' by {1} commit{2}.\n (use 'git push dev' to push your local commits to {0})\n\nnothing to commit, working tree clean".format(dev_platform, git_diff_files['n_commits'],
                                                                                                                                                                                                              ['s' if git_diff_files['n_commits'] > 1 else '' for i in range(1)][0])

                else:  # untracked changes (outside shell)
                    cmd_git_l = shlex.split(
                        'git log --name-status  --pretty=format:"%H"')
                    local_gitlog = subprocess.check_output(cmd_git_l).decode()
                    commit_dict = {c.split('\n')[0]: [s for s in c.replace(
                        '\t', '\n').split()[1:]] for c in local_gitlog.split('\n\n')}
                    for commit in commit_dict.keys():
                        if commit not in log_info_list:
                            git_diff_files['diff'] += commit_dict[commit][1::2]
                            git_diff_files['n_commits'] += 1

                    status_msg = "On branch master\nYour branch is ahead of '{0}' by {1} commit{2} not tracked by upydev.\n (use 'git push dev' to push last commit changes to {0})\n\nnothing to commit, working tree clean".format(dev_platform, git_diff_files['n_commits'],
                                                                                                                                                                                                                                     ['s' if git_diff_files['n_commits'] > 1 else '' for i in range(1)][0])
            print(status_msg)

        elif 'repo' in cmd_git:
            remote_repo_cmd = shlex.split('git remote -v')
            git_repo = subprocess.check_output(remote_repo_cmd)
            if git_repo != b'':
                webbrowser.open(git_repo.split()[1].decode())
            else:
                print('git repository does not have a remote')

        else:
            subprocess.call(cmd_git)

    if cmd_inp == 'tig':
        try:
            tigtool = 'tig'
            tig_cmd = shlex.split(tigtool)
            # old_action = signal.signal(signal.SIGINT, signal.SIG_IGN)
            #
            # def preexec_function(action=old_action):
            #     signal.signal(signal.SIGINT, action)
            try:
                if '.git' in os.listdir():
                    tig = subprocess.call(tig_cmd)
                else:
                    print('tig: Not a git repository')
                # signal.signal(signal.SIGINT, old_action)
            except Exception as e:
                print(e)
                print('tig not found')
        except Exception as e:
            print(e)
            pass
#
#     if cmd_inp == 'upy-config':
#         results_array = radiolist_dialog(
#                 title="Upydev {} Configuration Tool".format(dev_platform),
#                 text="Select an option and press Select (Use tab to change focus)",
#                 ok_text="Select",
#                 cancel_text="Finish",
#                 values=[
#                     ("network", "{:25}{}".format("1 Network Options",
#                                                  "Configure network settings")),
#                     ("interface", "{:25}{}".format("2 Interfacing Options",
#                                                    "Configure connections to peripherals"))
#                 ]
#             ).run()
#         if results_array is not None:
#             # print('Configure dialog: {}'.format(results_array))
#             if results_array == 'network':
#                 results_array = radiolist_dialog(
#                         title="Upydev {} Configuration Tool".format(
#                             dev_platform),
#                         text="Select an option and press Select (Use tab to change focus)",
#                         ok_text="Select",
#                         cancel_text="Finish",
#                         values=[
#                             ("STA", "{:10}{}".format("1 STA:",
#                                                      "Configure network SSID and PASSWORD to connect to a WLAN")),
#                             ("AP", "{:10}{}".format("2 AP:",
#                                                     "Configure SSID and PASSWORD to enable {} access Point".format(dev_platform)))
#                         ]
#                     ).run()
#                 if results_array == 'STA':
#                     print('Scanning WLANs...')
#                     enable_sta = send_custom_sh_cmd(
#                         "import network;network.WLAN(network.STA_IF).active(1)")
#                     if enable_sta:
#                         scan = send_custom_sh_cmd(
#                             "network.WLAN(network.STA_IF).scan()")
#                         if 'event' in espdev.response:
#                             # print(espdev.response)
#                             try:
#                                 scan = ast.literal_eval(
#                                     espdev.response.splitlines()[-1])
#                             except Exception as e:
#                                 scan = []
#                             # print(scan)
#                         if len(scan) == 0:
#                             message_dialog(title="Upydev {} Configuration Tool".format(dev_platform),
#                                            text="No WLAN available").run()
#                         else:
#                             scan_list = []
#                             for netscan in scan:
#                                 auth = AUTHMODE_DICT[netscan[4]]
#                                 vals = hexlify(netscan[1]).decode()
#                                 bssid = ':'.join([vals[i:i+2]
#                                                   for i in range(0, len(vals), 2)])
#                                 scan_list.append((netscan[0].decode(), '{0:20}[{1}] RSSI:{2} AUTH: {3}'.format(
#                                     netscan[0].decode(), bssid, netscan[3],
#                                     auth)))
#
#                             results_array = radiolist_dialog(
#                                     title="Upydev {} Configuration Tool".format(
#                                         dev_platform),
#                                     text="Select an option and press Select (Use tab to change focus)",
#                                     ok_text="Select",
#                                     cancel_text="Finish",
#                                     values=scan_list
#                                 ).run()
#                             if results_array is not None:
#                                 ssid = results_array
#                                 passwd = input_dialog(
#                                         title="Upydev {} Configuration Tool".format(
#                                             dev_platform),
#                                         text='Please type {} Password:'.format(ssid)).run()
#                                 connect_to = "network.WLAN(network.STA_IF).connect('{}', '{}')".format(
#                                     ssid, passwd)
#                                 if passwd is not None:
#                                     print('Connecting to {}...'.format(ssid))
#                                     disconn_cmd = "network.WLAN(network.STA_IF).disconnect()\r"
#                                     conn_cmd = "{};gc.collect()\r".format(connect_to)
#                                     dconn_sta = ble_cp.send_message(
#                                         disconn_cmd)
#                                     # send_custom_sh_cmd('\r\r')
#                                     time.sleep(1)
#                                     ble_cp.send_message('\r\r')
#                                     is_connected = False
#                                     conn_sta = ble_cp.send_message(conn_cmd)
#                                     time.sleep(1)
#                                     ble_cp.send_message('\r\r')
#                                     while not is_connected:
#                                         is_connected = send_custom_sh_cmd(
#                                             "network.WLAN(network.STA_IF).isconnected()")
#                                         time.sleep(1)
#
#                                     saveconfig = yes_no_dialog(title="Upydev {} Configuration Tool".format(dev_platform),
#                                                                text="{} connected successfully to {}\n Do you want to save configuration?".format(dev_platform, ssid)).run()
#
#                                     if saveconfig:
#                                         saveconfig_cmd = "import json;sta_config=open('wifi_.config', 'w');sta_config.write(json.dumps(dict(ssid='{}', password='{}')));sta_config.close()".format(
#                                             ssid, passwd)
#                                         send_custom_sh_cmd(saveconfig_cmd)
#                                         message_dialog(title="Upydev {} Configuration Tool".format(dev_platform),
#                                                        text="{} Configuration saved successfully as 'wifi_.config'".format(ssid)).run()
#
#                     else:
#                         message_dialog(title="Upydev {} Configuration Tool".format(dev_platform),
#                                        text="Can't enable STA interface").run()
#
#                 elif results_array == 'AP':
#                     ssid = input_dialog(
#                             title="Upydev {} Configuration Tool".format(
#                                 dev_platform),
#                             text='Please set {} SSID:'.format(dev_platform)).run()
#                     passwd = input_dialog(
#                             title="Upydev {} Configuration Tool".format(
#                                 dev_platform),
#                             text='Please set {} Password:'.format(ssid)).run()
#                     if ssid is not None and passwd is not None:
#                         ap_enbale = ble_cp.send_message(
#                             "import network;network.WLAN(network.AP_IF).active(0)\r")
#                         time.sleep(0.5)
#                         ap_enbale = ble_cp.send_message(
#                             "import network;network.WLAN(network.AP_IF).active(1)\r")
#                         print('Configuring {} AP...'.format(ssid))
#                         time.sleep(0.5)
#                         ap_config = "import network;network.WLAN(network.AP_IF).config(essid='{}',authmode=network.AUTH_WPA_WPA2_PSK, password='{}')".format(
#                             ssid, passwd)
#                         resp = ble_cp.send_message(ap_config+'\r')
#                         time.sleep(1)
#                         saveconfig = yes_no_dialog(title="Upydev {} Configuration Tool".format(dev_platform),
#                                                    text="{} AP:{} enabled successfully\n Do you want to save configuration?".format(dev_platform, ssid)).run()
#
#                         if saveconfig:
#                             saveconfig_cmd = "import json;sta_config=open('ap_.config', 'w');sta_config.write(json.dumps(dict(ssid='{}', password='{}')));sta_config.close()".format(
#                                 ssid, passwd)
#                             send_custom_sh_cmd(saveconfig_cmd)
#                             message_dialog(title="Upydev {} Configuration Tool".format(dev_platform),
#                                            text="{} Configuration saved successfully as 'ap_.config'".format(ssid)).run()
#
#             elif results_array == 'interface':
#                 results_array = radiolist_dialog(
#                         title="Upydev {} Configuration Tool".format(
#                             dev_platform),
#                         text="Select an option and press Select (Use tab to change focus)",
#                         ok_text="Select",
#                         cancel_text="Finish",
#                         values=[
#                             ("I2C", "{:10}{}".format("1 I2C:",
#                                                      "Configure I2C interface"))
#                         ]
#                     ).run()
#
#                 if results_array == 'I2C':
#                     if dev_platform == 'esp32':
#                         default = 'I2C: SCL = 22 , SDA = 23, FREQ = 100000'
#                         scl = 22
#                         sda = 23
#                     elif dev_platform == 'esp8266':
#                         default = 'I2C: SCL = 5 , SDA = 4, FREQ = 100000'
#                         scl = 5
#                         sda = 4
#                     elif dev_platform == 'pyboard':
#                         default = 'I2C: SCL = X9 , SDA = X10, FREQ = 100000'
#                         scl = 'X9'
#                         sda = 'X10'
#                     results_array = radiolist_dialog(
#                             title="Upydev {} Configuration Tool".format(
#                                 dev_platform),
#                             text="Select an option and press Select (Use tab to change focus)",
#                             ok_text="Select",
#                             cancel_text="Finish",
#                             values=[
#                                 ("I2C_DEF", "{:10}{}".format("1 I2C DEFAULT:",
#                                                          default)),
#                                 ("I2C_CUSTOM", "{:10}{}".format("2 I2C CUSTOM:",
#                                                          "SET SCL, SDA and FREQ"))
#                             ]
#                         ).run()
#                     if results_array == 'I2C_DEF':
#                         if dev_platform == 'pyboard':
#                             ble_cp.sh_repl("from machine import I2C;i2c = I2C(scl='{}', sda='{}')".format(scl,sda))
#                         else:
#                             ble_cp.sh_repl("from machine import Pin, I2C;i2c = I2C(scl=Pin({}), sda=Pin({}))".format(scl,sda))
#
#                         message_dialog(title="Upydev {} Configuration Tool".format(dev_platform),
#                                        text="I2C Default configuration enabled successfully as 'i2c'").run()
#                     elif results_array == 'I2C_CUSTOM':
#                         custom_i2c = input_dialog(
#                                 title="Upydev {} Configuration Tool".format(
#                                     dev_platform),
#                                 text='Please set SCL SDA and FREQ separated by space, e.g. : 22 23 100000:'.format(dev_platform)).run()
#                         if custom_i2c is None or custom_i2c == '':
#                             freq = 100000
#                             custom_i2c = "{} {} {}".format(scl, sda, freq)
#                         else:
#                             scl = custom_i2c.split()[0]
#                             sda = custom_i2c.split()[1]
#                             freq = custom_i2c.split()[2]
#                         if dev_platform == 'pyboard':
#                             ble_cp.sh_repl("from machine import I2C;i2c_custom = I2C(scl='{}', sda='{}', freq={})".format(scl, sda, freq))
#                         else:
#                             ble_cp.sh_repl("from machine import Pin, I2C;i2c_custom = I2C(scl=Pin({}), sda=Pin({}), freq={})".format(scl, sda, freq))
#
#                         message_dialog(title="Upydev {} Configuration Tool".format(dev_platform),
#                                        text="I2C Custom configuration enabled successfully as 'i2c_custom'").run()

    if cmd_inp.split()[0].startswith('%') or cmd_inp.split()[0] not in custom_sh_cmd_kw + shell_commands:
        try:
            if cmd_inp.startswith('%'):
                cmd_inp = cmd_inp[1:]
            shell_cmd_str = shlex.split(cmd_inp)
            if shell_cmd_str[0] in SHELL_ALIASES:
                shell_cmd_str = SHELL_ALIASES[shell_cmd_str[0]
                                              ].split() + shell_cmd_str[1:]
            old_action = signal.signal(signal.SIGINT, signal.SIG_IGN)

            def preexec_function(action=old_action):
                signal.signal(signal.SIGINT, action)
            try:
                shell_cmd = subprocess.call(shell_cmd_str,
                                            preexec_fn=preexec_function)
                signal.signal(signal.SIGINT, old_action)
            except Exception as e:
                print(e)
        except Exception as e:
            print(e)
            pass

    if cmd_inp.split(' ')[0] == 'exit':
        if reset_flag['R']:
            if len(cmd_inp.split(' ')) > 1:
                if cmd_inp.split(' ')[1] == '-nr':
                    # ble_cp.sh_repl("ssl_client.switch_wrepl()")
                    if ble_cp.dev.is_connected():
                        ble_cp.dev.disconnect()
                    time.sleep(0.5)
                elif cmd_inp.split(' ')[1] == '-hr':
                    print('Device Hard Reset...')
                    ble_cp.dev.write_char_raw(
                        key='Nordic UART RX', data='import machine;machine.reset()\r')
                    time.sleep(1)
                    print('Done!')
            else:
                if ble_cp.dev.is_connected():
                    print('Rebooting device...')
                    ble_cp.dev.reset(silent=True, reconnect=False)
                    time.sleep(1)
                    print('Done!')
                else:
                    print('Cannot Reboot, device disconnected already.')
        else:
            # ble_cp.sh_repl("ssl_client.switch_wrepl()")
            time.sleep(0.5)
        exit_flag['exit'] = True
#
#


@Condition
def autosuggest_is_on():
    return autosuggest['A']
#
#
# # KEYBINDINGS
#


@kb.add('c-x')
def exitpress(event):
    " Exit BLE SHELL-REPL terminal "
    async def run_kb():
        if shell_mode['S']:
            print('\nclosing...')
        else:
            print('\n>>> closing...')
        if reset_flag['R']:
            print('Rebooting device...')
            await ble_cp.dev.as_reset()
            print('Done!')
        else:
            pass
            # ble_cp.sh_repl("ssl_client.switch_wrepl()")
            # time.sleep(0.5)
        # ble_cp.dev.disconnect()
        await asyncio.sleep(1, loop=ble_cp.dev.loop)
        exit_flag['exit'] = True
        event.app.exit()
        return None

    async def f():
        async with in_terminal():
            await run_kb()

    loop = asyncio.get_event_loop()
    loop.create_task(f())


@kb.add('c-b')
def bannerpress(event):
    "Prints MicroPython sys version info"
    # def upy_sysversion():
    #     ble_cp.dev.banner(kb=True, follow=True)
    #
    # run_in_terminal(upy_sysversion)
    async def run_kb():
        ble_cp.dev.output = None
        await ble_cp.as_sh_repl(ble_cp.dev._banner,
                                follow=True)
        return None

    async def f():
        async with in_terminal():
            await run_kb()

    loop = asyncio.get_event_loop()
    loop.create_task(f())


@kb.add('c-k')
def see_cmd_info_press(event):
    "CTRL-Commands info"
    # event.app.current_buffer.insert_text('import')
    def cmd_info():
        print(kb_info)
        if shell_mode['S']:
            print(shell_commands_info)
    run_in_terminal(cmd_info)


@kb.add('c-r')
def flush_line(event):
    event.app.current_buffer.reset()


# @kb.add('c-o')
# def cmd_ls(event):
#     def ls_out():
#         ble_cp.dev.output = None
#         print('>>> ls')
#         ble_cp.sh_repl('ls();gc.collect()')
#         if ble_cp.dev.output is not None:
#             print(ble_cp.dev.output)
#
#     run_in_terminal(ls_out)

#from micropython import mem_info; mem_info()

# output = await ble_cp.as_sh_repl('os.listdir()',
#                         silent=True, follow=False, rtn=True, rtn_resp=True)
# print(ble_cp.dev.output[-1])
#
#
@kb.add('c-n')
def cmd_mem_info(event):
    async def run_kb():
        ble_cp.dev.output = None
        print('>>> mem_info()')
        await ble_cp.as_sh_repl('from micropython import mem_info; mem_info()',
                                follow=True)
        return None

    async def f():
        async with in_terminal():
            await run_kb()

    loop = asyncio.get_event_loop()
    loop.create_task(f())


@kb.add('c-y')
def cmd_gc_collect(event):

    def gc_collect_out():
        ble_cp.dev.output = None
        print('>>> gc.collect()')
        ble_cp.sh_repl('import gc;gc.collect()', kb=True)
        if ble_cp.dev.output is not None:
            print(ble_cp.dev.output)

    run_in_terminal(gc_collect_out)


@kb.add('c-f')
def toggle_autosgst(event):
    if autosuggest['A']:
        autosuggest['A'] = False
    else:
        autosuggest['A'] = True

#
# @kb.add('c-space')
# def autocomppress(event):
#     "Send last command"
#
#     def print_last():
#         ble_cp.dev.output = None
#         last_cmd = event.app.current_buffer.history.get_strings()[-1]
#         if shell_mode['S']:
#             if local_path['p'] == '':
#                 g_p = [val[1] for val in prompt['p'][1:5]]
#                 b_p = [val[1] for val in prompt['p'][5:]]
#                 color_p = "\33[32;1m{}\033[0m:\u001b[34;1m{}\033[0m{}".format(
#                     "".join(g_p[:-1]), "".join(b_p), last_cmd)
#                 print(color_p)
#             else:
#                 m_p = [prompt['p'][0][1]]
#                 g_p = [val[1] for val in prompt['p'][1:5]]
#                 b_p = [val[1] for val in prompt['p'][5:]]
#                 color_p = "\u001b[35;1m{}\033[0m\33[32;1m{}\033[0m:\u001b[34;1m{}\033[0m{}".format(
#                     "".join(m_p), "".join(g_p[:-1]), "".join(b_p), last_cmd)
#                 print(color_p)
#             if last_cmd.split()[0] in custom_sh_cmd_kw:
#                 custom_sh_cmd(last_cmd)
#             else:
#                 inp, frst_cmd = map_upysh(last_cmd)
#                 if frst_cmd == 'run':
#
#                     try:
#                         ble_cp.sh_repl(inp, follow=True)
#                         # if ble_cp.dev.output is not None:
#                         #     print(ble_cp.dev.output)
#                     except KeyboardInterrupt:
#                         time.sleep(0.2)
#                         ble_cp.sh_repl('\x03', silent=True,
#                                         traceback=True)  # KBI
#                         time.sleep(1)
#                         for i in range(3):
#                             ble_cp.sh_repl('\x0d', silent=True)
#                             ble_cp.flush_conn()
#                         pass
#                 else:
#
#                     if inp == 'ls()' or frst_cmd == 'tree' or frst_cmd == 'cat':
#                         ble_cp.sh_repl(inp+';gc.collect()', follow=True)
#                         # ble_cp.send_message('gc.collect()\r')
#                     else:
#                         ble_cp.sh_repl(inp)
#                         if ble_cp.dev.output is not None:
#                             print(ble_cp.dev.output)
#         else:
#             print('>>> {}'.format(last_cmd))
#             try:
#                 ble_cp.sh_repl(last_cmd, follow=True)
#                 # if ble_cp.dev.output is not None:
#                 #     print(ble_cp.dev.output)
#             except KeyboardInterrupt:
#                 time.sleep(0.2)
#                 ble_cp.sh_repl('\x03', silent=True,
#                                 traceback=True)  # KBI
#                 time.sleep(0.2)
#                 for i in range(1):
#                     ble_cp.sh_repl('\x0d', silent=True)
#                     ble_cp.flush_conn()
#                 pass
#
#     run_in_terminal(print_last)
#


@kb.add('c-t')
def testpress(event):
    "Test code command"
    # async def run_kb():
    #     ble_cp.dev.output = None
    #     print('>>> import test_code')
    #     await ble_cp.as_sh_repl('do_test(10)',
    #                             follow=True)
    #     return None
    #
    # async def f():
    #     async with in_terminal():
    #         await run_kb()
    #
    # loop = asyncio.get_event_loop()
    # loop.create_task(f())
#
#
# @kb.add('c-w')
# def reloadpress(event):
#     "Reload test_code command"
#     def reload_code():
#         ble_cp.dev.output = None
#         reload_cmd = "import sys;del(sys.modules['test_code']);gc.collect()"
#         print('>>> {}'.format(reload_cmd))
#
#         ble_cp.sh_repl(reload_cmd)
#         if ble_cp.dev.output is not None:
#             print(ble_cp.dev.output)
#     if not edit_mode['E']:
#         run_in_terminal(reload_code)
#     else:
#         edit_mode['E'] = False
#


@kb.add('c-a')
def reset_cursor(event):
    "Move cursor to init position"
    buff_text = event.app.current_buffer.document.text
    event.app.current_buffer.reset()
    event.app.current_buffer.insert_text(buff_text, move_cursor=False)
#
#


@kb.add('c-s')
def toggle_shell_mode(event):

    def show_p():
        if shell_mode['S']:
            if local_path['p'] == '':
                g_p = [val[1] for val in prompt['p'][1:5]]
                b_p = [val[1] for val in prompt['p'][5:]]
                color_p = "\33[32;1m{}\033[0m:\u001b[34;1m{}\033[0m".format(
                    "".join(g_p[:-1]), "".join(b_p))
                print(color_p)
            else:
                m_p = [prompt['p'][0][1]]
                g_p = [val[1] for val in prompt['p'][1:5]]
                b_p = [val[1] for val in prompt['p'][5:]]
                color_p = "\u001b[35;1m{}\033[0m\33[32;1m{}\033[0m:\u001b[34;1m{}\033[0m".format(
                    "".join(m_p), "".join(g_p[:-1]), "".join(b_p))
                print(color_p)
        else:
            print(prompt['p'])

        if shell_mode['S']:
            prompt['p'] = d_prompt
            shell_mode['S'] = False
        else:
            prompt['p'] = shell_prompt['s']
            shell_mode['S'] = True
            ble_cp.dev.output = None

    run_in_terminal(show_p)


@kb.add('c-c')
def send_KBI(event):

    async def run_kb():
        try:
            last_cmd = ''
            if edit_mode['E']:
                prompt['p'] = shell_prompt['s']
                event.app.current_buffer.reset()
            else:
                ble_cp.dev.output = None
                print('^C')
                # await ble_cp.as_sh_repl(ble_cp.dev._kbi, follow=True)
                # await ble_cp.dev.as_kbi()
                data = bytes(ble_cp.dev._kbi + '\r', 'utf-8')
                await ble_cp.dev.ble_client.write_gatt_char(ble_cp.dev.writeables['Nordic UART RX'], data)

                paste_flag['p'] = False
                if not shell_mode['S']:
                    prompt['p'] = d_prompt
                    last_cmd = event.app.current_buffer.document.text
                if shell_mode['S']:
                    print('^C')
                event.app.current_buffer.reset()
        except Exception as e:
            pass

        def cmd_kbi(command=last_cmd):
            if prompt['p'] == ">>> ":
                print(prompt['p'] + command)
            elif prompt['p'] == shell_prompt['s']:
                if edit_mode['E'] is True:
                    edit_mode['E'] = False
                    print("<-----Edition Cancelled---->")
                    print('Press ESC, ENTER to exit and return to shell')
        cmd_kbi()

        return None

    async def f():
        async with in_terminal():
            await run_kb()

    loop = asyncio.get_event_loop()
    loop.create_task(f())
    # pending = asyncio.all_tasks()
    # print(pending)


@kb.add('c-e')
def paste_mode(event):
    "PASTE MODE IN REPL, EDIT MODE IN SHELL MODE"

    async def run_kb():
        ble_cp.dev.output = None
        if not shell_mode['S']:
            paste_flag['p'] = True
            event.app.current_buffer.reset()
            # event.app.current_buffer.insert_text('import')

            def cmd_paste():
                print(
                    'paste mode; Press ENTER to write, Ctrl-C to cancel, Ctrl-D to finish, Then ESC, ENTER to exit paste mode')
                print("===")
                prompt['p'] = ""
            cmd_paste()
        else:
            if edit_mode['E']:
                def cmd_paste():
                    print('Edit Mode; Ctrl-C to cancel, Ctrl-D to finish, then ESC, ENTER')
                    print("#<---- File: {} --->".format(edit_mode['File']))
                    prompt['p'] = ""
                cmd_paste()
                event.app.current_buffer.reset()
                # espdev.long_output = []
                ble_cp.dev.output = None
                ble_cp.dev.response = ''
                await ble_cp.as_sh_repl("cat('{}')".format(edit_mode['File']),
                                        silent=True, rtn=True)
                file_content = ble_cp.dev.output
                try:
                    process_content = file_content
                except Exception as e:
                    process_content = ' '
                if isinstance(file_content, str):
                    event.app.current_buffer.insert_text(process_content)
            else:
                # "Move cursor to final position"
                buff_text = event.app.current_buffer.document.text
                event.app.current_buffer.reset()
                event.app.current_buffer.insert_text(buff_text, move_cursor=True)
        return None

    async def f():
        async with in_terminal():
            await run_kb()

    loop = asyncio.get_event_loop()
    loop.create_task(f())


@kb.add('c-d')
def paste_mode_exit(event):
    "PASTE MODE exit in REPL, EDIT MODE exit in SHELL"
    # event.app.current_buffer.insert_text('import')
    # print([event.app.current_buffer.document.text])
    async def run_kb():
        if not shell_mode['S']:
            if paste_flag['p']:
                buff_P = paste_buffer['B']
                buff_text = '\n'.join(
                    buff_P + [event.app.current_buffer.document.text])
                # print(buff_text.splitlines())
                if buff_text is not None and buff_text != '':
                    try:
                        await ble_cp.as_paste_buff(buff_text, follow=True)
                        # ble_cp.sh_repl("\x04", follow=True)
                    except KeyboardInterrupt:
                        # time.sleep(0.2)
                        # ble_cp.sh_repl('\x03', silent=True,
                        #                 traceback=True)  # KBI
                        # time.sleep(0.2)
                        # for i in range(1):
                        #     ble_cp.sh_repl('\x0d', silent=True)
                        #     ble_cp.flush_conn()
                        pass
                    ble_cp.dev.output = None
                    # time.sleep(1)
                    # ble_cp.flush_conn()
                    # ble_cp.flush_conn()

                event.app.current_buffer.reset()

                print(">>> ")
                prompt['p'] = ">>> "
                paste_buffer['B'] = ['']
            else:
                pass
            paste_flag['p'] = False
        else:
            if edit_mode['E']:
                file_to_edit = edit_mode['File']
                buff_text = ''.join([event.app.current_buffer.document.text])
                if buff_text is not None and buff_text != '':
                    send_custom_sh_cmd(
                        "_file_to_edit = open('{}','w')".format(file_to_edit))
                    send_custom_sh_cmd("nl = '\\n'")
                    print('Writing to file...')
                    lines_to_write = buff_text.split('\n')
                    for line in lines_to_write:
                        if line != ' ' and line != "" and line != '':
                            send_custom_sh_cmd(
                                '_file_to_edit.write("{}")'.format(line))

                            send_custom_sh_cmd('_file_to_edit.write(nl)')

                    send_custom_sh_cmd("_file_to_edit.close()")
                    send_custom_sh_cmd("gc.collect()")
                    time.sleep(0.5)
                    ble_cp.flush_conn()
                    ble_cp.flush_conn()
                event.app.current_buffer.reset()

                print("File {} edited successfully!".format(file_to_edit))
                print('PRESS ESC, THEN ENTER TO EXIT EDIT MODE')
                prompt['p'] = shell_prompt['s']
            edit_mode['E'] = False
        # paste_buffer['B'] = ['']  # RESET PASTE BUFFER
        return None

    async def f():
        async with in_terminal():
            await run_kb()

    loop = asyncio.get_event_loop()
    loop.create_task(f())


@kb.add('tab')
def tab_press(event):
    "Tab autocompletion info"
    # event.app.current_buffer.insert_text('import')
    async def run_kb(event=event):
        if paste_flag['p'] or edit_mode['E']:
            event.app.current_buffer.insert_text('    ')
        else:
            glb = False
            import_cmd = False
            try:
                buff_text_frst_cmd = event.app.current_buffer.document.text.split(' ')[
                                                                                  0]
                if buff_text_frst_cmd == 'import' or buff_text_frst_cmd == 'from':
                    import_cmd = True
                if ').' not in event.app.current_buffer.document.text:
                    buff_text = event.app.current_buffer.document.text.replace(
                        '=', ' ').replace('(', ' ').split(' ')[-1]
                else:
                    buff_text = event.app.current_buffer.document.text.replace(
                        '=', ' ').split(' ')[-1]
                if isinstance(buff_text, str):
                    if '.' in buff_text and not shell_mode['S']:

                        root_text = '.'.join(buff_text.split('.')[:-1])
                        rest = buff_text.split('.')[-1]
                        if rest != '':
                            await ble_cp.as_sh_repl("[val for val in dir({}) if val.startswith('{}')]".format(root_text, rest),
                                                    silent=True, follow=False, rtn=True, rtn_resp=True)
                            ble_cp.flush_conn()

                        else:
                            try:
                                await ble_cp.as_sh_repl('dir({});gc.collect()'.format(root_text),
                                                        silent=True, follow=False, rtn=True, rtn_resp=True)
                            except KeyboardInterrupt:
                                pass
                                # time.sleep(0.2)
                                # ble_cp.sh_repl('\x03', silent=True,
                                #                 traceback=True)

                            ble_cp.flush_conn()

                    else:
                        rest = ''
                        glb = True
                        cmd_ls_glb = 'dir()'
                        if buff_text != '':
                            cmd_ls_glb = "[val for val in dir() if val.startswith('{}')]".format(
                                buff_text)
                        if import_cmd:
                            fbuff_text = event.app.current_buffer.document.text.split()
                            if 'import' in fbuff_text and 'from' in fbuff_text and len(fbuff_text) >= 3:
                                if fbuff_text[1] not in frozen_modules['FM']:
                                    if len(fbuff_text) == 3:
                                        cmd_ls_glb = "import {0};dir({0});del(sys.modules['{0}'])".format(
                                            fbuff_text[1])
                                    if len(fbuff_text) == 4:
                                        cmd_ls_glb = "import {0};[val for val in dir({0}) if val.startswith('{1}')];del(sys.modules['{0}'])".format(
                                            fbuff_text[1], fbuff_text[3])
                                else:
                                    if len(fbuff_text) == 3:
                                        cmd_ls_glb = "import {0};dir({0})".format(
                                            fbuff_text[1])
                                    if len(fbuff_text) == 4:
                                        cmd_ls_glb = "import {0};[val for val in dir({0}) if val.startswith('{1}')]".format(
                                            fbuff_text[1], fbuff_text[3])
                            else:
                                cmd_ls_glb = "[scp.split('.')[0] for scp in os.listdir()+os.listdir('./lib') if '.py' in scp]"
                                frozen_modules['SUB'] = frozen_modules['FM']
                                if buff_text != '':
                                    cmd_ls_glb = "[scp.split('.')[0] for scp in os.listdir()+os.listdir('./lib') if '.py' in scp and scp.startswith('{}')]".format(
                                        buff_text)
                                    frozen_modules['SUB'] = [
                                        mod for mod in frozen_modules['FM'] if mod.startswith(buff_text)]

                        if shell_mode['S']:

                            if '/' in buff_text:
                                glb = False
                                dir_to_list = '/'.join(buff_text.split('/')[:-1])
                                cmd_ls_glb = "os.listdir('{}')".format(dir_to_list)
                                if buff_text.split('/')[-1] != '':
                                    cmd_ls_glb = "[val for val in os.listdir('{}') if val.startswith('{}')]".format(
                                        dir_to_list, buff_text.split('/')[-1])

                            else:
                                cmd_ls_glb = 'os.listdir()'
                                if buff_text != '':
                                    cmd_ls_glb = "[val for val in os.listdir() if val.startswith('{}')]".format(
                                        buff_text)

                        try:
                            await ble_cp.as_sh_repl(cmd_ls_glb+';gc.collect()',
                                                    silent=True, follow=False, rtn=True, rtn_resp=True)
                        except KeyboardInterrupt:
                            pass
                            # time.sleep(0.2)
                            # ble_cp.sh_repl('\x03', silent=True,
                            #                 traceback=True)
                        ble_cp.flush_conn()

                else:
                    root_text = buff_text.split('.')[0]
                    rest = buff_text.split('.')[1]
                    try:
                        await ble_cp.as_sh_repl('dir({});gc.collect()'.format(root_text),
                                                silent=True, follow=False, rtn=True, rtn_resp=True)
                    except KeyboardInterrupt:
                        # time.sleep(0.2)
                        # ble_cp.sh_repl('\x03', silent=True, traceback=True)
                        pass
                    ble_cp.flush_conn()
            except Exception as e:
                pass

            def tab_cmd_info(rest_part=rest, flag=glb, buff=buff_text):
                try:
                    if ble_cp.dev.output is not None:
                        ble_cp.dev.get_output()
                        if glb:
                            kw_line_buff = event.app.current_buffer.document.text.split()
                            if len(kw_line_buff) > 0 and len(kw_line_buff) <= 2:
                                if 'import' == kw_line_buff[0] or 'from' == kw_line_buff[0]:
                                    ble_cp.dev.output = ble_cp.dev.output + \
                                        frozen_modules['SUB']
                        if isinstance(ble_cp.dev.output, str):
                            # print(ble_cp.dev.output)
                            pass
                        else:
                            if rest != '':  # print attributes
                                result = [
                                    val for val in ble_cp.dev.output if val.startswith(rest)]
                                if len(result) > 1:
                                    comm_part = os.path.commonprefix(result)
                                    if comm_part == rest:
                                        print('>>> {}'.format(buff_text))
                                        print_table(result, autowide=True, sort=False)
                                    else:
                                        event.app.current_buffer.insert_text(
                                            comm_part[len(rest):])
                                else:
                                    event.app.current_buffer.insert_text(
                                        result[0][len(rest):])
                            else:
                                if not glb:
                                    if shell_mode['S']:  # print dirs/files
                                        result = [val for val in ble_cp.dev.output if val.startswith(
                                            buff_text.split('/')[-1])]
                                        if len(result) > 1:
                                            comm_part = os.path.commonprefix(result)
                                            if comm_part == buff_text.split('/')[-1]:
                                                if shell_mode['S']:
                                                    last_cmd = event.app.current_buffer.document.text
                                                    if local_path['p'] == '':
                                                        g_p = [val[1]
                                                               for val in prompt['p'][1:5]]
                                                        b_p = [val[1]
                                                               for val in prompt['p'][5:]]
                                                        color_p = "\33[32;1m{}\033[0m:\u001b[34;1m{}\033[0m{}".format(
                                                            "".join(g_p[:-1]), "".join(b_p), last_cmd)
                                                        print(color_p)
                                                    else:
                                                        m_p = [prompt['p'][0][1]]
                                                        g_p = [val[1]
                                                               for val in prompt['p'][1:5]]
                                                        b_p = [val[1]
                                                               for val in prompt['p'][5:]]
                                                        color_p = "\u001b[35;1m{}\033[0m\33[32;1m{}\033[0m:\u001b[34;1m{}\033[0m{}".format(
                                                            "".join(m_p), "".join(g_p[:-1]), "".join(b_p), last_cmd)
                                                        print(color_p)
                                                    # format ouput
                                                    print_table(
                                                        result, wide=28, format_SH=True)
                                            else:
                                                event.app.current_buffer.insert_text(
                                                    comm_part[len(buff_text.split('/')[-1]):])
                                        else:
                                            event.app.current_buffer.insert_text(
                                                result[0][len(buff_text.split('/')[-1]):])
                                    else:
                                        print('>>> {}'.format(buff_text))   # dir var
                                        print_table(ble_cp.dev.output, wide=16,
                                                    autocol_tab=True, sort=False)
                                else:
                                    result = [
                                        val for val in ble_cp.dev.output if val.startswith(buff_text)]
                                    if len(result) > 1:
                                        comm_part = os.path.commonprefix(result)
                                        if comm_part == buff_text:
                                            if shell_mode['S']:
                                                last_cmd = event.app.current_buffer.document.text
                                                if local_path['p'] == '':
                                                    g_p = [val[1]
                                                           for val in prompt['p'][1:5]]
                                                    b_p = [val[1]
                                                           for val in prompt['p'][5:]]
                                                    color_p = "\33[32;1m{}\033[0m:\u001b[34;1m{}\033[0m{}".format(
                                                        "".join(g_p[:-1]), "".join(b_p), last_cmd)
                                                    print(color_p)
                                                else:
                                                    m_p = [prompt['p'][0][1]]
                                                    g_p = [val[1]
                                                           for val in prompt['p'][1:5]]
                                                    b_p = [val[1]
                                                           for val in prompt['p'][5:]]
                                                    color_p = "\u001b[35;1m{}\033[0m\33[32;1m{}\033[0m:\u001b[34;1m{}\033[0m{}".format(
                                                        "".join(m_p), "".join(g_p[:-1]), "".join(b_p), last_cmd)
                                                    print(color_p)
                                                # format ouput
                                                print_table(
                                                    result, wide=28, format_SH=True)
                                            else:
                                                # globals
                                                print('>>> {}'.format(buff_text))
                                                print_table(result, wide=16,
                                                            autowide=True)
                                        else:
                                            event.app.current_buffer.insert_text(
                                                comm_part[len(buff_text):])
                                    else:
                                        event.app.current_buffer.insert_text(
                                            result[0][len(buff_text):])

                except Exception as e:
                    # print(e)
                    pass

            tab_cmd_info()
        return None

    async def f():
        async with in_terminal():
            await run_kb()

    loop = asyncio.get_event_loop()
    loop.create_task(f())


@kb.add('s-tab')
def shif_tab(event):
    "Autocomplete shell commands"
    def autocomplete_sh_cmd():
        if shell_mode['S']:
            buff_text = event.app.current_buffer.document.text
            result = [sh_cmd for sh_cmd in shell_commands +
                      custom_sh_cmd_kw if sh_cmd.startswith(buff_text)]
            if 'fw' in buff_text.split():
                # print('Here: {}'.format(buff_text.split()))
                if len(buff_text.split()) > 1:
                    result = [sh_cmd for sh_cmd in ['list', 'get', 'latest',
                                                    'update'] if sh_cmd.startswith(buff_text.split()[-1])]
                    # print(result)
                    buff_text = buff_text.split()[-1]
                else:
                    result = ['list', 'get', 'latest', 'update']
                    buff_text = ''
            if len(result) > 1:
                comm_part = os.path.commonprefix(result)
                if comm_part == buff_text:
                    last_cmd = buff_text
                    if local_path['p'] == '':
                        g_p = [val[1] for val in prompt['p'][1:5]]
                        b_p = [val[1] for val in prompt['p'][5:]]
                        color_p = "\33[32;1m{}\033[0m:\u001b[34;1m{}\033[0m{}".format(
                            "".join(g_p[:-1]), "".join(b_p), last_cmd)
                        print(color_p)
                    else:
                        m_p = [prompt['p'][0][1]]
                        g_p = [val[1] for val in prompt['p'][1:5]]
                        b_p = [val[1] for val in prompt['p'][5:]]
                        color_p = "\u001b[35;1m{}\033[0m\33[32;1m{}\033[0m:\u001b[34;1m{}\033[0m{}".format(
                            "".join(m_p), "".join(g_p[:-1]), "".join(b_p), last_cmd)
                        print(color_p)

                    print_table(result)
                else:
                    event.app.current_buffer.insert_text(
                        comm_part[len(buff_text):])
            else:
                if len(result) > 0:
                    event.app.current_buffer.insert_text(
                        result[0][len(buff_text):])

    run_in_terminal(autocomplete_sh_cmd)


@kb.add('s-right')
def autocomplete_locals(event):
    glb = False
    if shell_mode['S']:
        try:
            buff_text_frst_cmd = event.app.current_buffer.document.text.split(' ')[
                                                                              0]
            buff_text = event.app.current_buffer.document.text.split(' ')[-1]
            if isinstance(buff_text, str):
                if '/' in buff_text:
                    root_text = '/'.join(buff_text.split('/')[:-1])
                    rest = buff_text.split('/')[-1]
                    if shell_mode['S']:
                        cmd_ls_glb = os.listdir(root_text)
                else:
                    rest = ''
                    glb = True
                    if shell_mode['S']:
                            cmd_ls_glb = os.listdir()
            else:
                pass
        except Exception as e:
            pass

        def s_r_cmd_info(rest_part=rest, flag=glb, buff=buff_text, output=cmd_ls_glb):
            try:
                    if isinstance(cmd_ls_glb, str):
                        # print(ble_cp.dev.output)
                        pass
                    else:
                        if rest != '':
                            result = [
                                val for val in output if val.startswith(rest)]
                            if len(result) > 1:
                                comm_part = os.path.commonprefix(result)
                                if comm_part == rest:
                                    if shell_mode['S']:
                                        last_cmd = event.app.current_buffer.document.text
                                        if local_path['p'] == '':
                                            g_p = [val[1]
                                                   for val in prompt['p'][1:5]]
                                            b_p = [val[1]
                                                   for val in prompt['p'][5:]]
                                            color_p = "\33[32;1m{}\033[0m:\u001b[34;1m{}\033[0m{}".format(
                                                "".join(g_p[:-1]), "".join(b_p), last_cmd)
                                            print(color_p)
                                        else:
                                            m_p = [prompt['p'][0][1]]
                                            g_p = [val[1]
                                                   for val in prompt['p'][1:5]]
                                            b_p = [val[1]
                                                   for val in prompt['p'][5:]]
                                            color_p = "\u001b[35;1m{}\033[0m\33[32;1m{}\033[0m:\u001b[34;1m{}\033[0m{}".format(
                                                "".join(m_p), "".join(g_p[:-1]), "".join(b_p), last_cmd)
                                            print(color_p)
                                    print_table(result, wide=28, format_SH=True)
                                else:
                                    event.app.current_buffer.insert_text(
                                        comm_part[len(rest):])
                            else:
                                event.app.current_buffer.insert_text(
                                    result[0][len(rest):])
                        else:
                            if not glb:
                                if shell_mode['S']:
                                    result = [val for val in output if val.startswith(
                                        buff_text.split('/')[-1])]
                                    if len(result) > 1:
                                        comm_part = os.path.commonprefix(result)
                                        if comm_part == buff_text.split('/')[-1]:
                                            if shell_mode['S']:
                                                last_cmd = event.app.current_buffer.document.text
                                                if local_path['p'] == '':
                                                    g_p = [val[1]
                                                           for val in prompt['p'][1:5]]
                                                    b_p = [val[1]
                                                           for val in prompt['p'][5:]]
                                                    color_p = "\33[32;1m{}\033[0m:\u001b[34;1m{}\033[0m{}".format(
                                                        "".join(g_p[:-1]), "".join(b_p), last_cmd)
                                                    print(color_p)
                                                else:
                                                    m_p = [prompt['p'][0][1]]
                                                    g_p = [val[1]
                                                           for val in prompt['p'][1:5]]
                                                    b_p = [val[1]
                                                           for val in prompt['p'][5:]]
                                                    color_p = "\u001b[35;1m{}\033[0m\33[32;1m{}\033[0m:\u001b[34;1m{}\033[0m{}".format(
                                                        "".join(m_p), "".join(g_p[:-1]), "".join(b_p), last_cmd)
                                                    print(color_p)
                                                # format ouput
                                                print_table(
                                                    result, wide=28, format_SH=True)
                                        else:
                                            event.app.current_buffer.insert_text(
                                                comm_part[len(buff_text.split('/')[-1]):])
                                    else:
                                        event.app.current_buffer.insert_text(
                                            result[0][len(buff_text.split('/')[-1]):])
                                else:
                                    print_table(output, wide=28, format_SH=True)
                            else:
                                result = [
                                    val for val in output if val.startswith(buff_text)]
                                if len(result) > 1:
                                    comm_part = os.path.commonprefix(result)
                                    if comm_part == buff_text:
                                        if shell_mode['S']:
                                            last_cmd = event.app.current_buffer.document.text
                                            if local_path['p'] == '':
                                                g_p = [val[1]
                                                       for val in prompt['p'][1:5]]
                                                b_p = [val[1]
                                                       for val in prompt['p'][5:]]
                                                color_p = "\33[32;1m{}\033[0m:\u001b[34;1m{}\033[0m{}".format(
                                                    "".join(g_p[:-1]), "".join(b_p), last_cmd)
                                                print(color_p)
                                            else:
                                                m_p = [prompt['p'][0][1]]
                                                g_p = [val[1]
                                                       for val in prompt['p'][1:5]]
                                                b_p = [val[1]
                                                       for val in prompt['p'][5:]]
                                                color_p = "\u001b[35;1m{}\033[0m\33[32;1m{}\033[0m:\u001b[34;1m{}\033[0m{}".format(
                                                    "".join(m_p), "".join(g_p[:-1]), "".join(b_p), last_cmd)
                                                print(color_p)

                                            # format ouput
                                            print_table(
                                                result, wide=28, format_SH=True)
                                        else:
                                            print('>>> {}'.format(buff_text))
                                            print_table(
                                                result, wide=28, format_SH=True)
                                    else:
                                        event.app.current_buffer.insert_text(
                                            comm_part[len(buff_text):])
                                else:
                                    event.app.current_buffer.insert_text(
                                        result[0][len(buff_text):])

            except Exception as e:
                pass
        run_in_terminal(s_r_cmd_info)

#


@kb.add('s-left')
def toggle_local_path(event):
    if shell_mode['S']:
        if show_local_path['s']:
            show_local_path['s'] = False
            local_path['p'] = ''

            # SET ROOT USER PATH:
            shell_message = [
                ('class:userpath',    local_path['p']),
                ('class:username', dev_platform),
                ('class:at',       '@'),
                ('class:host',     host_name),
                ('class:colon',    ':'),
                ('class:path',     '~{}'.format(dev_path['p'])),
                ('class:pound',    '$ '),
            ]
            shell_prompt['s'] = shell_message
            prompt['p'] = shell_prompt['s']
        else:
            show_local_path['s'] = True
            local_path['p'] = os.getcwd().split('/')[-1]+':/'
            if os.getcwd() == os.environ['HOME']:
                local_path['p'] = '~:/'

            # SET ROOT USER PATH:
            shell_message = [
                ('class:userpath',    local_path['p']),
                ('class:username', dev_platform),
                ('class:at',       '@'),
                ('class:host',     host_name),
                ('class:colon',    ':'),
                ('class:path',     '~{}'.format(dev_path['p'])),
                ('class:pound',    '$ '),
            ]
            shell_prompt['s'] = shell_message
            prompt['p'] = shell_prompt['s']
#
#
# # @kb.add('c-u')
# # def unsecure_mode(event):
# #     "Toggle send unencrypted commands"
# #     if not args.nem:
# #         status_encryp_msg['S'] = True
# #         status_encryp_msg['Toggle'] = False
# #         if encrypted_flag['sec']:
# #             encrypted_flag['sec'] = False
# #             ble_cp.dev.output = None
# #             ble_cp.sw_repl()
# #
# #             def warning_us():
# #                 print(CRED + 'WARNING: ENCRYPTION DISABLED' + CEND)
# #             # run_in_terminal(warning_us)
# #         else:
# #             encrypted_flag['sec'] = True
# #             ble_cp.dev.output = None
# #             ble_cp.sw_repl()
# #             ble_cp.dev.output = None
# #
# #             def warning_sec():
# #                 print(CGREEN + 'INFO: ENCRYPTION ENABLED' + CEND)
# #         # run_in_terminal(warning_sec)
#


@kb.add('c-p')
def toggle_status_ram_msg(event):
    "Toggle Right RAM status"
    if status_encryp_msg['Toggle']:
        if status_encryp_msg['S']:
            status_encryp_msg['S'] = False
        else:
            status_encryp_msg['S'] = True

    async def run_kb():
        ble_cp.dev.output = None
        output = await ble_cp.as_sh_repl('from micropython import mem_info;mem_info()',
                                         silent=True, follow=False, rtn=True, rtn_resp=True)

        if not mem_show_rp['show']:
            mem_show_rp['show'] = True
            mem_show_rp['call'] = True
            RAM = output
            try:
                mem_info = RAM.replace('\n', '').replace(
                    'GC', '\nGC').replace('No', '\nNo').splitlines()[1]
                mem = {elem.strip().split(':')[0]: int(elem.strip().split(':')[
                                  1]) for elem in mem_info[4:].split(',')}
                used_mem = mem['used']/1024
                free_mem = mem['free']/1024
                used_mem_s = "{:.3f} KB".format(used_mem)
                free_mem_s = "{:.3f} KB".format(free_mem)
                # set used and free
                mem_show_rp['used'] = used_mem_s
                mem_show_rp['free'] = free_mem_s
                total_mem = mem['total']/1024
                use_percent = round((used_mem/total_mem)*100, 2)
                mem_show_rp['percent'] = use_percent
            except Exception as e:
                print(e)
        else:
            mem_show_rp['show'] = False

        return None

    async def f():
        async with in_terminal():
            await run_kb()

    loop = asyncio.get_event_loop()
    loop.create_task(f())


# @kb.add('c-g')
# def listen_SHELL_REPL(event):
#     "Echo SHELL_REPL --> Active listening for timer/hardware interrupts"
#     def echo_ouput():
#         try:
#             buff_text = "import time\nwhile True:\n    time.sleep(1)"
#             ble_cp.paste_buff(buff_text)
#             ble_cp.sh_repl("\x04", follow=True)
#         except KeyboardInterrupt:
#             time.sleep(0.2)
#             ble_cp.sh_repl('\x03', silent=True,
#                             traceback=True)  # KBI
#             time.sleep(0.2)
#             for i in range(1):
#                 ble_cp.sh_repl('\x0d', silent=True)
#                 ble_cp.flush_conn()
#             pass
#         ble_cp.dev.output = None
#         time.sleep(1)
#         ble_cp.flush_conn()
#         ble_cp.flush_conn()
#         event.app.current_buffer.reset()
#     run_in_terminal(echo_ouput)
#


def check_prompt():
    if ble_cp.dev.cmd_finished:
        return prompt['p']
    else:
        return ''


session_p = PromptSession()
# REPL/SHELL LOOP
repl = True
while repl:
    try:
        ble_cp.dev.output = None
        if exit_flag['exit']:
            break
        else:
            inp = session_p.prompt(check_prompt,
                                   auto_suggest=ConditionalAutoSuggest(AutoSuggestFromHistory(),
                                                                       autosuggest_is_on),
                                   key_bindings=kb, style=style_p,
                                   multiline=edit_mode['E'] or paste_flag['p'],
                                   rprompt=get_rprompt,
                                   refresh_interval=0.2)
            status_encryp_msg['S'] = False
            status_encryp_msg['Toggle'] = True
            if inp is not None and inp != '' and not paste_flag['p']:
                # HERE IN SHELL MODE PROCESS INPUT BEFORE SENDING # shlex.split
                if shell_mode['S']:  # SHELL
                    pass
                    inp_args = shlex.split(inp)
                    if inp_args[0] in custom_sh_cmd_kw:
                        custom_sh_cmd(inp)
                    else:
                        shell_inp = inp
                        inp, frst_cmd = map_upysh(inp)
                        if frst_cmd == 'run':
                            ble_cp.dev.cmd_finished = False

                            async def frepl():
                                output = await ble_cp.as_sh_repl(inp, follow=True,
                                                                 rtn=True,
                                                                 rtn_resp=True)
                                ble_cp.dev.cmd_finished = True
                                # async with in_terminal():
                                #     output = await ble_cp.as_sh_repl(inp, follow=True,
                                #                                      rtn=True,
                                #                                    rtn_resp=True)
                                return None

                            try:
                                loop = asyncio.get_event_loop()
                                asyncio.ensure_future(frepl(), loop=loop)
                            finally:
                                pass

                        else:
                            if inp == 'ls()' or frst_cmd == 'cat' or frst_cmd == 'tree' or 'help' in frst_cmd:
                                ble_cp.sh_repl(inp+';gc.collect()', follow=True)
                                ble_cp.flush_conn()
                            else:
                                if frst_cmd not in shell_commands:
                                    custom_sh_cmd(shell_inp)
                                else:
                                    ble_cp.sh_repl(inp)
                        if frst_cmd == 'cd':
                            ble_cp.dev.output = None
                            ble_cp.sh_repl('pwd', silent=True)
                            if ble_cp.dev.output is not None:
                                # print(ble_cp.dev.output)
                                devpath = ble_cp.dev.output.replace('\n', '')
                                dev_path['p'] = devpath
                                if devpath == '/':
                                    devpath = ' '
                                    dev_path['p'] = ' '
                                shell_message = [
                                    ('class:userpath',    local_path['p']),
                                    ('class:username', dev_platform),
                                    ('class:at',       '@'),
                                    ('class:host',     host_name),
                                    ('class:colon',    ':'),
                                    ('class:path',     '~{}'.format(devpath)),
                                    ('class:pound',    '$ '),
                                ]
                                shell_prompt['s'] = shell_message
                                prompt['p'] = shell_prompt['s']
                else:  # REPL
                    ble_cp.dev.cmd_finished = False

                    async def frepl():
                        output = await ble_cp.as_sh_repl(inp, follow=True,
                                                         rtn=True,
                                                         rtn_resp=True)
                        ble_cp.dev.cmd_finished = True
                        # async with in_terminal():
                        #     output = await ble_cp.as_sh_repl(inp, follow=True,
                        #                                      rtn=True,
                        #                                    rtn_resp=True)
                        return None

                    try:
                        loop = asyncio.get_event_loop()
                        asyncio.ensure_future(frepl(), loop=loop)
                    finally:
                        pass

                        # ble_cp.sh_repl(inp, follow=True)
                        # ble_cp.sh_repl(inp, follow=True, kb=True)
                        # if ble_cp.dev.output is not None:
                        #     print(ble_cp.dev.output)
                        # time.sleep(0.2)
                        # ble_cp.sh_repl('\x03', silent=True,
                        #                traceback=True)  # KBI
                        # print('Catched here 2')
                        # print('BUFF: {}'.format(ble_cp.dev.raw_buff))
                        # print('IS NOTIFYING: {}'.format(ble_cp.dev.is_notifying))
                        # ble_cp.dev.repl_kbi()
                        # print('IS NOTIFYING: {}'.format(ble_cp.dev.is_notifying))
                        # time.sleep(0.2)
                        # for i in range(1):
                        #     ble_cp.sh_repl('\x0d', silent=True)
                        #     ble_cp.flush_conn()
                    pass

                if inp != '':  # PASTE BUFFER
                    pass
                    # paste_buffer['B'].append(inp)
    except Exception as e:
        print(e)
        continue
    except KeyboardInterrupt:
        try:
            ble_cp.flush_conn()
            # if hasattr(SERIAL_TOOL, 'serv_soc'):
            #     ble_htool.stop_SOC()
            continue
        except Exception as e:
            print(e)
            continue
    except EOFError:
            # print('This is EOF ERROR!')
            continue
        # espdev.reset()
        # sys.exit()

# sys.exit()
# EXIT MESSAGE
if ble_cp.dev.connected and ble_cp.dev.is_connected():

    ble_cp.dev.disconnect()
print('logout')
if host_name != unique_id:
    print('Connection to {} closed.'.format(host_name))
else:
    print('Connection to {} closed.'.format(ble_cp.dev.name))
