import os
import sys
import shutil
import xml.sax
import subprocess
from xml.sax import handler
from typing import Dict, Any, Optional, List, Union

class SunhpcException(Exception):
    'Base class for Sunhpc exceptions.'
    pass

class HostnotfoundException(SunhpcException):
    'Host not found exception.'
    pass

class ParameterNotValid(SunhpcException):
    'Parameter not valid exception.'
    pass

class CommandError(SunhpcException):
    'Command error exception.'
    pass

# An exception for Kickstart builder trinity: kcgi, kgen, kp perrors.
class KickstartError(SunhpcException):
    pass
class KickstartGraphError(KickstartError):
    pass
class KickstartNodeError(KickstartError):
    pass

class Struct:
    pass

def escapeAttr(value):
    if value is None:
        return value
    return xml.sax.saxutils.escape(value, { 
        "\"": "&quot;", 
        "%": "&#x0025i;",
        "'": "\\'"
    })

def unescapeAttr(value):
    if value is None:
        return value
    return xml.sax.saxutils.unescape(value, {
        "&quot;": "\"",
        "&#x0025;": "%"
    })

def escapeStringForShell(value):
    return value.replace('\"', '\\\"')

def str2bool(s):
    if s and s.upper() in [ 'ON', 'YES', 'Y', 'TRUE', '1', 'ENABLE', 'ENABLED']:
        return True
    else:
        return False

def bool2str(b):
    if b:
        return 'yes'
    else:
        return 'no'

def list2str(l):
    s = ''
    for e in l:
        s = s + e
    return s

def listcmp(l1, l2):
    return map(lambda x, y: x == y, l1, l2)

def listdup(e, n):
    l = []
    for i in range(0, n):
        l.append(e)
    return l

def list_isprefix(l1, l2):
    l = listcmp(l1, l2)
    if all(l):
        return True
    else:
        return False
def list_issuffix(l1, l2):
    l = listcmp(l1, l2)
    for i in range(0, len(l)):
        if not l[i]:
            return False
    return True

def getNativeArch():
    arch = os.uname()[4]
    if arch in [ 'i386', 'i686']:
        return 'i386'
    return arch

def getTerminalWidth():
    get_terminal_size = getattr(os, 'get_terminal_size', shutil.get_terminal_size)
    try:
        width = get_terminal_size().columns
    except Exception:
        width = 80

    return width

def mkdir(newdir):
    if os.path.isdir(newdir):
        pass
    elif os.path.isfile(newdir):
        raise OSError("a file with the same name as the desired dir '%s', already exists." % newdir)
    else:
        head, tail = os.path.split(newdir)
        if head and not os.path.isdir(head):
            mkdir(head)
        if tail:
            os.mkdir(newdir)

class ParseXML(
    handler.ContentHandler, handler.DTDHandler,
    handler.EntityResolver, handler.ErrorHandler):

    '''A helper class to for XML parsers. Uses our
    startElement_name style.
    '''
    def __init__(self, app=None):
        handler.ContentHandler.__init__(self)
        self.app = app
        self.text = ''

    def startElement(self, name, attrs):
        try:
            f = getattr(self, "startElement_" + name)
            f(name, attrs)
        except AttributeError:
            return

    def endElement(self, name):
        try:
            f = getattr(self, "endElement_" + name)
            f(name)
        except AttributeError:
            return

    def characters(self, content: str) -> None:
        self.text += content

def system(cmd, type='standard'):
    if type == 'spinner':
        return startSpinner(cmd)
    else:
        return subprocess.call(cmd, shell=True)

def startSpinner(cmd):
    
    p = subprocess.Popen(cmd, shell=True,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            close_fds=True)
    w, r, e = (p.stdin, p.stdout, p.stderr)
    currLength = 0
    prevLength = 0
    spinChars = '|/-\\'
    spinIndex = 0
    while True:
        line = e.readline()
        if not line:
            break
        if isinstance(line, bytes):
            line = line.decode('utf-8', errors='replace')
        if len(line) > 79:
            data = line[0:78]
        else:
            data = line[:-1]
        currLength = len(data)
        pad = ''
        for i in range(0, prevLength - currLength):
            pad = pad + ' '
        spin = spinChars[spinIndex % len(spinChars)]
        spinIndex = spinIndex + 1
        print (spin + data + pad + '\r', end='', flush=True)
        prevLength = currLength
    w.close()
    r.close()
    e.close()
    pad = ''
    for i in range(0, 78):
        pad = pad + ' '
    print ('\r%s\r' % pad, end='', flush=True)

