import re
import os
from subprocess import call
from clint.textui import colored, puts, puts_err, indent, progress

def command_out(message):
    """
        Used to print shell commands to stderr
    """
    puts_err(colored.green(message))
    

def message(message, n_indent = 4, color = "blue"):
    with indent(n_indent):
        if color == "blue":
            puts_err(colored.blue('\n' + message + '\n'))
        elif color == "red":
            puts_err(colored.red('\n' + message + '\n'))


def boolify(s):
    if s == 'True':
        return True
    if s == 'False':
        return False
    raise ValueError("huh?")

def autoconvert(s):
    for fn in (boolify, int, float):
        try:
            return fn(s)
        except ValueError:
            pass
    return s

def parse_region(region):
    return re.split("[:-]+", region)


# Stack Overflow: 377017
def which(program):
    def is_exe(fpath):
        return os.path.isfile(fpath) and os.access(fpath, os.X_OK)

    fpath, fname = os.path.split(program)
    if fpath:
        if is_exe(program):
            return program
    else:
        for path in os.environ["PATH"].split(os.pathsep):
            path = path.strip('"')
            exe_file = os.path.join(path, program)
            if is_exe(exe_file):
                return exe_file

    return None

def check_program_exists(program):
    if which(program) is None:
        exit(puts_err(colored.red("\n\t" + program + " not installed or on PATH.\n")))


def run_command(comm, shell = True):
    """
        Runs a shell command
    """
    sh_out = ' '.join(comm) if type(comm) == list else comm
    command_out(sh_out)
    out = call(comm, shell = shell)
    if out != 0:
        raise Exception(f"Error [{out}] running {sh_out}")
    return out


# Levenshtein edit distnace
# https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#Python
def lev(s1, s2):
    if len(s1) < len(s2):
        return lev(s2, s1)

    # len(s1) >= len(s2)
    if len(s2) == 0:
        return len(s1)

    previous_row = list(range(len(s2) + 1))
    for i, c1 in enumerate(s1):
        current_row = [i + 1]
        for j, c2 in enumerate(s2):
            insertions = previous_row[j + 1] + 1 # j+1 instead of j since previous_row and current_row are one character longer
            deletions = current_row[j] + 1       # than s2
            substitutions = previous_row[j] + (c1 != c2)
            current_row.append(min(insertions, deletions, substitutions))
        previous_row = current_row
    
    return previous_row[-1]

