#-*- 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 PySide import QtGui, QtCore
import urllib2

from calculate.core.client.function import switch

from more import get_sid, ClientServiceThread, show_msg, ErrorWgt, ResultLayout
from more import LabelWordWrap as Label
from MainFrameResult import LabelWordWrap,ChoiceDialog

class TaskWidget(QtGui.QWidget):
    def __init__(self, taskNumber = 1, parent = None):
        QtGui.QWidget.__init__(self)
        layout = QtGui.QGridLayout(self)
        self.text_lbl = LabelWordWrap('', self)
        layout.addWidget(self.text_lbl, 0, 0)
        layout.setContentsMargins(0,4,0,4)
        layout.setSpacing(10)

        self.image_lbl = QtGui.QLabel(self)
        self.image_lbl.setFixedSize(16,16)
        layout.addWidget(self.image_lbl, 0, 1)

        self.progressbar = QtGui.QProgressBar(self)
        layout.addWidget(self.progressbar, 1, 0, 1, 2)

        self.set_status = False

        self.task_number = taskNumber
        self.coef_for_one_part = 1 / float(taskNumber)
        self.coef_perc = 1
        self.completed_parts = 0
        self.completed_perc = 0
        self.part_current_task = 1

    def set_ok(self):
        if not self.set_status:
            i_path = '/usr/share/icons/Calculate/16x16/client-gui/console_ok'
            icon = QtGui.QIcon(i_path)
            self.image_lbl.setPixmap(icon.pixmap(16))
            self.set_status = True

    def set_error(self):
        if not self.set_status:
            i_path = '/usr/share/icons/Calculate/16x16/client-gui/'\
                     'console_cancel'
            icon = QtGui.QIcon(i_path)
            self.image_lbl.setPixmap(icon.pixmap(16))
            self.set_status = True

    def setText(self, text):
        self.text_lbl.setText(text)

    def setProgress(self, perc):
        if perc > 100:
            perc = 100
        elif perc < 0:
            perc = 0 - perc

        cur_perc = self.completed_perc + self.coef_perc * perc
        self.progressbar.setValue(cur_perc)

    def endCurrentTask(self):
        self.completed_parts += self.part_current_task
        self.completed_perc = 100*self.completed_parts/float(self.task_number)
        if self.completed_parts >= self.task_number:
            self.progressbar.hide()
            self.set_ok()

    def setCurrentCount(self, count):
        if count > (self.task_number - self.completed_parts):
            count = self.task_number - self.completed_parts

        self.part_current_task = count
        # calculate coef for this task
        self.coef_perc = count * self.coef_for_one_part

class ShortFrameRes(QtGui.QWidget):
    valSignal = QtCore.Signal()
    def __init__(self, parent, ClientObj, method_name, meth_result):
#        super(ShortFrameRes, self).__init__(self, parent)
        QtGui.QWidget.__init__(self, parent)
        self._parent = parent
        self.ClientObj = ClientObj
        self.method_name = method_name
        self.meth_result = meth_result
        self.client = ClientObj.client
        self.cur_pid = 0
        
        if self.meth_result.type == "pid":
            self.cur_pid = self.meth_result.message
        
        self.work_layout = ResultLayout(self)
        self.work_layout.kill_process_button.clicked.connect \
                                            (self.kill_process(self.cur_pid))

        self.valSignal.connect(ClientObj.MainWidget.setScrollBarVal)
            
        self.progress_list = []
        self.task_list = []

        self._parent.methodname_wgt.show()
        self._parent.control_button.show()
        self._parent.control_button.del_button()
        if self.ClientObj.method_names.has_key(self.method_name):
            self._parent.methodname_wgt.setMethodName \
                    (self.ClientObj.method_names[self.method_name])
        else:
            self._parent.methodname_wgt.setMethodName(method_name)

        parent.main_frame.viewport().setObjectName('main_frame')
        parent.main_frame.viewport().setStyleSheet("#main_frame "
                                            "{background-color: #E4E1E0;}")
        self.setObjectName('Frame_Res')
        self.setStyleSheet("#Frame_Res {background-color: transparent;}")

    def initUI(self):
        self.updateGeometry()

################# API FUNCTION ###############################
    def show_view(self, view):
        pass

    def show_normal(self, message, mono=False):
        # add normal message label
        if hasattr (self, 'taskWidget') and not mono:
            self.taskWidget.setText(message)
        else:
            if message:
                # add normal message label
                lbl_temp = LabelWordWrap(message, self, mono=mono)
                lbl_temp.setMinimumHeight(lbl_temp.sizeHint().height())
                lbl_temp.setMaximumHeight(lbl_temp.sizeHint().height())
                self.work_layout._addWidget(lbl_temp)
                self.initUI()

    def show_error(self, message):
        # add error message label
#        if self.progress_list:
#            self.progress_list[len(self.progress_list)-1].hide()
        lbl_temp = ErrorWgt(message, self)
        lbl_temp.set_error()
        lbl_temp.setMinimumHeight(lbl_temp.sizeHint().height())
        lbl_temp.setMaximumHeight(lbl_temp.sizeHint().height())
        self.work_layout._addWidget(lbl_temp)
        if hasattr (self, 'taskWidget'):
            self.taskWidget.set_error()
        self.initUI()

    def show_warning(self, item):
        # add warning message label
        lbl_temp = ErrorWgt(item.message, self)
        lbl_temp.set_warning()
        lbl_temp.setMinimumHeight(lbl_temp.sizeHint().height())
        lbl_temp.setMaximumHeight(lbl_temp.sizeHint().height())
        self.work_layout._addWidget(lbl_temp)
        self.initUI()

    def show_group(self, item):
        pass
    
    def show_result(self, result):
        # add normal message label
        lbl_temp = LabelWordWrap(result.message, self)
        lbl_temp.setMinimumHeight(lbl_temp.sizeHint().height())
        self.work_layout._addWidget(lbl_temp)
        self.initUI()
        
    def startTask(self, item):
        if not hasattr (self, 'taskWidget'):
            self.taskWidget = TaskWidget(1, self)
            self.work_layout._addWidget(self.taskWidget)
        self.taskWidget.setText(item.message)
        self.taskWidget.setCurrentCount(item.id)
        self.initUI()
    
    def setTaskNumber(self, number):
        self.taskWidget = TaskWidget(int(number), self)
        self.work_layout._addWidget(self.taskWidget)
        self.initUI()
    
    def endTask(self, item):
        if hasattr (self, 'get_Progress_thread'):
            if self.get_Progress_thread.isRunning():
                self.get_Progress_thread.close()
                self.get_Progress_thread.wait()
        if hasattr(self,"taskWidget"):
            self.taskWidget.endCurrentTask()
            
            if item.message:
                self.taskWidget.setText(item.message)
        self.initUI()

    def beginFrame(self, item):
        pass

    def endFrame(self, item):
        self.work_layout.hide_kill_button()
        self.close_all_thread()

        if hasattr (self, 'taskWidget'):
            self.taskWidget.endCurrentTask()
            if item.message:
                self.taskWidget.setText(item.message)
        self._parent.control_button.add_clear_cache_button(self.cur_pid)
        self.initUI()

#    def clear_pid_cache(self):
#        self.clear_proc_cache_button.setDisabled(True)
#        sid = int(self.ClientObj.sid)
#        res = self.client.service.clear_pid_cache(sid, self.cur_pid)
#        if res:
#            show_msg (_('Error clear process cache on server'))
#        else:
#            self._parent.back()

    def startGroup(self, item):
        pass

    def endGroup(self, item):
        pass

    def kill_process(self, pid):
        def wrapper():
            sid = int(self.ClientObj.sid)

            # Call server method
            if hasattr (self, 'kill_process_thread'):
                if self.kill_process_thread.isRunning():
                    return 1
            self.kill_process_thread = ClientServiceThread(self.ClientObj,\
                                    'pid_kill', pid, sid)
            self.kill_process_thread.signal.connect(self.kill_process_after)
            self.kill_process_thread.start()
        return wrapper

    def kill_process_after(self, result):
        if result in [0,2]:
            return 0
        elif result == -1:
            msg = _("Certificate not found in the server!")
        elif result == -2:
            msg = _("Session not matching your certificate!")
        elif result == 1:
            msg = _("Failed to kill the process!")
        else:
            msg = 'error'
        show_msg(msg)
###############################################################################

    def analysis(self, meth_result):
            """ analysis of the bounced message method """
    #    for mess in meth_result:
            self.ClientObj.app.processEvents()

            mess = meth_result
            if mess.type == 'pid':
                self.get_messages(self.ClientObj.sid, mess.message)
            if mess.type == 'error':
                try:
                    self.show_error(self, mess.message)
                except:
                    pass
            if mess.type == 'warning':
                try:
                    self.show_warning(self, mess)
                except:
                    pass
            
    def get_message(self, item, sid, pid):
        try:
            if self.cur_pid != pid:
                return 1
        except:
            return 1
        """ get one message by its type """
        if not hasattr(item,"type"):
            return 1
        for case in switch(item.type):
            if case('normal'):
                 self.show_normal(item.message)
                 return 1
            if case('plain'):
                 self.show_normal(item.message)
                 return 1
            if case('pre'):
                 self.show_normal(item.message,mono=True)
                 return 1
            if case('progress'):
                 self.get_Progress(sid, pid, item.id)
                 return 1
            if case('error'):
    #            for i in range(20):
                self.show_error(item.message)
                if item.message == "403 Forbidden":
    #             show_msg(item.message, 'Access Denied')
                    return 0
                return 1
            if case('warning'):
                self.show_warning(item)
                return 1
            if case('table'):
                return 1
            if case('group'):
                self.show_group(sid, pid, item)
                return 1
            if case('question'):
                self.send_Message(sid, pid, item)
                return 1
            if case('confirm'):
                self.send_Confirm(sid, pid, item)
                return 1
            if case('choice'):
                self.send_Choice(sid, pid, item)
                return 1
            if case('password'):
                self.send_Password(sid, pid, item)
                return 1
            if case('startTask'):
                self.startTask(item)
                return 1
            if case('taskNumber'):
                self.setTaskNumber(item.message)
                return 1
            if case('endTask'):
                self.endTask(item)
                return 1
            if case('beginFrame'):
                self.beginFrame(item)
                return 1
            if case('endFrame'):
                self.endFrame(item)
                return 0
            if case('startGroup'):
                self.startGroup(item)
                return 1
            if case('endGroup'):
                self.endGroup(item)
                return 1
            if case(): # default, could also just omit condition or 'if True'
                return 1
    
    def get_messages(self, sid, pid):
        """ get frame in a separate thread """
        self.get_Frame(sid, pid)

    def get_Frame(self, sid, pid, def_end_frame = 1):
        """ get all messages, until type is not endFrame (or Error) """
        self.not_end_frame = def_end_frame

        if self.not_end_frame:
            # Call server method
            if hasattr (self, 'get_Frame_thread'):
                if self.get_Frame_thread.isRunning():
                    self.get_Frame_thread.wait()
            self.get_Frame_thread = ClientServiceThread(self.ClientObj,\
                           'get_frame', sid, pid, "gui", return_except = True, \
                           through_object = (sid, pid))
            self.get_Frame_thread.signal_extended.connect(self.get_Frame_cycle)
            self.get_Frame_thread.start()

    def get_Frame_cycle(self, current_frame, through_object):
        if type(current_frame) == Exception:
            _print (_("Failed to get a frame from the server."))
            show_msg(_("Failed to get a frame from the server.") +'\n'+ \
                     _('Try resfeshing this page later.'),'get frame error')
            return 1
        sid, pid = through_object
        if current_frame in [None, [], ""]:
            try:
                if  self.cur_pid != pid:
                    return 1
            except:
                return 1
        else:
            self.get_Frame_after(current_frame, sid, pid)
        if self.not_end_frame:
            # Call server method
            if hasattr (self, 'get_Frame_thread'):
                if self.get_Frame_thread.isRunning():
                    self.get_Frame_thread.wait()
            self.get_Frame_thread = ClientServiceThread(self.ClientObj,\
                           'get_frame', sid, pid, "gui", return_except = True, \
                           sleeptime = int(self.ClientObj.client.frame_period),\
                           through_object = (sid, pid))
            self.get_Frame_thread.signal_extended.connect(self.get_Frame_cycle)
            self.get_Frame_thread.start()

    def get_Frame_after(self, current_frame, sid, pid):
            for item in range(len(current_frame[0])):
                self.not_end_frame = self.get_message(current_frame[0][item], \
                                                                    sid, pid)
                try:
                    if  self.cur_pid != pid:
                        return 1
                except:
                    return 1
                if len (current_frame[0]) > 3:
                    if not item % 10:
                        self.ClientObj.app.processEvents()
                else:
                    self.ClientObj.app.processEvents()
                    self.valSignal.emit()

    def get_entire_frame(self, pid = None):
        """ get entire frame, from beginning (if client disconnected) """
        self.ClientObj.app.processEvents()

        sid = get_sid(self.ClientObj.client)
        if not pid:
            try:
                list_pid = self.ClientObj.client.service.list_pid(sid = sid)
            except urllib2.URLError, e:
                show_msg (e, _("Not connected!"))
                return 1
        else:
            list_pid = {}
            list_pid[0] = [pid]
        
        if list_pid[0] == [0]:
            return 0
        pid = list_pid[0][0]
        
        
        # Call server method
        if hasattr (self, 'get_Frame_thread'):
            if self.get_Frame_thread.isRunning():
                self.get_Frame_thread.wait()
        self.get_Frame_thread = ClientServiceThread(self.ClientObj,\
                       'get_entire_frame', sid, pid, return_except = True, \
                       through_object = (sid, pid))
        self.get_Frame_thread.signal_extended.connect \
                                                (self.get_entire_frame_after)
        self.get_Frame_thread.start()

    def get_entire_frame_after(self, current_frame, through_object):
        if type(current_frame) == Exception:
            _print (_('Failed to get the complete frame from the server.'))
            show_msg(_("Failed to get a frame from the server.") +'\n'+ \
                     _('Try resfeshing this page later.'),'get frame error')
            return 1
        sid, pid = through_object
        end_frame = 1
        if not current_frame in [None, [], ""]:
#                for i in range (10):
            for item in range(len(current_frame[0])):
                end_frame = self.get_message(current_frame[0][item], sid, pid)
                try:
                    if self.cur_pid != pid:
                        return 1
                except:
                    return 1
                if len (current_frame[0]) > 3:
                    if not item % 10:
                        self.ClientObj.app.processEvents()
                else:
                    self.ClientObj.app.processEvents()
        
        self.get_Frame(sid, pid, def_end_frame = end_frame)

    def get_Progress(self, sid, pid, id):
        if not hasattr (self, 'taskWidget'):
            self.taskWidget = TaskWidget(1, self)
            self.work_layout._addWidget(self.taskWidget)
            self.initUI()
        """ get progress for the current job """
        temp_progress = -1
        # Call server method
        if hasattr (self, 'get_Progress_thread'):
            if self.get_Progress_thread.isRunning():
                self.get_Progress_thread.wait()
        self.get_Progress_thread = ClientServiceThread(self.ClientObj,\
                       'get_progress', sid, pid, id, return_except = True, \
                       through_object = (sid, pid, id, temp_progress))
        self.get_Progress_thread.signal_extended.connect(self.get_Progress_cycle)
        self.get_Progress_thread.start()

    def get_Progress_cycle(self, returnProgr, through_object):
        if type(returnProgr) == Exception:
            _print (_('Failed to get the progress status from the server'))
            show_msg(_('Failed to get the progress status from the server')+\
                '\n'+_('Try resfeshing this page later.'),'get progress error')
            return 1
        sid, pid, id, temp_progress = through_object
########################################
        percent = returnProgr.percent
        if percent <= 100 and percent >= 0:
            if temp_progress != percent:

                if percent:
#                    progressBar.setProperty("value", 100)
                    if not hasattr (self, 'taskWidget'):
                        return 1
                    self.taskWidget.setProgress(percent)
                    if returnProgr.long_message:
                        self.show_normal(returnProgr.long_message)
                    elif returnProgr.short_message:
                        self.show_normal(returnProgr.short_message)
                if percent == 100:
                    return 0
                self.initUI()
                temp_progress = percent
            try:
                if  self.cur_pid != pid:
                    return 1
            except:
                return 1

            # Call server method
            if hasattr (self, 'get_Progress_thread'):
                if self.get_Progress_thread.isRunning():
                    self.get_Progress_thread.wait()
            self.get_Progress_thread = ClientServiceThread(self.ClientObj,\
                           'get_progress', sid, pid, id, return_except = True,\
                           sleeptime = 1, through_object = \
                           (sid, pid, id, temp_progress))
            self.get_Progress_thread.signal_extended.connect \
                                                    (self.get_Progress_cycle)
            self.get_Progress_thread.start()


        if percent < 0:
            returnProgr.percent = 0 - percent
            if not hasattr (self, 'taskWidget'):
                return 1
            self.taskWidget.setProgress(percent)
            self.ClientObj.app.processEvents()
            self.initUI()

    def send_Confirm(self, sid, pid, item):
        """ send answer to the question """
        self.message = ConfirmDialog(self, item, sid, pid)
        self.message.show()

    def send_Choice(self, sid, pid, item):
        """ send answer to the question """
        self.message = ChoiceDialog(self, item, sid, pid)
        self.message.show()

    def send_Message(self, sid, pid, item):
        """ send answer to the question """
        self.message = MessageDialog(self, item, sid, pid)
        self.message.show()
    
    def send_Password(self, sid, pid, item):
        """ send password """
        self.passwd = MessageDialog(self, item, sid, pid, True)
        self.passwd.show()

    def close_all_thread(self):
        if hasattr (self, 'get_Frame_thread'):
            if self.get_Frame_thread.isRunning():
                self.get_Frame_thread.close()
                self.get_Frame_thread.wait()

        if hasattr (self, 'get_Progress_thread'):
            if self.get_Progress_thread.isRunning():
                self.get_Progress_thread.close()
                self.get_Progress_thread.wait()

        if hasattr (self, 'send_password_thread'):
            if self.send_password_thread.isRunning():
                self.send_password_thread.close()
                self.send_password_thread.wait()

    def closeEvent(self, event):
        self.cur_pid = 'no'
        self.close_all_thread()
        self._parent.methodname_wgt._hide()
        self._parent.control_button._hide()
        if hasattr(self,"message") and self.message:
            self.message.close()
        if hasattr(self,"passwd") and self.passwd:
            self.passwd.close()
        event.accept()
            
class MessageDialog(QtGui.QWidget):
    # answer to the server question
    def __init__(self, parent, item, sid, pid, isPassword = False):
        super(MessageDialog, self).__init__()
        self._parent = parent
#        self.item = item
        self.sid = sid
        self.pid = pid
        self.layout = QtGui.QGridLayout(self)
        
        self.label = LabelWordWrap(item.message, self)

        self.pass_edit = QtGui.QLineEdit(self)
        self.layout.addWidget(self.label,0,0)
        self.layout.addWidget(self.pass_edit,0,1)
        
        x = 1
        if isPassword:
            self.pass_edit.setEchoMode(self.pass_edit.Password)
            if item.id == 2:        
                self.pass_edit2 = QtGui.QLineEdit(self)
                self.pass_edit2.setEchoMode(self.pass_edit2.Password)
                
                self.layout.addWidget(LabelWordWrap(_('Repeat'), self), x, 0)
                self.layout.addWidget(self.pass_edit2, x, 1)
                x += 1
                self.pass_edit.textChanged.connect(self.check_passwd)
                self.pass_edit2.textChanged.connect(self.check_passwd)
                

        self.send_button = QtGui.QPushButton(_('Send'), self)
        self.send_button.setShortcut(QtGui.QKeySequence(QtCore.Qt.Key_Return))
        self.send_button.clicked.connect(self.send_password)
        
        self.layout.addWidget(self.send_button,x,1)
                          
        self.move(parent.ClientObj._parent.geometry().x() + \
                  parent.ClientObj._parent.geometry().width() / 2 \
                  - self.sizeHint().width() / 2, \
                  parent.ClientObj._parent.geometry().y() + \
                  parent.ClientObj._parent.geometry().height() / 2 \
                  - self.sizeHint().height() / 2)
        self.setWindowFlags (QtCore.Qt.WindowMinimizeButtonHint)
#        self.setAttribute(QtCore.Qt.WA_ShowModal)
        
    def send_password(self):
        answer = self.pass_edit.text()
        if answer == self.pass_edit2.text():
            
            # Call server method
            if hasattr (self, 'send_password_thread'):
                if self.send_password_thread.isRunning():
                    return 1
            self.send_password_thread = ClientServiceThread(\
                            self._parent.ClientObj, 'send_message', self.sid, \
                            self.pid, answer, return_except = True)
            self.send_password_thread.signal.connect(self.send_password_after)
            self.send_password_thread.start()            
        else:
            self.send_button.setDisabled(True)

    def send_password_after(self, result):
        if type(result) == Exception:
            show_msg(_('Failed to send the message (password) to the server'))
            self.close()
            return 1
        self._parent.show_result(result)
        self.close()

    def check_passwd(self):
        if self.pass_edit.text() != self.pass_edit2.text():
            self.send_button.setDisabled(True)
        else:
            self.send_button.setEnabled(True)

class ConfirmDialog(QtGui.QWidget):
    # answer to the server question
    def __init__(self, parent, item, sid, pid):
        super(ConfirmDialog, self).__init__()
        self._parent = parent
#        self.item = item
        self.sid = sid
        self.pid = pid
        self.layout = QtGui.QGridLayout(self)

        self.label = LabelWordWrap(item.message, self)

        self.layout.addWidget(self.label,0,0,1,2)

        self.yes_button = QtGui.QPushButton(_('Yes'), self)
        self.yes_button.setShortcut(QtGui.QKeySequence(QtCore.Qt.Key_Return))
        self.yes_button.clicked.connect(self.sendYes)
        
        self.layout.addWidget(self.yes_button,1,0)

        self.no_button = QtGui.QPushButton(_('No'), self)
        self.no_button.setShortcut(QtGui.QKeySequence(QtCore.Qt.Key_Escape))
        self.no_button.clicked.connect(self.sendNo)
        
        self.layout.addWidget(self.no_button,1,1)
                          
        self.move(parent.ClientObj._parent.geometry().x() + \
                  parent.ClientObj._parent.geometry().width() / 2 \
                  - self.sizeHint().width() / 2, \
                  parent.ClientObj._parent.geometry().y() + \
                  parent.ClientObj._parent.geometry().height() / 2 \
                  - self.sizeHint().height() / 2)
        self.setWindowFlags (QtCore.Qt.WindowMinimizeButtonHint)
#        self.setAttribute(QtCore.Qt.WA_ShowModal)
        
    def send(self,answer):
        # Call server method
        if hasattr (self, 'send_password_thread'):
            if self.send_password_thread.isRunning():
                return 1
        self.send_password_thread = ClientServiceThread(\
                        self._parent.ClientObj, 'send_message', self.sid, \
                        self.pid, answer, return_except = True)
        self.send_password_thread.signal.connect(self.send_after)
        self.send_password_thread.start()            

    def sendYes(self):
        self.send("yes")

    def sendNo(self):
        self.send("no")

    def send_after(self, result):
        if type(result) == Exception:
            show_msg(_('Failed to send the message (password) to the server'))
            self.close()
            return 1
        self._parent.show_result(result)
        self.close()
