#!/usr/bin/python
#-*- 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 sys, os
from calculate.lib.utils.common import getpass
from calculate.lib.cl_print import color_print
from calculate.lib.cl_lang import setLocalTranslate
setLocalTranslate('cl_core',sys.modules[__name__])

colorPrint = color_print()

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

def get_password(text1 = None, text2 = None, getfromstdin=False):
    if getfromstdin:
        try:
            passwd = ''
            while not passwd:
                passwd = sys.stdin.readline()
                if not passwd:
                    return None
                passwd = passwd.rstrip('\n')
            return passwd
        except:
            return None
    if not text1:
        text1 = _('Password: ')
    if not text2:
        text2 = _('Repeat: ')
    try:
        pass1 = 'password'
        pass2 = 'repeat'
        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 ''
    return passwd

def listToArray (client, _list, _type = 'string'):
    if not client:
        return _list
    Array = client.factory.create('%sArray' %_type)
    for i in _list:
        Array['%s' %_type].append(i)
    return Array

def listToArrayArray (client, _list, _type = 'string'):
    if not client:
        return _list
    ArrayArray = client.factory.create('%sArrayArray' %_type)
    for i in _list:
        Array = client.factory.create('%sArray' %_type)
        for j in i:
            Array[_type].append(j)
        ArrayArray['%sArray' %_type].append(Array)
    return ArrayArray

def _getattr(obj, attr):
    return getattr(obj, attr) if hasattr(obj, attr) else None


def get_terminal_size():
    import struct
    try:
        import fcntl
        import termios
    except ImportError:
        return (None, None)

    try:
        data = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, 4 * '00')
        height, width = struct.unpack('4H', data)[:2]
        return (height, width)
    except Exception:
        return (None, None)

import argparse
import textwrap as _textwrap
class RawAndDefaultsHelpFormatter(argparse.HelpFormatter):
    def __init__(self, prog, max_help_position=24, **kwargs):
        # Use the whole terminal width
        height, width = get_terminal_size()
        argparse.HelpFormatter.__init__(self, prog, width=width,
                                        max_help_position = max_help_position,
                                        **kwargs)

    def _split_lines(self, text, width):
        text = self._whitespace_matcher.sub(' ', text.decode('utf-8')).strip()
        return _textwrap.wrap(text.decode('utf-8'), width)

def get_method_argparser(view, args, cl_core = False):
    """
    Get argparser by ViewInfo get from WSDL server (or stub)

    cl_core  - argparser for cl_core (local call)
    """
    # get list (local View) or first element of tuple (WSDL View)
    getlist = (lambda x:x) if cl_core else (lambda x:x[0])
    error_flag = False
    method = args.method
    if cl_core:
        progr = os.path.basename(sys.argv[0])
    else:
        progr = 'cl-console --method '+method

    parser = argparse.ArgumentParser(prog=progr, add_help=False,
                                 formatter_class=RawAndDefaultsHelpFormatter)

    for Group in getlist(view.groups):
        if not Group.fields:
            continue
        group = parser.add_argument_group(Group.name)
        for field in getlist(Group.fields):
            if field.element == 'error':
                error_flag = True
                colorPrint.printERROR(field.label)
            elif field.opt:
                opt = field.opt
                data = {'dest':field.name,'help' : opt.help}
                if field.type == "password":
                    data['action'] = "store_true"
                else:
                    data['type'] = str
                if field.element == 'table' and field.type != 'steps':
                    data['action'] = 'append'

                if data.get('action') != "store_true":
                    data['metavar'] = opt.metavalue if opt.metavalue \
                                              else field.name.upper()
                    if ':' in data['metavar']:
                        data['metavar'] = field.name.upper()
                try:
                    opts = filter(None,[opt.shortopt,opt.longopt])
                    group.add_argument(*opts,**data)
                except argparse.ArgumentError:
                    continue
    if error_flag:
        raise Exception
    return parser

def set_obj_item(client, param_object, field_name, value):
    """
    Set value for Info object. By client detect (local or WSDL call)
    """
    if client:
        param_object.__setitem__(field_name, value)
    else:
        setattr(param_object, field_name, value)
    return param_object

def set_table_pwd(client, param_object, field, value):
    if type(field.tablevalue.values) in [list, tuple]:
        ChoiceValues = field.tablevalue.values
#        column = len(field.tablevalue.head)
    else:
        ChoiceValues = field.tablevalue.values.ChoiceValue
#        column = len(field.tablevalue.head.string)
#    print ChoiceValue, column
    found_flag = False
    if not getattr(param_object, field.name):
        for column in range(len(ChoiceValues)):
            if found_flag:
                break
            if ChoiceValues[column].typefield == 'password':
                if hasattr(field.tablevalue.body, 'stringArray'):
                    body = field.tablevalue.body.stringArray
                else:
                    body = field.tablevalue.body
                for _row in body:
                    if hasattr(_row, 'string'):
                        row = _row.string
                    else:
                        if not _row[0]:
                            row = _row[1:]
                        else:
                            row = _row
                    if not row[column]:
                        row[column] = value
                        temp = []
                        for item in row:
                            temp.append(item) if item else temp.append('')
                        changed_string = temp
                        found_flag = True
                        break
    else:
        user_table = getattr(param_object, field.name)
        if hasattr (user_table, 'stringArray'):
            user_table = user_table.stringArray
        for column in range(len(ChoiceValues)):
            if found_flag:
                break
            if ChoiceValues[column].typefield == 'password':
                for _row in user_table:
                    if hasattr(_row, 'string'):
                        row = _row.string
                    else:
                        if not _row[0]:
                            row = _row[1:]
                        else:
                            row = _row
                    if not row[column]:
                        row[column] = value
                        temp = []
                        for item in row:
                            temp.append(item) if item else temp.append('')
                        changed_string = temp
                        found_flag = True
                        break

    if client:
        if not getattr(param_object, field.name):
            param_object[field.name] = field.tablevalue.body

        result = []
        for _array in param_object[field.name].stringArray:
            temp = []
            for item in _array.string:
                temp.append(item) if item else temp.append('')
            result.append(temp)
        for item in result:
            if str(item[0]) == str(changed_string[0]):
                result.remove(item)

        result.append(changed_string)
        param_object[field.name] = listToArrayArray(client, result)
        return param_object
    else:
        if not getattr(param_object, field.name):
            param_object[field.name] = field.tablevalue.body

        result = []
        for _array in getattr(param_object,field.name):
            temp = []
            for item in _array:
                temp.append(item) if item else temp.append('')
            result.append(temp)
        for item in result:
            if str(item[0]) == str(changed_string[0]):
                result.remove(item)

        result.append(changed_string)
        setattr (param_object, field.name, result)
        return param_object

def check_result_msg(method_result, view, input_error_dict = {}):
    password_errors = {}
    if hasattr (method_result, 'ReturnedMessage'):
        method_result = method_result.ReturnedMessage

    if hasattr (view.groups, 'GroupField'):
        groups = view.groups.GroupField
    else:
        groups = view.groups
    for error in method_result:
        if error.type == 'pwderror':
            password_errors[error.field] = error.message
            continue

        params_text = ''
        for Group in groups:
            if hasattr (Group.fields, 'Field'):
                fields = Group.fields.Field
            else:
                fields = Group.fields
            for field in fields:
                if field.name == error.field:
                    if field.opt.shortopt or field.opt.longopt:
                        params_text += _('Wrong option ')
                        params_text += ' '+', '.join(filter(None,
                            [field.opt.shortopt, field.opt.longopt])) \
                            + '. '

        sys.stdout.write('\r')
        sys.stdout.flush()
        colorPrint.printERROR(params_text + error.message)

    if len(password_errors) < len(method_result):
        return None
    else:
        if not dict([x for x in input_error_dict.items() \
                            if x not in password_errors.items()]) and \
                            not dict([x for x in password_errors.items() \
                            if x not in input_error_dict.items()]):
            return None
        return password_errors

def get_param_pwd(check_res, view, param_object, client = None,
                  stdin_passwd=False):
    if hasattr (view.groups, 'GroupField'):
        groups = view.groups.GroupField
    else:
        groups = view.groups
    for pwd_field in check_res:
        _print (check_res[pwd_field])
        for Group in groups:
            if hasattr (Group.fields, 'Field'):
                fields = Group.fields.Field
            else:
                fields = Group.fields
            for field in fields:
                if field.name == pwd_field:
                    if field.element == 'table':
                        value = get_password(getfromstdin=stdin_passwd)
                        if value == None:
                            raise KeyboardInterrupt
                        set_table_pwd(client, param_object, field, value)
                    else:
                        value = get_password(getfromstdin=stdin_passwd)
                        if value == None:
                            raise KeyboardInterrupt
                        setattr(param_object, pwd_field, value)
    return param_object

def collect_object(client, param_object, view, args,wait_thread = None,
                   stdin_passwd = False):
    """
    Collect Info object by args
    """
    steps = None
    if type(view.groups) == list:
        # for local call method
        groups = view.groups
    else:
        groups = view.groups[0]
    for Group in groups:
        if not Group.fields:
            continue
        if type(Group.fields) == list:
            # for local call method
            fields = Group.fields
        else:
            fields = Group.fields[0]
        for field in fields:
            if field.uncompatible:
                continue
            if field.element in ['check', 'check_tristate'] and \
                                                        field.type == 'bool':
                set_flag = False
                if hasattr (field.opt, 'metavalue'):
                    if field.opt.metavalue and _getattr(args, field.name):
                        if type(_getattr(args, field.name)) == bool:
                            if _getattr(args, field.name) == True:
                                param_object = set_obj_item(client,
                                                    param_object, field.name,
                                                    field.opt.metavalue)
                        elif _getattr(args, field.name).lower() in \
                                                                ['on','yes']:
                            param_object = set_obj_item(client, param_object,
                                             field.name, field.opt.metavalue)
                            set_flag = True
                if not set_flag:
                    value = _getattr(args, field.name)
                    if value:
                        if _getattr(args, field.name).lower() in ['on','yes']:
                            value = True
                        elif _getattr(args,field.name).lower() in ['off','no']:
                            value = False
                        else:
                            value = None
                    else:
                        value = None
                    param_object = set_obj_item(client, param_object,
                                                field.name, value)

            if field.element == 'radio' and field.type == 'bool':
#                param_object[field.name] = _getattr(args, field.name)
                param_object = set_obj_item(client, param_object, field.name,
                                            _getattr(args, field.name))

            elif field.element == 'input' and \
                            field.name in ['cl_page_offset','cl_page_count']:
                val = _getattr(args, field.name)
                if not val:
                    val = 0
                if wait_thread:
                    wait_thread.stop()
#                param_object[field.name] = val
                param_object = set_obj_item(client, param_object, field.name,
                                            val)

            elif field.element in ['input', 'openfile',\
                                   'file', 'radio']:
                param_object = set_obj_item(client, param_object, field.name,
                                            _getattr(args, field.name))
            elif field.element in ['password'] and _getattr(args, field.name):
                if wait_thread:
                    wait_thread.pause()
                password=get_password("%s: "%field.label,_('Repeat password: '),
                                      getfromstdin=stdin_passwd)
                if password is None:
                    raise KeyboardInterrupt
                param_object = set_obj_item(client, param_object, field.name,
                                            password)
                if wait_thread:
                    wait_thread.resume()
            elif field.element in ['combo', 'comboEdit']:
                value = _getattr(args, field.name)
                if value:
                    if hasattr (field.choice, 'string'):
                        choice = field.choice.string
                    else:
                        choice = field.choice
                    for item in choice:
                        if item:
                            if value.lower() == item.lower():
                                value = item
                                break

                param_object = set_obj_item(client, param_object, field.name,
                                            value)
            elif field.element in ['multichoice', 'multichoice_add', \
                                   'selecttable', 'selecttable_add']:
                val = _getattr(args, field.name)
                if val in ['off', 'none']:
                    if client:
                        value = listToArray(client, [None])
                    else:
                        value = []
                else:
                    value = listToArray(client, \
                            val.split(',')) if val else None
                param_object = set_obj_item(client, param_object, field.name,
                                            value)

            elif field.element == 'table' and field.type != 'steps':
                val = _getattr(args, field.name)
                value = collect_table(field, val, client, wait_thread,
                                      stdin_passwd)
                param_object = set_obj_item(client, param_object, field.name,
                                            value)

            elif field.element == 'table' and field.type == 'steps':
                steps = field
    if hasattr (param_object, 'CheckAll'):
        param_object = set_obj_item(client, param_object, 'CheckAll', True)

    return param_object, steps

def collect_table(field, val_list, client, wait_thread = None,
                  stdin_passwd = False):
    if not val_list:
        return None
    val_table = map(lambda x: x.split(':'), val_list)

#    obj_body = []

#    if type(field.tablevalue.body) == list:
#        temp_body = field.tablevalue.body
#    else:
#        temp_body = field.tablevalue.body.stringArray
#    for obj_row in temp_body:
#        if type(obj_row) != list:
#            obj_row = obj_row.string
#            for item in range(len(obj_row)):
#                if not obj_row[item]:
#                    obj_row[item] = ''
#        else:
#            if obj_row:
#                if obj_row[0] == '':
#                    obj_row.pop(0)
#        obj_body.append(obj_row)
#
#    obj_body = collect_obj_body(obj_body, field)

    if type(field.tablevalue.values) in [list, tuple]:
        ChoiceValue = field.tablevalue.values
        column = len(field.tablevalue.head)
    else:
        ChoiceValue = field.tablevalue.values.ChoiceValue
        column = len(field.tablevalue.head.string)
    type_list = map(lambda x: x.typefield, ChoiceValue)
    obj_body = []
    for i in range(len(val_table)):
        if 'password' in type_list:
            if len(val_table[i]) != 2 or val_table[i][1].lower() != '':
                if wait_thread:
                    wait_thread.stop()
                    sys.stdout.write('\r')
                    sys.stdout.flush()
                password=get_password(_('Password for %s: ')%val_table[i][0],\
                               _('Repeat password for %s: ') %val_table[i][0],
                               getfromstdin=stdin_passwd)
                if password == None:
                    raise KeyboardInterrupt
        temp_row = []
        for j in range(column):
            # not adding if readonly
            if j > (len(ChoiceValue) + 1):
                continue

            choice_value = ChoiceValue[j]
            typefield = choice_value.typefield
            # if readonly, except first column

            if typefield == 'readonly' and j > 0:
                continue
            elif typefield in ['check', 'check_tristate']:
                if len (val_table[i]) < j + 1:
                    temp_row.append('')
                    continue
                if val_table[i][j].lower() in ['on', 'yes']:
                    temp_row.append('on')
                elif val_table[i][j].lower() in ['off', 'no']:
                    temp_row.append('off')
                else:
                    temp_row.append(val_table[i][j])
            elif typefield in ['input', 'combo', 'comboEdit', 'openfile', \
                               'file', 'radio', 'text']:
                if len (val_table[i]) < j + 1:
                    temp_row.append('')
                else:
                    temp_row.append(val_table[i][j])
            elif typefield == 'password':
                if 'password' in locals():
                    temp_row.append(password)
                    del password
                else:
                    if len (val_table[i]) < j + 1:
                        temp_row.append('')
                    else:
                        temp_row.append(val_table[i][j])
            elif typefield in ['multichoice', 'multichoice_add']:
                if len (val_table[i]) < j + 1:
                    temp_row.append('')
                else:
                    temp_row.append(val_table[i][j])

        obj_body.append(temp_row)

    if not obj_body:
        obj_body = [[None]]
    return listToArrayArray(client, obj_body)

def collect_obj_body(body, field):
    column = len(field.tablevalue.head) if type(field.tablevalue.head) == list\
                                        else len(field.tablevalue.head.string)
    result_table = []
    if type(field.tablevalue.values) in [list, tuple]:
        ChoiceValue = field.tablevalue.values
    else:
        ChoiceValue = field.tablevalue.values.ChoiceValue
    for i in range(len(body)):
        temp_row = []
        for j in range(column):
            # not adding if readonly
            if j > (len(ChoiceValue) + 1):
                continue
            typefield = ChoiceValue[j].typefield
            if typefield == 'readonly' and j > 0:
                continue
            elif typefield in ['check', 'check_tristate']:
                if len (body[i]) < j + 1:
                    temp_row.append('')
                    continue
                if not body[i][j]:
                    temp_row.append('')
                elif body[i][j].lower() in ['on', 'yes']:
                    temp_row.append('on')
                elif body[i][j].lower() in ['off', 'no']:
                    temp_row.append('off')
                else:
                    temp_row.append(body[i][j])

            elif typefield in ['input', 'combo', 'comboEdit', 'openfile', \
                               'file', 'password', 'radio', 'text']:
                if len (body[i]) < j + 1:
                    temp_row.append('')
                elif not body[i][j]:
                    temp_row.append('')
                else:
                    temp_row.append(body[i][j])
            elif typefield in ['multichoice', 'multichoice_add']:
                if len (body[i]) < j + 1:
                    temp_row.append('')
                elif not body[i][j]:
                    temp_row.append('')
                else:
                    temp_row.append(body[i][j])
        result_table.append(temp_row)
    return result_table
