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

# Copyright 2012-2016 Mir Calculate. 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
import os
import sys
import subprocess
import shlex

from OpenSSL import crypto
from calculate.core.client.function import create_obj
from calculate.core.server.api_types import ArrayReturnedMessage, TableAdapter
from calculate.core.server.cert_cmd import getHwAddr, getIpLocal
from calculate.core.server.local_call import print_brief_group
from calculate.lib.cl_lang import setLocalTranslate
from calculate.lib.utils.files import readFile
import calculate.contrib
from suds import MethodNotFound
_ = lambda x: x
setLocalTranslate('cl_console3', sys.modules[__name__])


def _print(*args):
    print(" ".join(map(str, args)))

def parse_error(e):
    if hasattr(e, "message"):
        return e.message
    return e

# get list of certificate and session id

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 as e:
                _print(parse_error(e))
    except Exception:
        print(_("Failed to clear the cache! "))
        return 1


def get_ip_global():
    import urllib.request as 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 = [getIpLocal(), getHwAddr()]
    if client_type:
        results.append(client_type)
    else:
        results.append('console')
    return results


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)


def _return_revoked_serials(crlfile):
    try:
        serials = []
        crltext = readFile(crlfile)
        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


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, dispatch_usenew=False, unknown_args=[]):
    view_params = _create_obj(client, method)
    view_params.step = step
    view_params.expert = expert
    view_params.brief = brief
    view_params.onlyhelp = onlyhelp
    view_params.conargs.string = unknown_args
    view_params.dispatch_usenew = dispatch_usenew
    return view_params


class MessageReceiver(object):
    """
    Объект организует цикл получения сообщений от WsdlServer и передает их на
    обработку MessageDispatcher
    """

    class States(object):
        Messages = 0
        Progress = 1
        Finish = 2

    def __init__(self, client, message_dispatcher=None, display=None):
        """
        @param display: Display
        @param message_dispatcher: MessageDispatcher
        """
        self.client = client
        self.sid = client.sid
        self.message_dispatcher = message_dispatcher
        self.message_dispatcher.parent = self
        self.display = display
        self.state = self.States.Messages
        self.pid = 0
        self.entire = False

    @classmethod
    def from_entire(cls, client, pid, message_dispatcher=None, display=None):
        sid = client.get_sid()
        list_pid = client.service.list_pid(sid=sid)
        if hasattr(list_pid, 'integer'):
            if pid not in list_pid.integer:
                display.print_error(
                    _("The process does not exist or does not belong to "
                      "your session"))
                return None
        obj = cls(client, message_dispatcher, display)
        obj.sid = sid
        obj.pid = pid
        obj.entire = True
        return obj

    def analysis(self, method_result):
        """ analysis of the bounced message method """
        messages = ArrayReturnedMessage.from_detect(method_result)
        for message in messages:
            if message.type == 'pid':
                try:
                    self.pid = int(message.message)
                except (ValueError, AttributeError):
                    self.display.print_error(
                        _('the server sent PID = ') + str(message))
                    return 1
                try:
                    self.get_messages()
                except Exception as e:
                    import traceback

                    traceback.print_exc()
            elif message.type == 'error':
                self.display.print_error(message.message)
            elif message.type == 'warning':
                self.display.print_warning(message.message)

    def get_client_frame(self):
        if self.entire:
            self.entire = False
            return self.client.service[0].get_entire_frame(self.sid, self.pid,
                                                           "console")
        else:
            return self.client.service[0].get_frame(self.sid, self.pid,
                                                    "console")

    def get_messages(self):
        """ get all messages, until type is not endFrame (or Error) """
        self.state = self.States.Messages

        while self.state != self.States.Finish:
            if self.state == self.States.Messages:
                current_frame = self.get_client_frame()
                while current_frame in [None, [], ""]:
                    time.sleep(float(self.client.frame_period) / 10)
                    current_frame = self.get_client_frame()
                for item in current_frame[0]:
                    self.message_dispatcher.dispatch_message(item)

    def get_client_progress(self, id):
        return self.client.service.get_progress(self.sid, self.pid, id).percent

    def get_progress(self, id):
        percent = self.get_client_progress(id)
        yield percent
        while 0 <= percent < 100:
            time.sleep(0.2)
            temp_percent = self.get_client_progress(id)
            if temp_percent != percent:
                percent = temp_percent
                yield percent
        if percent < 0:
            yield 0
        elif percent > 100:
            yield 100

    def get_table(self, id):
        return self.client.service.get_table(self.sid, self.pid, id)

    def send_message(self, message):
        self.client.service.send_message(self.sid, self.pid, message)


class MessageDispatcher(object):
    """
    Объект разбирает полученное сообщение Message через функцию dispatch_message

    В дальнейшем взаимодействует с parent через методы get_progress, get_table,
    send_message, поле State
    """

    def __init__(self, methods=None):
        """
        @param methods: Common
        """
        self.methods = methods
        self.parent = None
        self.dispatchers = {'endFrame': self.end_frame,
                            'progress': self.progress,
                            'startTask': self.start_task,
                            'endTask': self.end_task,
                            'startGroup': self.start_group,
                            'endGroup': self.end_group,
                            'normal': self.print_success,
                            'error': self.print_error,
                            'warning': self.print_warning,
                            'pre': self.print_pre,
                            'plain': self.print_default,
                            'table': self.print_table,
                            'choice': self.ask_choice,
                            'confirm': self.ask_confirm,
                            'question': self.ask_question,
                            }

    def default_action(self, message):
        pass

    def dispatch_message(self, message):
        self.dispatchers.get(message.type, self.default_action)(message)

    def end_frame(self, message):
        self.parent.state = self.parent.States.Finish

    def progress(self, message):
        self.methods.addProgress(message.message)
        for perc in self.parent.get_progress(message.id):
            self.methods.setProgress(perc)

    def start_task(self, message):
        self.methods.startTask(message.message)

    def end_task(self, message):
        self.methods.endTask(message.message)

    def start_group(self, message):
        self.methods.startGroup(message.message)

    def end_group(self, message):
        self.methods.endGroup()

    def print_success(self, message):
        self.methods.printSUCCESS(message.message)

    def print_error(self, message):
        self.methods.printERROR(message.message)
        if "403 Forbidden" in message.message:
            self.parent.state = self.parent.States.Finish

    def print_warning(self, message):
        self.methods.printWARNING(message.message)

    def print_pre(self, message):
        self.methods.printPre(message.message)

    def print_default(self, message):
        self.methods.printDefault(message.message)

    def print_table(self, message):
        table = TableAdapter.from_detect(self.parent.get_table(message.id))
        self.methods.printTable(message.message, table.head, table.body)

    def ask_choice(self, message):
        message, answers = message.message.split('|')
        answers = [(x[0], x[1].strip(')')) for x 
                in (y.split('(') for y in answers.split(','))]
        answer = self.methods.askChoice(message, answers)
        self.parent.send_message(answer)

    def ask_confirm(self, message):
        answer = self.methods.askConfirm(message.message, message.default or "")
        self.parent.send_message(answer)

    def ask_question(self, message):
        answer = self.methods.askQuestion(message.message)
        self.parent.send_message(answer)

    def ask_password(self, message):
        answer = self.methods.askPassword(message.message, message.id == 2)
        self.parent.send_message(answer)


