#!/usr/bin/env python

import os
import rospy
import rospkg
from std_msgs.msg import String

# reused from ros smach
from rosplan_rqt.xdot_qt import DotWidget

# common imports that work for both versions PyQt4 and PyQt5
from python_qt_binding import loadUi, QT_BINDING_VERSION

# check user Qt version and import libraries accordingly
if QT_BINDING_VERSION.startswith('4'):
    from PyQt4.QtGui import QWidget, QFileDialog
elif QT_BINDING_VERSION.startswith('5'):
    from PyQt5.QtWidgets import QWidget, QFileDialog
else:
    raise ValueError('Unsupported Qt version, supported versions: PyQt4, PyQt5')


class EsterelPlanViewerWidget(QWidget):

    def __init__(self, plugin=None):
        super(EsterelPlanViewerWidget, self).__init__()

        # Create QWidget
        ui_file = os.path.join(rospkg.RosPack().get_path('rosplan_rqt'), 'resource', 'esterel_plan_viewer.ui')
        loadUi(ui_file, self, {'DotWidget':DotWidget})
        self.setObjectName('ROSPlanEsterelPlanViewer')

        self.refreshButton.clicked[bool].connect(self._handle_refresh_clicked)
        self.saveButton.clicked[bool].connect(self._handle_save_button_clicked)

        self._sub = rospy.Subscriber('/rosplan_plan_dispatcher/plan_graph', String, self.planReceivedCallback)

        # flag used to zoom out to fit graph the first time it's received
        self.first_time_graph_received = True
        # to store the graph msg received in callback, later on is used to save the graph if needed
        self.graph = None
        # inform user that no graph has been received by drawing a single node in the rqt
        self.gen_single_node('no plan received')

    def gen_single_node(self, node_text):
        '''
        input: the node content (text)
        return dot code corresponding to a graph of 1 node
        '''
        # generate dot code (of a single node) from received text
        graph = 'digraph plan {0[ label="' + node_text + '",style=filled,fillcolor=white,fontcolor=black];}'
        # render single node graph
        self.xdot_widget.set_dotcode(graph)
        # zoom the single node to be clearly visible
        self.xdot_widget.zoom_image(5.0, center=True)

    def planReceivedCallback(self, msg):
        '''
        updating plan view
        '''
        # save graph in member variable in case user clicks save button later
        self.graph = msg.data
        # render graph using DotWidget class
        rospy.loginfo('Rendering graph started...')
        # inform the user his graph is being rendered
        if self.first_time_graph_received:
            self.gen_single_node('Plan received! rendering...')
        # start rendering graph, might take a while depending on the graph size
        self.xdot_widget.set_dotcode(msg.data)
        if self.first_time_graph_received:
            # zoom out until graph fits in screen
            self.xdot_widget.zoom_to_fit()
            # only zoom to fit for the first graph
            self.first_time_graph_received = False
        self.xdot_widget.update()
        rospy.loginfo('Rendering graph ended !')

    def _handle_refresh_clicked(self, checked):
        '''
        called when the refresh button is clicked
        '''
        self._sub.unregister()
        self._sub = rospy.Subscriber(self.topicText.text(), String, self.planReceivedCallback)

    def save_graph(self, full_path):
        '''
        check if last graph msg received is valid (non empty), then save in file.dot
        '''
        if self.graph:
            dot_file = open(full_path,'w')
            dot_file.write(self.graph)
            dot_file.close()
            rospy.loginfo('graph saved succesfully in %s', full_path)
        else:
            # if self.graph is None it will fall in this case
            rospy.logerr('Could not save Graph: is empty, currently subscribing to: %s, try' +\
                         ' clicking "Update subscriber" button and make sure graph is published at least one time'\
                         , self.topicText.text())

    def _handle_save_button_clicked(self, checked):
        '''
        called when the save button is clicked
        '''
        rospy.loginfo('Saving esterel plan to dot file')
        fileName = QFileDialog.getSaveFileName(self, 'Save Esterel plan to dot file','','Graph xdot Files (*.dot)')
        if fileName[0] == '':
            rospy.loginfo("User has cancelled saving process")
        else:
            # add .dot at the end of the filename
            full_dot_path = fileName[0]
            if not '.dot' in full_dot_path:
                full_dot_path += '.dot'
            rospy.loginfo("path to save dot file: %s", full_dot_path)
            self.save_graph(full_dot_path)

    # Qt methods
    def shutdown_plugin(self):
        pass

    def save_settings(self, plugin_settings, instance_settings):
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        pass
