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

# Copyright 2012-2013 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.

from methods_func import get_method_argparser, collect_object, \
                         check_result_msg, get_param_pwd, _print
from api_types import ViewInfo
from calculate.core.client.progressbar import Bar,Percentage,ETA,ProgressBar
from calculate.lib.cl_progressbar import ClProgressDialog,ClMessageBox
from calculate.lib.utils.text import tableReport
from calculate.lib.utils.files import getch
from cert_cmd import parse
import sys, termios
import os
import re
from methods_func import getErrorOnParam,GotErrorField
from func import shortTraceback, CommonMethods
from fcntl import ioctl
from array import array
import threading
from calculate.lib.cl_print import color_print
from calculate.lib.datavars import VariableError
from calculate.lib.cl_lang import setLocalTranslate
setLocalTranslate('cl_core3',sys.modules[__name__])
from itertools import repeat

class replaceClass():
    method_status = 2
    def startprocess (self, sid, target=None, method=None, method_name=None, \
                      auto_delete=False, args_proc = {}):
        """ start process """
        if "LANG" in os.environ:
            curThread = threading.currentThread()
            curThread.lang = os.environ["LANG"]
        com = target(self.no_progress,self.gui_progress,self.gui_warning,self.no_questions)
        if len(com.__class__.__bases__)>1 and \
            hasattr (com.__class__.__bases__[1], '__init__'):
            com.__class__.__bases__[1].__init__(com)
        self.method_status = 0 if getattr(com, method)(*args_proc) else 1
        return 0

    class Common(CommonMethods):
        """ class to interact with the processes """
        reClearTags = re.compile("<.*?>")
        def __init__(self, no_progress, gui_progress,gui_warning,no_questions):
            self.pid = 0
            self.Num = 100000
            self.color_print = color_print()

            self.no_progress = no_progress
            self.gui_progress = gui_progress
            self.gui_warning = gui_warning
            self.progress = None
            self.progressbar = None
            self.last_progress_msg = ''
            self.error = []
            self.warning = []
            self.no_questions = no_questions
            self.currentTaskMessage = ""

        def writeFile(self):
            pass

        def setProgress(self, perc, short_message = None, long_message = None):
            if self.no_progress:
                self.progress = perc
                return
            if perc >= 100 or perc < 0:
                if self.progressbar:
                    if not self.gui_progress:
                        self.progressbar.finish()
                return
            self.last_progress_msg = self.print_progressbar(perc,
                                            short_message, long_message,
                                            last_msg = self.last_progress_msg)

        def print_progressbar(self, percent, short_message, long_message,
                              last_msg = None, error = False):
            if not self.progressbar:
                return
            if long_message:
                if self.gui_progress:
                    self.progressbar.setTextLabel(long_message)
                    self.progressbar.setMaximum(100)
                    self.progressbar.setTextVisible(True)
                    if percent == 100:
                        self.progressbar.setValue(99)
                    self.progressbar.setValue(percent)
                else:
                    if last_msg != long_message:
                        cout_progress()
                        for line in long_message.splitlines():
                            self.color_print.printSUCCESS(line)
                    self.progressbar.update(percent)
                return long_message
            elif short_message:
                if self.gui_progress:
                    self.progressbar.setTextLabel(short_message)
                    self.progressbar.setMaximum(100)
                    self.progressbar.setTextVisible(True)
                    if percent == 100:
                        self.progressbar.setValue(99)
                    self.progressbar.setValue(percent)
                else:
                    if last_msg != short_message:
                        cout_progress()
                        for line in short_message.splitlines():
                            self.color_print.printSUCCESS(line)
                    self.progressbar.update(percent)
                return short_message
            else:
                if self.gui_progress:
                    self.progressbar.setMaximum(100)
                    self.progressbar.setTextVisible(True)
                    if percent == 100:
                        self.progressbar.setValue(99)
                    self.progressbar.setValue(percent)
                else:
                    self.progressbar.update(percent)
                return last_msg

        def setStatus(self, stat):
            pass

        def setData(self, dat):
            self.data_list = dat

        def getStatus(self):
            pass

        def getProgress(self):
            if self.no_progress:
                return self.progress
            if self.progressbar:
                if self.gui_progress:
                    return self.progressbar.value()
                else:
                    return self.progressbar.currval
            return 100

        def addProgress(self,message=""):
            self.nextLine()
            if self.no_progress:
                self.progress = 0
                return
            if self.gui_progress:
                if ClProgressDialog:
                    if not self.progressbar:
                        self.progressbar = ClProgressDialog()
                        self.progressbar.setCancelButton(None)
                        self.progressbar.adjustSize()
                        self.progressbar.setWindowTitle("")
                        self.progressbar.setAutoClose(False)
                        self.progressbar.setAutoReset(False)
                    self.progressbar.setLabelText(message.decode('utf-8'))
                    self.progressbar.setMaximum(0)
                    self.progressbar.setTextVisible(False)
                else:
                    self.progressbar = None
            else:
                widgets = ['','', Bar(), '', Percentage(),' ', ETA()]
                self.progressbar = ProgressBar(widgets=widgets, maxval=100)
                self.progressbar.start()
                self.progress = 0
            self.addMessage(type = 'progress', id = id)

        def printTable(self, table_name, head, body, fields = None,\
                        onClick = None, addAction = None):
            self.printSUCCESS(message = table_name)
            print printTable(body, head)

        def addMessage(self, type = 'normal', message = None, id = None,
                       onlyShow = None):
            self.printSUCCESS(message, onlyShow = onlyShow)

        def printDefault(self,message = '', onlyShow = None):
            if onlyShow == "gui":
                return
            message = self.clearTags(message)
            self.color_print.defaultPrint(message+"\n")

        def printPre(self,message = '', onlyShow = None):
            if onlyShow == "gui":
                return
            for line in message.split('<br/>'):
                self.printDefault(line)

        def clearTags(self,s):
            """
            Remove tags from text
            """
            reColor = re.compile("color=['\"](.*?)['\"]")
            def fontFunc(part):
                attr,text = part.groups()
                color = reColor.search(attr)
                if color:
                    color = color.group(1)
                    if "light" in color or "dark gray" in color:
                        bg = "1"
                    else:
                        bg = "0"
                    color = color.replace("light","").strip()
                    colorMap = {'red':"31",
                                'green':"32",
                                'brown':"33",
                                'blue':"34",
                                'purple':"35",
                                'cyan':"36",
                                'gray':"37",
                                'dark gray':"30"}
                    if color in colorMap:
                        fg = colorMap[color]
                        return "\033[%s;%sm%s\033[0m"%(bg,fg,text)
                return text
            reFont = re.compile("<font (.*?)>(.*?)</font>")
            return self.reClearTags.sub(
                "",reFont.sub(fontFunc,s)).replace("&nbsp;"," ")
            #return self.reClearTags.sub("",s)

        def printSUCCESS(self, message = '', onlyShow = None):
            if onlyShow == "gui":
                return
            if message:
                self.nextLine()
                if self.progressbar and not self.gui_progress:
                    if not self.progressbar.finished:
                        cout_progress()
                        for line in message.splitlines():
                            self.color_print.printSUCCESS(self.clearTags(line))
                        return
                for line in message.splitlines():
                    self.color_print.printSUCCESS(self.clearTags(line))

        def printWARNING(self, message = '', onlyShow = None):
            if onlyShow == "gui":
                return
            self.nextLine()
            if self.progressbar and not self.no_progress \
                    and not self.gui_progress:
                if not self.progressbar.finished:
                    cout_progress()
                    for line in message.splitlines():
                        line = self.clearTags(line)
                        self.color_print.printWARNING(line)
                    return
            if message:
                for line in message.splitlines():
                    line = self.clearTags(line)
                    self.color_print.printWARNING(line)
            if message:
                self.warning.append(message)

        def printERROR(self, message = '', onlyShow = None):
            if onlyShow == "gui":
                return
            self.nextLine()
            perc = self.getProgress()
            if perc == 0:
                self.setProgress(100)
            elif self.getProgress() > 0:
                # finish progress
                self.setProgress(0 - self.getProgress())
            else:
                #self.setProgress(-100)
                self.setProgress(perc)
            if message:
                for line in message.splitlines():
                    line = self.clearTags(line)
                    self.color_print.printERROR(line)
            if message:
                self.error.append(message)

        def startTask(self, message, progress = False, num = 1):
            self.nextLine()
            message = self.clearTags(message)
            self.color_print.printSUCCESS(message,printBR=False)
            self.color_print.defaultPrint(" ...")
            self.currentTaskMessage = message
            if progress:
                self.addProgress(message)

        def setTaskNumber(self, number = None):
            pass

        def endTask(self, result = None, progress_message = None):
            if result and not result is True:
                self.color_print.printOK(self.clearTags(result))
            elif self.currentTaskMessage:
                if not result is False:
                    self.color_print.printOnlyOK("",
                        len(unicode(self.currentTaskMessage))+8)
                    self.currentTaskMessage = ""
                if result is False:
                    self.color_print.printOnlyNotOK("",
                        len(unicode(self.currentTaskMessage))+8)
                    self.currentTaskMessage = ""
            self.nextLine()
            self.setProgress(100, progress_message)

        def nextLine(self):
            if self.currentTaskMessage:
                self.color_print.defaultPrint('\n')
                self.currentTaskMessage = ""

        def askConfirm(self,message,default="yes"):
            colorPrint = color_print()
            if self.no_questions:
                return default
            print
            while True:
                try:
                    ask = raw_input(message+ ' (yes/no): ')
                except KeyboardInterrupt:
                    ask = 'no'
                    print
                if ask.lower() in ['n', 'no']:
                    #colorPrint.printERROR(_('Manually interrupted'))
                    return "no"
                if ask.lower() in ['y', 'yes']:
                    return "yes"

        def isInteractive(self):
            """
            Check interactive ability
            """
            return sys.stdin.isatty()

        def askChoice(self,message,answers=[("yes","Yes"),("no","No")]):
            colorPrint = color_print()
            print
            # ability answer by first letter
            FIRSTLETTER = 0
            VALUE,COMMENT = 0,1
            answerByChar = map(lambda x:x[VALUE][FIRSTLETTER],answers)
            if filter(lambda x:answerByChar.count(x) > 1,answerByChar):
                useGetChar = False
            else:
                useGetChar = True
            while True:
                outMessage = message+ ' (%s): '% \
                                ("/".join(map(lambda x: \
                                    "%s[%s]"%(x[COMMENT],x[VALUE][FIRSTLETTER]),
                                                       answers)))
                if useGetChar:
                    colorPrint.defaultPrint(outMessage)
                    ask = getch()
                    print
                else:
                    try:
                        ask = raw_input(outMessage)
                    except KeyboardInterrupt:
                        ask = answers[-1][COMMENT]
                        print
                ask = ask.lower()
                likeAnswers = filter(lambda x:x[VALUE].startswith(ask),answers)
                if not likeAnswers:
                    colorPrint.printERROR(_('The answer is uncertain'))
                    continue
                if len(likeAnswers) == 1:
                    return likeAnswers[VALUE][FIRSTLETTER]
                else:
                    colorPrint.printERROR(_('Ambiguous answer:')+
                        ",".join(map(lambda x:x[COMMENT],likeAnswers)))

        def askQuestion(self, message):
            print
            return raw_input(message+":")

        def askPassword(self, message, twice = False):
            from calculate.lib.utils.common import getpass
            if os.readlink('/proc/self/fd/0') == '/dev/console':
                os.system('chvt 1 &>/dev/null')
            text1 = "%s:"%message
            if not twice:
                return getpass.getpass(text1)
            if twice:
                text2 = _('Repeat: ')
                pass1 = 'password'
                pass2 = 'repeat'
            try:
                while pass1 != pass2:
                    pass1 = getpass.getpass(text1)
                    pass2 = getpass.getpass(text2)
                    if pass1 != pass2:
                        print _('Passwords do not match')
            except KeyboardInterrupt:
                return None
            passwd = pass1 if (pass1 and pass1 == pass2) else None
            return passwd

        def beginFrame(self, message = None):
            self.printSUCCESS(message = message)

        def endFrame(self):
            self.addMessage(type = 'endFrame')
            if self.gui_progress and self.progressbar:
                self.progressbar.quit()
            if self.warning and not self.error and self.gui_warning:
                ClMessageBox().warning(("\n".join(self.warning)).decode('utf-8'))
            if self.error and self.gui_progress:
                if self.warning and self.gui_warning:
                    error = "\n".join(self.warning+self.error)
                else:
                    error = "\n".join(self.error)
                ClMessageBox().critical(error.decode('utf-8'))

        def startGroup(self, message):
            self.addMessage(type = 'startGroup', message = message)

        def endGroup(self):
            pass

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

def local_method(metaclass, args, unknown_args):
    """
    Call method from metaclass, check method existing.

    Generate help, for method, run method by 'call_method'.
    """
    import os
    sym_link = os.path.basename(sys.argv[0])
    if sym_link != 'cl-core':
        if sym_link in metaclass.Dec.conMethods.keys():
            args.method = metaclass.Dec.conMethods[sym_link][0]
        else:
            _print (_("Method not found for %s") %sym_link)
            sys.exit(1)

    if args.list_methods:
        keys = metaclass.Dec.conMethods.keys()
        keys.sort()
        for key in keys:
            print metaclass.Dec.conMethods[key][0], ' - ', \
                  metaclass.Dec.conMethods[key][2]
        return 0

    colorPrint = color_print()
    metaObject = metaclass()
    method_name = args.method
    method_view_name = method_name + '_view'
    if args.method and args.help:
        view_obj = ViewInfo()
        view_obj.step = None
        view_obj.expert = True
        view_obj.brief = None
        view_obj.onlyhelp = True
        try:
            view = getattr(metaObject, method_view_name)(0, view_obj)
        except AttributeError:
            colorPrint.printERROR (_('Method not found: ')+ method_view_name)
            return 1
        try:
            method_parser = get_method_argparser(view, args, cl_core = True)
        except Exception as e:
            #import traceback
            #for i in apply(traceback.format_exception, sys.exc_info()):
            #    sys.stderr.write(i)
            #sys.stderr.flush()
            metaObject.clear_cache(0, method_name)
            return  1
        method_parser.print_help()
    else:
        try:
            call_method(metaObject, args, unknown_args, colorPrint)
            metaObject.clear_cache(0, method_name)
            return metaObject.method_status
        except (ValueError) as e:
            colorPrint.printERROR(str(e))
            #colorPrint.printERROR(shortTraceback(*sys.exc_info()))
        except (KeyboardInterrupt, EOFError):
            colorPrint.printERROR(_('Manually interrupted'))
        except (GotErrorField,) as e:
            pass
        except Exception as e:
            colorPrint.printERROR(shortTraceback(*sys.exc_info()))
            pass
#            print 'Error: ', e
    metaObject.clear_cache(0, method_name)

def call_method(metaObject, args, unknown_args, colorPrint):
    """
    Function for call method through metaObject and args
    """
    method_name = args.method
    stdin_passwd = args.stdin_passwd
    method_view_name = method_name + '_view'
    metaObject.no_progress = args.no_progress
    metaObject.gui_progress = args.gui_progress
    metaObject.gui_warning = args.gui_warning
    metaObject.no_questions = False
    view_obj = ViewInfo()
    view_obj.step = None
    view_obj.expert = True
    view_obj.brief = None
    view_obj.onlyhelp = True

    try:
        view = getattr(metaObject, method_view_name)(0, view_obj)
    except AttributeError:
        colorPrint.printERROR (_('Method not found: ') + method_name)
        return None
    method_parser = get_method_argparser(view, args, cl_core = True)
    param_object = create_param_object(view)
    try:
        unknown_args = method_parser.fixBoolVariables(unknown_args)
        args, unknown_args = method_parser.parse_known_args(unknown_args)
        no_questions = args.no_questions
        metaObject.no_questions = no_questions
    except SystemExit as e:
        return 1
    except Exception as e:
        import traceback
        for i in apply(traceback.format_exception, sys.exc_info()):
            sys.stderr.write(i)
        sys.stderr.flush()
        raise
    for i in unknown_args:
        if i.startswith('-'):
            if i in parse(True).parse_known_args()[1]:
                _print (_('Unknown parameter'), i)
                return 1
        else:
            _print (_('Unknown argument'), i)
            return 1

    param_object, steps = collect_object(None, param_object, view, args,
                                         stdin_passwd=stdin_passwd)
    if view.has_brief:
        setattr(param_object, 'CheckOnly', True)
        check_res = {}
        while True:
            method_result = getattr(metaObject, method_name)(0, param_object)
            if not method_result:
                print _('Method not available')
                return None

            if method_result[0].type and method_result[0].type != "pid":
                check_res = check_result_msg(method_result, view, check_res,args)
                if not check_res:
                    return None
                else:
                    param_object = get_param_pwd(check_res, view,
                                                 param_object,
                                                 stdin_passwd=stdin_passwd)
            else:
                break

        view_obj = ViewInfo()
        view_obj.step = None
        view_obj.expert = True
        view_obj.brief = True
        view_obj.onlyhelp = False
        try:
            view = getattr(metaObject, method_view_name)(0, view_obj)
        except AttributeError:
            colorPrint.printERROR (_('Method not found: ') + method_name)

        print_brief(view, steps.label)
        if not no_questions:
            while True:
                try:
                    ask = raw_input('\n' + _('Run process? (yes/no): '))
                except KeyboardInterrupt:
                    ask = 'no'
                    print
                if ask.lower() in ['n', 'no']:
                    colorPrint.printERROR(_('Manually interrupted'))
                    return None
                if ask.lower() in ['y', 'yes']:
                    break

    setattr(param_object, 'CheckOnly', False)
    try:
        method_result = getattr(metaObject, method_name)(0, param_object)
    except VariableError, e:
        _print (e)
        return None
    if not method_result:
        colorPrint.printERROR (_('method unavailable'))
        return None
    for ReturnedMessage in method_result:
        if ReturnedMessage.type and ReturnedMessage.type != "pid":
            params_text = ''
            for Group in view.groups:
                for field in Group.fields:
                    if field.name == ReturnedMessage.field:
                        params_text += getErrorOnParam(args,field)
            colorPrint.printERROR('\r' + params_text% \
                                     str(ReturnedMessage.message))
            return None
    return method_result

def create_param_object(view):
    param_object = type ('collect_object', (object,), {})
    setattr(param_object, 'CheckAll', True)
    setattr(param_object, '_type_info', {})
    for Group in view.groups:
        if not Group.fields:
            continue
        for field in Group.fields:
            setattr(param_object, field.name, None)
            param_object._type_info[field.name] = None
    return param_object

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

def print_brief_group(Fields, group_name):
    print_group_flag = False
#    if group_name:
#        _print ('\b'+group_name)
    uncompatible_count = 0
    colorPrint = color_print()
    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:
                if group_name:
                    print_group_flag = True
                    _print ('\b'+group_name)
            colorPrint.printSUCCESS('%s: %s' %(field.label, value))

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

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

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

        elif field.element == 'error':
            if not print_group_flag:
                if group_name:
                    print_group_flag = True
                    _print ('\b'+group_name)
            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:
                if group_name:
                    print_group_flag = True
                    _print ('\b'+group_name)
            colorPrint.printSUCCESS('%s: %s' %(field.label, value))

        elif field.element == 'table' and field.type != 'steps':
            head = field.tablevalue.head

            body = []
            for row in field.tablevalue.body:
                if not row[0]:
                    row.pop(0)
                body.append(row)

            # 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+"\n")
                continue
            ChoiceValue = field.tablevalue.values
            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 "password" in ChoiceValue[row].typefield:
                    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:
                if group_name:
                    print_group_flag = True
                    _print ('\b'+group_name)
            colorPrint.printSUCCESS(field.label+': ')
            res = printTable(data, head)
            sys.stdout.flush()
            sys.stdout.write(res+"\n")
        else:
            uncompatible_count += 1

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

class Table(tableReport):
    def __init__(self,*args,**kwargs):
        self.res = []
        tableReport.__init__(self,*args,**kwargs)

    def printFunc(self,s):
        self.res.append(s)

    def printTable(self):
        self.setAutosize()
        self.printReport(printRows=False)
        return "\n".join(self.res)

class Methods(replaceClass.Common,object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Methods, cls).__new__(
                                cls, *args, **kwargs)
        return cls._instance

    def __init__(self):
        replaceClass.Common.__init__(self,True,False,False,False)

def printTable(data, header=None):
    try:
        return Table(None,header,data,colSpan=0).printTable()
    except Exception as e:
        print str(e)
        raise

