#-*- coding: utf-8 -*-

# Copyright 2012 Calculate Ltd. http://www.calculate-linux.org
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import time, os, sys, re
from fcntl import ioctl
from array import array
import termios
import subprocess
from OpenSSL import crypto
import shlex
from sudsds import MethodNotFound
from calculate.core.server.cert_cmd import getHwAddr, getIpLocal
from calculate.core.server.replace_class import printTable,Methods

from calculate.core.client.progressbar import Bar,Percentage,ETA,ProgressBar
from calculate.lib.cl_lang import setLocalTranslate
from calculate.lib.cl_print import color_print
setLocalTranslate('cl_console3',sys.modules[__name__])
from calculate.core.client.function import create_obj

green = '\033[32m * \033[0m'
red = '\033[31m * \033[0m'

class _color_print(color_print):
    def _printSUCCESS(self, string, offsetL=0, printBR=True):
        self.printSUCCESS(string.encode('utf-8'), offsetL, printBR)

colorPrint = _color_print()

def _print (*args):
    print " ".join(map(lambda x:unicode(x).encode('utf-8'),args))

# get list of certificate and session id
def get_sid (SID_FILE):
    if not os.path.exists(SID_FILE):
        fi = open(SID_FILE, 'w')
        fi.write('0')
        fi.close()
        sid = 0
    else:
        fi = open(SID_FILE, 'r')
        sid = fi.read()
        fi.close()
    return sid

def clear ():
    """ delete caching suds file """
    try:
        import glob
        for filename in glob.glob("/tmp/suds/suds-*"):
            #print "delete", filename
            try:
                os.unlink (filename)
            except OSError, e:
                _print (e.message)
    except:
        print _("Failed to clear the cache! ")
        return 1
    
def get_ip_global():
    import urllib2

    strURL = 'http://api.wipmania.com/'
    f = urllib2.urlopen(urllib2.Request(strURL))
    response = f.read()
    outerIP = response.split("<br>")[0]
    f.close()
    return outerIP

def get_ip_mac_type(client_type = None):
    results = []
    #try:
    results.append ( getIpLocal() )
    #except:
        #results.append ('no_ip')
    #try:
    results.append ( getHwAddr())
    #except:
        #results.append ('no_mac')
    if client_type:
        results.append (client_type)
    else:
        results.append ('console')
    return results

def print_brief_group(Fields, group_name):
    print_group_flag = False
#    if group_name:
#        _print ('\b'+group_name)
    uncompatible_count = 0
    for field in Fields:
        if field.uncompatible:
            uncompatible_count += 1
            continue
        if field.element in ['input', 'openfile']:
            value = field.value if field.value else ''
            if not print_group_flag:
                _print ('\b'+group_name)
                print_group_flag = True
            colorPrint._printSUCCESS('%s: %s' %(field.label, value))

        elif field.element in ['combo', 'comboEdit', 'radio', 'file']:
            if hasattr (field.comments, 'string') and field.value in \
                                                      field.choice.string:
                value = map(lambda x: field.comments.string[x] \
                        if len(field.comments.string) > x \
                        else field.choice.string[x], 
                            map(lambda x: field.choice.string.index(x), \
                                [field.value]))
                value = ', '.join(value)
            else:
                value = field.value if field.value else ''
            if not print_group_flag:
                _print ('\b'+group_name)
                print_group_flag = True
            colorPrint._printSUCCESS('%s: %s' %(field.label, value))

        elif field.element in ['multichoice', 'multichoice_add',\
                                 'selecttable', 'selecttable_add']:
            if hasattr (field.comments, 'string') and \
                                    hasattr (field.listvalue, 'string'):
                value = map(lambda x: field.comments.string[x] \
                        if len(field.comments.string) > x \
                        else field.choice.string[x], 
                            map(lambda x: field.choice.string.index(x), \
                                field.listvalue.string))
                value = ', '.join(value)
            elif hasattr (field.listvalue, 'string'):
                value = ', '.join(field.listvalue.string)
            else:
                value = field.value if field.value else ''
            if not print_group_flag:
                _print ('\b'+group_name)
                print_group_flag = True
            colorPrint._printSUCCESS('%s: %s' %(field.label, value))

#        elif field.element == 'label':
#            print field.label

        elif field.element == 'error':
            if not print_group_flag:
                _print ('\b'+group_name)
                print_group_flag = True
            colorPrint.printERROR(field.label)

        elif field.element in ['check', 'check_tristate']:
            if field.value == 'on':
                value = _('yes')
            elif field.value == 'off':
                value = _('no')
            elif field.value == 'auto':
                value = _('auto')
            else:
                value = field.value
            if not print_group_flag:
                _print ('\b'+group_name)
                print_group_flag = True
            colorPrint._printSUCCESS('%s: %s' %(field.label, value))

        elif field.element == 'table' and field.type != 'steps':
            if hasattr (field.tablevalue.head, 'string'):
                head = field.tablevalue.head.string
            else: head = None

            body = []
            if hasattr (field.tablevalue.body, 'stringArray'):
                for row in field.tablevalue.body.stringArray:
                    if hasattr(row, 'string'):
                        body.append(row.string)
            else: body = [[]]

            # if empty table
            if not filter (None, map(lambda x: x, body)):
                body = [['']*len(head)]
                res = printTable(body, head)
                sys.stdout.flush()
                sys.stdout.write(res)
                continue

            ChoiceValue = field.tablevalue.values.ChoiceValue
            for row in xrange(len(ChoiceValue)):
                if ChoiceValue[row].typefield in ['check', 'check_tristate']:
                    for i in xrange(len(body)):
                        if body[i][row] == 'on':
                            body[i][row] = _('yes')
                        if body[i][row] == 'off':
                            body[i][row] = _('no')
                        if body[i][row] == 'auto':
                            body[i][row] = _('auto')
                if ChoiceValue[row].typefield == 'password':
                    for i in xrange(len(body)):
                        if body[i][row]:
                            body[i][row] = '***'

            data = []
            for body_row in body:
                data.append(map(lambda x: x if x else '', body_row))

            if not print_group_flag:
                _print ('\b'+group_name)
                print_group_flag = True
            colorPrint._printSUCCESS('%s: ' %(field.label))
            res = printTable(data, head)
            sys.stdout.write(res+"\n")
            sys.stdout.flush()

        else:
            uncompatible_count += 1

#    if uncompatible_count == len (Fields) and group_name:
#        colorPrint._printSUCCESS(_('Not used'))

def print_brief(view, brief_label):
    for Group in view.groups.GroupField:
        if Group.name:
            if not Group.fields:
                continue
        print_brief_group(Group.fields.Field, Group.name)

class switch(object):
    def __init__(self, value):
        self.value = value
        self.fall = False

    def __iter__(self):
        """Return the match method once, then stop"""
        yield self.match
        raise StopIteration
    
    def match(self, *args):
        """Indicate whether or not to enter a case suite"""
        if self.fall or not args:
            return True
        elif self.value in args: # changed for v1.5, see below
            self.fall = True
            return True
        else:
            return False

#################API FUNCTION###############################
def show_view(view):
    return
    print "+====== show view! ======+"

def show_table(table, item):
    if item.message:
        colorPrint._printSUCCESS(item.message)
    head = table.head.string if hasattr (table.head, 'string') else None
    data = []
    for line in table.body[0]:
        if hasattr (line, 'string'):
            data.append(line.string)

    res = printTable(data, head)
    sys.stdout.write(res+"\n")
    sys.stdout.flush()

def show_error(item):
    if item.message:
        for line in item.message.splitlines():
            colorPrint.printERROR(line)
    
def show_warning(item):
    if item.message:
        for line in item.message.splitlines():
            colorPrint.printWARNING(line)
    
def show_group(item):
    if item.message:
        for line in item.message.splitlines():
            colorPrint._printSUCCESS(line)

def show_result(result):
    pass

def startTask(item):
    if item.message:
        for line in item.message.splitlines():
            colorPrint._printSUCCESS(line)

def endTask(item):
    if item.message:
        for line in item.message.splitlines():
            colorPrint._printSUCCESS(line)
    
def beginFrame(item):
    pass

def endFrame(item):
    pass

def startGroup(item):
    pass

def endGruop(item):
    pass

def _create_obj(client, method):
    try:
        view_params = create_obj(client, method)
    except MethodNotFound:
        if method.endswith('_view'):
            method = method[:-5]
        _print (_('Method not found: ') + method)
        raise Exception(1)
    return view_params

def get_view_params(client, method, step = None, expert = None, brief = None, 
                    onlyhelp = False):
    view_params = _create_obj(client, method)
    view_params.step = step
    view_params.expert = expert
    view_params.brief = brief
    view_params.onlyhelp = onlyhelp
    return view_params

def callView(client, item, sid):
    return
    _print ("\n",item.message)
    try:
        view_params = get_view_params(client, item.message, brief = True, \
                                      expert = True)
        view = client.service[0][item.message] (sid, view_params)
        show_view(view)
    except:
        pass

#################MESSAGE####################################
def analysis(client, sid, s):
    """ analysis of the bounced message method """
    messages = s[0]
    for mess in messages:
        if mess.type == 'pid':
            try:
                pid = int(mess.message)
            except:
                show_error(_('the server sent PID = ') + pid)
                return 1
            get_messages(client, sid, pid)
        elif mess.type == 'error':
            show_error(mess)
        elif mess.type == 'warning':
            show_warning(mess)

def get_message(client, item, sid, pid):
    """ get one message by its type """
    for case in switch(item.type):
        if case('normal'):
            if item.message:
                Methods().printSUCCESS(item.message)
            return 1
        if case('plain'):
#            sys.stdout.flush()
#            sys.stdout.write('\x1b[1;33m' + item.message + '\033[0m')
            if item.message:
                p = re.compile(r'<b>|</b>')
                print p.sub('', item.message)
#            print item.message
            return 1
        if case('pre'):
            Methods().printPre(item.message)
            return 1
        if case('choice'):
            message,answers = item.message.split('|')
            answers = map(lambda x:(x[0],x[1].strip(')')),
                      map(lambda x:x.split('('),
                      answers.split(',')))
            answer = Methods().askChoice(message,answers)
            client.service.send_message(sid, pid, answer)
            return 1
        if case('progress'):
            if not client.no_progress:
                get_Progress(client, sid, pid, item.id)
            return 1
        if case('error'):
            show_error(item)
            if item.message == "403 Forbidden":
                return 0
            return 1
        if case('warning'):
            show_warning(item)
            return 1
        if case('table'):
            get_Table(client, sid, pid, item)
            return 1
        if case('group'):
            show_group(client, sid, pid, item)
            return 1
        if case('question'):
            send_Message(client, sid, pid, item)
            return 1
        if case('confirm'):
            send_Confirm(client, sid, pid, item)
            return 1
        if case('password'):
            send_Password(client, sid, pid, item)
            return 1
        if case('startTask'):
            startTask(item)
            return 1
        if case('endTask'):
            endTask(item)
            return 1
        if case('beginFrame'):
            beginFrame(item)
            return 1
        if case('endFrame'):
            endFrame(item)
            return 0
        if case('startGroup'):
            startGroup(item)
            return 1
        if case('endGruop'):
            endGruop(item)
            return 1
        if case('briefParams'):
            callView(client, item, sid)
        if case(): # default, could also just omit condition or 'if True'
            return 1

def get_messages(client, sid, pid):
    """ get frame in a separate thread """
    #thread_messages = threading.Thread(target=get_Frame,\
                                       #args = (client, sid, pid))
    #thread_messages.start()
    get_Frame(client, sid, pid)


def get_Frame(client, sid, pid):
    """ get all messages, until type is not endFrame (or Error) """
    end_frame = 1
    while end_frame:
        current_frame = client.service[0].get_frame(sid, pid, "console")
        while current_frame in [None, [], ""]:
            time.sleep(float(client.frame_period))
            current_frame = client.service[0].get_frame(sid, pid, "console")
        for item in current_frame[0]:
            end_frame = get_message(client, item, sid, pid)
            
def get_entire_frame(client, pid):
    """ get entire frame, from beginning (if client disconnected) """
    sid = get_sid(client.SID_FILE)
    list_pid = client.service.list_pid(sid = sid)
    if hasattr (list_pid, 'integer'):
        if not pid in list_pid.integer:
            print \
             _('The process does not exist or does not belong to your session')
#    if list_pid[0] == [0]:
#        return 0
#    for pid in list_pid[0]:
    end_frame = 1
    while end_frame:
        current_frame = client.service.get_entire_frame(sid, pid)
        while current_frame in [None, [], ""]:
            time.sleep(1)
            current_frame = client.service.get_frame(sid, pid, "console")
        for item in current_frame[0]:
            end_frame = get_message(client, item, sid, pid)

def get_Progress(client, sid, pid, id):
    widgets = ['','', Bar(), '', Percentage(),' ', ETA()]
    pbar = ProgressBar(widgets=widgets, maxval=100)
    # maybe do something
    pbar.start()

    """ get progress for the current job """
    returnProgr = client.service.get_progress(sid, pid, id)
    temp_progress = -1
    last_message = ''
    percent = returnProgr.percent
    while percent <= 100 and percent >= 0:
        if temp_progress != percent:
            last_message = print_progressbar(returnProgr, pbar,
                                          last_msg = last_message)
#            pbar.update(returnProgr.percent)
            if percent == 100:
                print
                return
            temp_progress = percent
        else:
            pbar.update(percent)
        time.sleep(1)
        returnProgr = client.service.get_progress(sid, pid, id)
        percent = returnProgr.percent
    if percent < 0:
#        print_progress(returnProgr, error = True)
        pbar.update(0)# - returnProgr.percent)
        pbar.finish()
    else:
#        print_progress(returnProgr)
        pbar.update(100)#returnProgr.percent)
        pbar.finish()

def cout_progress(string):
    h,w=array('h', ioctl(sys.stderr,termios.TIOCGWINSZ,'\0'*8))[:2]
    sys.stdout.write('\r' + (' '*(w)))
    sys.stdout.write('\r' + string)
    sys.stdout.flush()

def cout(string):
    sys.stdout.write(string)
    sys.stdout.flush()

def print_progressbar(returnProgr, pbar, last_msg = None, error = False):
    if returnProgr.long_message:
        if last_msg != returnProgr.long_message:
            colorPrint._printSUCCESS('%s\n' %returnProgr.long_message)
        pbar.update(returnProgr.percent)
        return returnProgr.long_message
    elif returnProgr.short_message:
        if last_msg != returnProgr.short_message:
            colorPrint._printSUCCESS('%s\n' %returnProgr.short_message)
        pbar.update(returnProgr.percent)
        return returnProgr.short_message
    else:
        pbar.update(returnProgr.percent)
        return last_msg

def print_progress(returnProgr, last_msg = None, error = False):
    if error:
        cout_progress (red + '\n'+_("Task error by %s") \
                            %str(0 - returnProgr.percent).rjust(5) + '%\n')
        return ''
    elif returnProgr.long_message:
        if last_msg == returnProgr.long_message:
            cout_progress('%s%%' %str(returnProgr.percent).rjust(5))
        else:
            if not last_msg:
                cout_progress('')
            else:
                cout_progress('OK'.rjust(6) + '\n')
            cout_progress(green + '%s %s%%' %(returnProgr.long_message, \
                                   str(returnProgr.percent).rjust(5)))
        return returnProgr.long_message
    elif returnProgr.short_message:
        if last_msg == returnProgr.short_message:
            cout_progress('%s%%' %str(returnProgr.percent).rjust(5))
        else:
            if not last_msg:
                cout_progress('')
            else:
                cout_progress('OK'.rjust(6) + '\n')
            cout_progress(green + '%s %s%%' %(returnProgr.short_message, \
                                   str(returnProgr.percent).rjust(5)))
        return returnProgr.short_message
    else:
#        print '%s' %str(returnProgr.percent).rjust(5) + '%'
        cout_progress ('%s' %str(returnProgr.percent).rjust(5) + '%')
        return ''

def get_Table(client, sid, pid, item):
    table = client.service.get_table(sid, pid, item.id)
    show_table(table, item)

def send_Confirm(client,sid,pid,item):
    print
    ask = ""
    while not ask and not client.no_questions:
        try:
            ask = raw_input(item.message+ '(yes/no): ')
        except KeyboardInterrupt:
            ask = 'no'
            print
        if ask.lower() in ['n', 'no']:
            ask = "no"
        elif ask.lower() in ['y', 'yes']:
            ask = "yes"
        else:
            ask = ""
    client.service.send_message(sid, pid, ask)

def send_Message(client, sid, pid, item):
    """ send answer to the question """
    print
    answer = raw_input (item.message)
    client.service.send_message(sid, pid, answer)
#    show_result(result)
    
def send_Password(client, sid, pid, item):
    """ send password """
    from getpass import getpass
    password = getpass(prompt=item.message)
    result = client.service.send_message(sid, pid, password)
    show_result(result)

def _return_revoked_serials(self, crlfile):
    try:
        serials = []
        crltext = open(crlfile, 'r').read()
        crl = crypto.load_crl(crypto.FILETYPE_PEM, crltext)
        revs = crl.get_revoked()
        for revoked in revs:
            serials.append(str(revoked.get_serial()))
        return serials
    except (ImportError, AttributeError):
        call = '/usr/bin/openssl crl -text -noout -in %s' % crlfile
        call = shlex.split(call)
        serials = []
        (res,err)=subprocess.Popen(call, stdout=subprocess.PIPE).communicate()
        for line in res.split('\n'):
            if line.find('Serial Number:') == -1:
                continue
            (crap, serial) = line.split(':')
            serial = serial.strip()
            serial = int(serial, 16)
            serials.append(serial)
        return serials
