import sys
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtGui import QPainter,QPainterPath
from PyQt5 import QtGui
from PyQt5.QtCore import Qt
from scipy.interpolate import interp1d
import time 

import numpy as np
# from transaction_editor import TransactionEditor
# from agent_editor import AgentEditor

from .agent_editor import CodeEditor

class Box:

    def __init__(self,x,y, name,ishelper=False,parent=None,parent_widget=None):

        self.x = x
        self.y = y

        self.x0 = x
        self.y0 = y


        self.name = name #.capitalize()
        self.ishelper = ishelper
        self.parent = parent
        self.n_connections = 0

        self.parent_widget = parent_widget
    

    def edit_agent(self):
        if not self.ishelper:
            print("EDIT AGENT CODE", self.name)

            start = ""
            if self.name in self.parent_widget.code_data:
                start = self.parent_widget.code_data[self.name]

            # workaround for old file versions
            if self.name.lower() in self.parent_widget.code_data:
                start = self.parent_widget.code_data[self.name.lower()]

            if self.name not in CodeEditor.instances:
                ce = CodeEditor(self.parent_widget,self,start)
                ce.setStyleSheet(ce.parent().parent().theme_manager.get_stylesheet("main"))
                ce.setStyleSheet(ce.parent().parent().theme_manager.get_background_style())



        else:
            print("CANNOT EDIT HELPER")

    def overlaps(self,boxes):
        if self.ishelper:
            e = 8
        else:
            return False

        for box in boxes:

            if self.x - box.x < e:
                return True
            if self.y - box.y < e:
                return True

        return False

    def adjust_position(self,boxes):
        k = 0
        while self.overlaps(boxes) and k < 100:

            self.x = self.x0 + np.random.uniform(-45,45)
            self.y = self.y0 + np.random.uniform(-45,45)

            try:
                self.x = np.clip(self.x ,1,self.parent_widget.W - 45) # 799)
                self.y = np.clip(self.y ,1,self.parent_widget.H -45) # 699)
                # TODO fix parent_widget is None sometimes
            except:
                pass

            k+= 1


class Connector:
    def __init__(self,a,b,name="Connector",items=None):
        """
        creates a new connector

        :param a: a box (a) sender
        :param b: a box (b) receiver
        :param name: the label of the conncetor
        :param items: items affected by this connector (list of str)
        """

        self.a = a
        self.b = b

        if items is None:
            self.items = []
        else:
            self.items =items

        #self.a.n_connections += 1
        #if self.a != self.b:
        #    self.b.n_connections += 1
        # ^moved to add_connection

        dx = self.b.x +25/2. - self.a.x #+ 25
        dy = self.b.y +25/2. - self.a.y #+ 25

        u = np.random.uniform(-1,1)
        v = np.random.uniform(-1,1)

        if a != b:
            self.c = Box(self.a.x + 0.5*dx + u, self.a.y + 0.5*dy + v,name,ishelper=True,parent=self)
            # self.d = Box(self.a.x + 0.75*dx, self.a.y + 0.75*dy,None,ishelper=True,parent=self)
        else:
            self.c = Box(self.a.x + 5*u, self.a.y + 5*u ,name,ishelper=True,parent=self)

        self.a.ishelper = False
        self.b.ishelper = False

class MyDrawWidget(QtWidgets.QWidget):

    def __init__(self,parent):

        super().__init__(parent)

        # self.W = int(self.parent().frameGeometry().width()-1030) # 850
        # self.H = 750
        # self.setGeometry(0,0,self.W,self.H)

        self.resize_frame()


        self.boxes = []
        self.connectors = []

        self.old_positions = {}

        self.code_data = {}

        self.selected = None
        self.connected1 = None
        self.connected2 = None
        self._highlighted = None

        self.last_click = time.time()

        self.parent().zoomOutButton.pressed.connect(self.zoom_out)
        self.parent().zoomInButton.pressed.connect(self.zoom_in)

        self.parent().graphEditButton.setEnabled(False)

        self.setMouseTracking(True)

        self.W0 = int(self.parent().frameGeometry().width()-970) # 850
        self.H0 = 850 # 750

        self.offx = 0
        self.offy = 0 # temporary offset when moving a box1

        self.gox = 0   # global offset x-axis
        self.goy = 0   # global offset y-axis
        self.go_startx = 0 # starting position when moving global offset
        self.go_starty = 0
        self.gox_start = 0
        self.goy_start = 0 # starting position before moving global offset

        self.maxx = self.W-50 # 800
        self.maxy = self.H- 50 # 700

        self.mousex = 0
        self.mousey = 0

        self.mode = "select"

        self.zoom_factor = 1.0

        self.show()

    def wheelEvent(self,event):

        numDegrees = event.angleDelta().y()
        numSteps = numDegrees / 15
        if numSteps > 0:
            self.zoom_in()
        else:
            self.zoom_out()
        event.accept()

    def zoom_in(self):
        self.zoom_factor += 0.1

        if self.zoom_factor > 3.0:
            self.zoom_factor = 3.0


        self.update()

    def zoom_out(self):

        self.zoom_factor -= 0.1
        if self.zoom_factor < .8:
            self.zoom_factor = .8

        self.update()

    def resize_frame(self):
        self.W = max(500,int(self.parent().frameGeometry().width()-970)) # 850
        self.H = max(850,int(850+self.parent().frameGeometry().height()-986)) # 750


        self.maxx = self.W- 50 # 800
        self.maxy = self.H- 50 # 700
        #self.setGeometry(890,170,self.W,self.H)
        self.setGeometry(950,80,self.W,self.H)

    @property
    def highlighted(self):
        return self._highlighted

    @highlighted.setter
    def highlighted(self,val):
        if val is None:
            self.parent().graphEditButton.setEnabled(False)
            self.parent().graphDeleteButton.setEnabled(False)

            self._highlighted = val
        else:
            if not val.ishelper: # is not a helper node (connection node)
                self.parent().graphEditButton.setEnabled(True)
                # self.parent().graphDeleteButton.setEnabled(True)
                if val.n_connections == 0:
                    self.parent().graphDeleteButton.setEnabled(True)
                else:
                    print("Delete disabled because has still %i connections" % val.n_connections)
                    self.parent().graphDeleteButton.setEnabled(False)
            else:
                print("Cannot delete (is helper)")
                self.parent().graphEditButton.setEnabled(False)
                self.parent().graphDeleteButton.setEnabled(False)

            self._highlighted = val

    def box_positions(self):
        pos_data = {}

        for box in self.boxes:
            pos_data[box.name] = {"x": int(box.x), "y": int(box.y)}

        return pos_data

    def reposition(self,pos_data):
        # reposition all the boxes
        print("reposition <-> ", pos_data)
        for box in self.boxes:
            if box.name in pos_data:

                print("reposition",box.name,pos_data[box.name])
                box.x = pos_data[box.name]["x"]
                box.y = pos_data[box.name]["y"]
                box.x0 = box.x
                box.y0 = box.y

    def edit_agent(self):
        if self.highlighted is not None:
            self.highlighted.edit_agent()

    def remove_agent(self):
        # remove currently selected agent if it has no connections
        box = self.highlighted
        if box is not None and box.n_connections <= 0:
            if box in self.boxes:
                self.boxes.remove(box)
            if box.name in self.code_data:
                del self.code_data[box.name]
            self.update()
        else:
            self.parent().notify("Box has still %i connection(s)! Cannot remove"%box.n_connections, title="Error")

    def check_exist(self,name):
        if name == "":
            return False

        for box in self.boxes:
            # TODO more efficient search for large number of boxes?
            if box.name == name:
                return True
        return False

    def add_agent(self, name):

        # mechanism to avoid overwriting...
        for box in self.boxes:
            if not box.ishelper and box.name == name:
                return box

        # actually a new agent?
        if name not in self.old_positions:
            u = np.random.uniform(40-self.gox,self.W-40-self.goy)
            v = np.random.uniform(40-self.gox ,self.H-40-self.goy)
        else:
            u,v = self.old_positions[name]

        new_box = Box(u,v,name,ishelper=False,parent_widget=self)
        self.boxes.append(new_box)
        self.update()

        return new_box

    def arrange_pretty(self):
        for box in self.boxes:
            box.adjust_position(self.boxes)

        self.update()

    def highlight_connector(self,name):
        for box in self.boxes:
            if box.ishelper and box.name == name or box.name.lower() == name.lower():
                self.highlighted = box

        self.update()

    def add_connection(self,box1, box2, name,items):

        #for box in self.boxes:
        #    if box.ishelper and box.name == name:
        #        return

        new_conn = Connector(box1,box2,name,items)

        if name not in self.old_positions:
            self.old_positions[name] = new_conn.c.x, new_conn.c.y
        else:
            new_conn.c.x = self.old_positions[name][0]
            new_conn.c.y = self.old_positions[name][1]
            box1.n_connections += 1
            box2.n_connections += 1

        self.connectors.append(new_conn)
        self.boxes.append(new_conn.c)

        return new_conn

    def clear(self,clearall=False):

        if clearall:
            boxes = [b for b in self.boxes]

        else:
            boxes = [b for b in self.boxes if b.ishelper]

        while len(boxes) > 0:
            b = boxes.pop()
            self.boxes.remove(b)
            self.old_positions[b.name] = (b.x, b.y)

        while len(self.connectors) > 0:
            self.connectors.pop()


    def rename_connection(self,name,new_name):
        for box in self.boxes:
            if box.ishelper and box.name == name:
                box.name = new_name
                self.update()
                self.old_positions[box.name] = (box.x,box.y)


    def remove_connection(self,name):
        self.update()

        self.highlighted = None
        self.selected = None
        print("remove connection",name)

        for box in self.boxes:
            if box.ishelper and box.name == name:
                print("*BOX a", box.parent.a.n_connections)
                print("*BOX b", box.parent.b.n_connections)

                box.parent.a.n_connections -= 1
                box.parent.b.n_connections -= 1

                print("remove box", box, box.name)
                print("BOX a", box.parent.a.n_connections)
                print("BOX b", box.parent.b.n_connections)

                self.boxes.remove(box)
                self.connectors.remove(box.parent)

                if False:
                    # TODO ask dialog if boxes are to be deleted if they have no longer any connection to other boxes...
                    if box.parent.a.n_connections == 0:
                        self.boxes.remove(box.parent.a)
                    if box.parent.b.n_connections == 0:
                        self.boxes.remove(box.parent.b)

                # print("self.boxes",[b.name for b in self.boxes])

                self.old_positions[box.name] = (box.x, box.y)
                self.old_positions[box.parent.a.name] = (box.parent.a.x,box.parent.a.y)
                self.old_positions[box.parent.b.name] = (box.parent.b.x,box.parent.b.y)

                self.update()
                return


    def draw_bezier(self,qp,x0,y0,xf,yf,xi,yi):
        """
        DEPRICATED draws a bezier line between x0,y0,xf and yf via xi,yi
        """

        # add offset
        dx = 0
        dy = 0

        # first quadrant (bottom right)
        if xf >= x0 and yf >= y0:
            x0 += 10
            xf -= 10

        # second quadrant (bottom left)
        if xf < x0 and yf >= y0:
            pass

        # third quadrant (top left)
        if xf < x0 and yf < y0:
            pass

        # fourth quadrant (top right)
        if xf >= x0 and yf < y0:
            pass

        t_vals = [0,0.2,0.4,0.6,0.8,1.0,1.1]
        points = []
        for t in t_vals:
            x_temp = (1-t)*((1-t)*x0+t*xi) + t*((1-t)*xi + t*xf)
            y_temp = (1-t)*((1-t)*y0+t*yi) + t*((1-t)*yi + t*yf)

            points.append((x_temp,y_temp))



        for i in range(len(points)-2):
            qp.drawLine(points[i][0],points[i][1],points[i+1][0],points[i+1][1])



    def curved_connector(self,qp,x0,y0,xf,yf,xi,yi,dashed=False):
        """
        draws a curved connector
        """

        # construct helper box_positions
        xi2 = 0.5*(xi-x0) + x0
        xi3 = 0.5*(xf-xi) + xi
        yi2 = 0.5*(yi-y0) + y0
        yi3 = 0.5*(yf-yi) + yi

        #qp.drawRect(xi2-5,yi2-5,10,10)
        #qp.drawRect(xi3-5,yi3-5,10,10)
        W = 25 # 12.5

        xi4 = xi
        yi4 = yi
        xi5 = xf
        yi5 = yf

        # first quadrant (bottom right)
        if xf >= xi and yf >= yi and abs(xi-xf) >W:
            xi4 = xi+W
            yi4 = yi
            xi5 = xf-W
            yi5 = yf

        # second quadrant (bottom left)
        if xf < xi and yf >= yi and abs(xi-xf) >W:
            xi4 = xi-W
            yi4 = yi
            xi5 = xf+W
            yi5 = yf

        # third quadrant (top left)
        if xf < xi and yf < yi and abs(xi-xf) >W:
            xi4 = xi-W
            yi4 = yi
            xi5 = xf+W
            yi5 = yf

        # fourth quadrant (top right)
        if xf >= xi and yf < yi and abs(xi-xf) >W:
            xi4 = xi+W
            yi4 = yi
            xi5 = xf-W
            yi5 = yf

        xi6 = x0
        yi6 = y0
        xi7 = xi
        yi7 = yi


        # first quadrant (bottom right)
        if xi >= x0 and yi >= y0 and abs(xi-x0) >W:
            xi6 = x0+W
            yi6 = y0
            xi7 = xi-W
            yi7 = yi

        # second quadrant (bottom left)
        if xi < x0 and yi >= y0 and abs(xi-x0) >W:
            xi6 = x0-W
            yi6 = y0
            xi7 = xi+W
            yi7 = yi

        # third quadrant (top left)
        if xi < x0 and yi < y0 and abs(xi-x0) >W:
            xi6 = x0-W
            yi6 = y0
            xi7 = xi+W
            yi7 = yi

        # fourth quadrant (top right)
        if xi >= x0 and yi < y0 and abs(xi-x0) >W:
            xi6 = x0+W
            yi6 = y0
            xi7 = xi-W
            yi7 = yi


        #qp.drawRect(xi4-5,yi4-5,10,10)
        #qp.drawRect(xi5-5,yi5-5,10,10)
        #qp.drawRect(xi6-5,yi6-5,10,10)
        #qp.drawRect(xi7-5,yi7-5,10,10)

        xi62 = 0.5*(xi6+xi2)
        yi62 = 0.5*(yi6+yi2)

        """
        if self.darkMode:
            pen = QtGui.QPen(QtGui.QColor(50, 50, 50 , 255),0) # ,2, Qt.SolidLine)
        else:
            pen = QtGui.QPen(QtGui.QColor(0, 0,0 , 255),0) # ,2, Qt.SolidLine)
        """
        #pen = QtGui.QPen(self.parent().theme_manager.get_color(1),0)
        #qp.setPen(pen)

        xi35 = 0.5*(xi3+xi5)
        yi35 = 0.5*(yi3+yi5)

        """
        qp.drawLine(x0,y0,xi6,yi6)
        qp.drawLine(xi6,yi6,xi62,yi62)
        qp.drawLine(xi62,yi62,xi2,yi2)
        qp.drawLine(xi2,yi2,xi,yi)
        qp.drawLine(xi,yi,xi3,yi3)

        qp.drawLine(xi3,yi3,xi3,yi3)
        qp.drawLine(xi3,yi3,xi35,yi35)
        qp.drawLine(xi35,yi35,xi5,yi5)
        qp.drawLine(xi5,yi5,xf,yf)
        """

        # https://stackoverflow.com/questions/52014197/how-to-interpolate-a-2d-curve-in-python
        # define some points
        if abs(x0-xf) < 0.1 and abs(y0-yf) < 0.1:
            points2 = np.array([[x0,xi6,xi62,xi2,xi],
                                [y0,yi6,yi62,yi2,yi]]).T
        else:
            points2 = np.array([[x0,xi6,xi62,xi2,xi,xi3,xi35,xi5,xf],
                          [y0,yi6,yi62,yi2,yi,yi3,yi35,yi5,yf]]).T
            
            points2 = np.array([[x0,xi2,xi,xi3,xf],
                          [y0,yi2,yi,yi3,yf]]).T
        
        
        points = []
        points.append(points2[0]) # make sure it always starts at x0,y0 and ends at xf, yf

        for i in range(2,len(points2)):
            if points2[i-1][0] == points2[i][0]:
                pass
            else:
                points.append(points2[i])

        points = np.array(points)

        # Linear length along the line:
        try:
            distance = np.cumsum( np.sqrt(np.sum( np.diff(points, axis=0)**2, axis=1 )) )
            distance = np.insert(distance, 0, 0)/distance[-1]

            # Interpolation for different methods:
            #interpolations_methods = ['slinear', 'quadratic', 'cubic']
            method  = "cubic" # "quadratic" 
            alpha = np.linspace(0, 1, 100)
            
            #for method in interpolations_methods:
            interpolator =  interp1d(distance, points, kind=method, axis=0)
            interpolated_points= interpolator(alpha)
            
            
            # print("interpolated_poitns",interpolated_points)
            # draw curved line 
            for i in range(1,len(interpolated_points)):
                qp.drawLine(self.zoom_factor*(interpolated_points[i-1][0]),self.zoom_factor*(interpolated_points[i-1][1]),
                            self.zoom_factor*(interpolated_points[i][0]),self.zoom_factor*(interpolated_points[i][1]))

            
            # draw arrow at mid point 
            n = len(interpolated_points)
            mid_point = interpolated_points[int(n/2)]
            mid_point2 = interpolated_points[int(n/2)+1]
            mid_points3 = interpolated_points[int(n/2)-2]
            
            dy = mid_point[1] - mid_point2[1]
            dx = mid_point[0] - mid_point2[0]
            
            alfa = np.arctan2(dy,dx)
            beta1 = alfa + np.pi/4
            beta2 = alfa - np.pi/4
            
            if abs(dy) > abs(dy):
                beta3 = alfa - np.pi/3
            else:
                beta3 = alfa - np.pi/3 + np.pi/2
            
            x2 = mid_point2[0]
            y2 = mid_point2[1]
            
            d = 10
            x3 = np.cos(beta1)*d + x2 
            y3 = np.sin(beta1)*d + y2
            
            x4 = np.cos(beta2)*d + x2 
            y4 = np.sin(beta2)*d + y2
            
            xt = np.cos(beta3)*21 + mid_points3[0]
            yt = np.sin(beta3)*21 + mid_points3[1]
            
            
            # left side 
            qp.drawLine(self.zoom_factor*(x2), self.zoom_factor*(y2), self.zoom_factor*(x3), self.zoom_factor*(y3))
            
            # right side 
            qp.drawLine(self.zoom_factor*(x2), self.zoom_factor*(y2), self.zoom_factor*(x4), self.zoom_factor*(y4))
            
            
            

        except Exception as e:
            print("Error in Line Interpolation:",str(e))

        """
        if self.darkMode:
            pen = QtGui.QPen(QtGui.QColor(255, 255, 255 , 255),0) # ,2, Qt.SolidLine)
        else:
            pen = QtGui.QPen(QtGui.QColor(0, 0,0 , 255),0) # ,2, Qt.SolidLine
        """

        #pen = QtGui.QPen(self.parent().theme_manager.get_color(3),0)
        #qp.setPen(pen)

        # points are [y0,xi6,xi2,xi3,xi5,xf]

        # qp.drawLine(xi2,yi2,xi5,yi5)

        #qp.drawLine(xi2,yi2,xi7,yi7)
        #qp.drawLine(xi7,yi7,xf,yf)

        #qp.drawLine(xi2,yi2,xf,yf)

        #self.draw_bezier(qp,xi4,yi4,xi5,yi5,xi3,yi3) # starting point
        #self.draw_bezier(qp,xi7,yi7,xi6,yi6,xi2,yi2) # starting point
        #self.draw_bezier(qp,xi,yi,xi3,yi3,xi7,yi7) # starting point
        #self.draw_bezier(qp,xi3,yi3,xf,yf,xi5,yi5) # starting point

        return xt,yt

    def dawPreview(self,qp):
        # draw small preview window
        # param qp: a qpainter object

        pen = QtGui.QPen(self.parent().theme_manager.get_color(2),1)
        qp.setPen(pen)

        # draw Origin
        #qp.drawLine(self.zoom_factor*(0.5*self.W0-5+self.gox),self.zoom_factor*(0.5*self.H0+self.goy),self.zoom_factor*(0.5*self.W0+5+self.gox),self.zoom_factor*(0.5*self.H0+self.goy))
        #qp.drawLine(self.zoom_factor*(0.5*self.W0+self.gox),self.zoom_factor*(0.5*self.H0-5+self.goy),self.zoom_factor*(0.5*self.W0+self.gox),self.zoom_factor*(0.5*self.H0+5+self.goy))

        w_frame = 80
        h_frame = 80
        distance = 25
        move_x = -10-590-130 #  distance + 10
        move_y = -10 # -25

        brush = QtGui.QBrush(self.parent().theme_manager.get_color(7))
        qp.fillRect(QtCore.QRect(move_x + self.W0-distance-w_frame,move_y + self.H-distance-h_frame,w_frame+25,h_frame+25),brush)

        if len(self.boxes) == 0:
            return

        x_min = min(0,-self.gox)
        x_max = max(self.W,self.W-self.gox)
        y_min = min(0,-self.goy)
        y_max = max(self.H,self.H-self.goy)
        x_vals = []
        y_vals = []

        for box in self.boxes:
            x_vals.append(box.x)
            y_vals.append(box.y)


        x_max = max(np.max(x_vals),-self.gox)
        y_max = max(np.max(y_vals),-self.goy)
        x_min = min(np.min(x_vals),-self.gox)
        y_min = min(np.min(y_vals),-self.goy)

        if x_max == x_min:
            x_max = x_min + 10
        if y_max == y_min:
            y_max = y_min + 10

        if x_max < self.W:
            x_max = self.W
        if y_max < self.H:
            y_max = self.H

        x_vals = [self.zoom_factor*(xi+self.gox)/10 for xi in x_vals]
        y_vals = [self.zoom_factor*(yi+self.goy)/10 for yi in y_vals]

        # origin
        x_vals.append(self.zoom_factor/10 *(self.gox + 0.5*self.W))
        y_vals.append(self.zoom_factor/10 *(self.goy + 0.5*self.H))

        # pointer
        x_vals.append(1/10 *(0.5*self.W))
        y_vals.append(1/10 *(0.5*self.H))

        mx = np.mean(x_vals)
        my = np.mean(y_vals)

        x_vals = [x-mx for x in x_vals]
        y_vals = [y-my for y in y_vals]

        dx = max(-np.min(x_vals),np.max(x_vals))
        dy = max(-np.min(y_vals),np.max(y_vals))

        x_vals = [0.5*w_frame*x/dx for x in x_vals]
        y_vals = [0.5*h_frame*y/dy for y in y_vals]

        x_vals = [x+0.5*w_frame for x in x_vals]
        y_vals = [y+0.5*h_frame for y in y_vals]

        x_vals = [move_x + self.W0-w_frame+x-2-10 for x in x_vals]
        y_vals = [move_y + self.H-h_frame+y-2-10 for y in y_vals]

        # transform all points

        for i in range(len(x_vals)-2):
            x = x_vals[i]
            y = y_vals[i]
            if self.boxes[i].ishelper:
                    qp.drawRect(x,y,1,1)
            else:
                #if self.boxes[i] == self.highlighted:
                #    pen = QtGui.QPen(self.parent().theme_manager.get_color(3),1)
                #    brush = QtGui.QBrush(self.parent().theme_manager.get_color(3))
                #    qp.setPen(pen)

                #    qp.fillRect(QtCore.QRect(x,y,4,4),brush)
                #else:
                qp.drawRect(x,y,4,4)

        # skip origin ...
        x = x_vals[-1]
        y = y_vals[-1]
        pen = QtGui.QPen(self.parent().theme_manager.get_color(4),1)
        qp.setPen(pen)
        ox = x
        oy =  y
        d = 5
        qp.drawLine(ox-d,oy,ox+d,oy)
        qp.drawLine(ox,oy-d,ox,oy+d)

        # draw Origin
        # ox = (self.gox-x_min)/(x_max-x_min)*(w_frame-distance)
        # oy = (self.goy-y_min)/(y_max-y_min)*(h_frame-distance)
        # dx = 5
        # dy = 5
        # ox_transformed = move_x + self.W-w_frame+ox-2
        # oy_transformed = move_y + self.H-h_frame+oy-2

        # qp.drawLine(ox_transformed-dx,oy_transformed,ox_transformed+dx,oy_transformed)
        # qp.drawLine(ox_transformed,oy_transformed-dy,ox_transformed,oy_transformed+dy)



    def paintEvent(self, event):

        qp = QtGui.QPainter(self)

        """
        if self.darkMode:
            pen = QtGui.QPen(QtGui.QColor(120, 120, 120, 255),1) # ,2, Qt.SolidLine)
        else:
            pen = QtGui.QPen(QtGui.QColor(0, 0, 0, 255),1) # ,2, Qt.SolidLine)
        """
        pen = QtGui.QPen(self.parent().theme_manager.get_color(2),1)

        qp.setPen(pen)

        qp.drawRect(0,0,self.W-2,self.H-2)

        #pen2 = QtGui.QPen(self.parent().theme_manager.get_color(2),1)
        #qp.setPen(pen2)
        #qp.drawRect(0+self.gox,0+self.goy,self.gox + self.W-2,self.goy + self.H-2)

        qp.setPen(pen)

        filter_vals = set([item.text() for item in self.parent().filterCombo.selectedItems()])
        active_boxes = []
        active_connectors = []

        for conn in self.connectors:
            no_selection = len(filter_vals) == 0
            is_selected = len(filter_vals) != 0 and len(list(set(conn.items) & filter_vals)) > 0
            if no_selection or is_selected:
                if conn not in active_connectors:
                    active_connectors.append(conn)


        for conn in self.connectors:

            if conn.c == self.highlighted:

                pen = QtGui.QPen(self.parent().theme_manager.get_color(4),2.0) # ,2, Qt.SolidLine)

            else:
                # pen = QtGui.QPen(QtGui.QColor(0, 0, 0, 200),1) # ,2, Qt.SolidLine)

                """
                if self.darkMode:
                    pen = QtGui.QPen(QtGui.QColor(120, 120, 120, 255),1) # ,2, Qt.SolidLine)
                else:
                    pen = QtGui.QPen(QtGui.QColor(0, 0,0 , 75),0) # ,2, Qt.SolidLine)
                """

                pen = QtGui.QPen(self.parent().theme_manager.get_color(2),1)

                if conn in active_connectors:
                    pen.setStyle(Qt.SolidLine)
                    pen.setWidthF(1.0)

                else:
                    pen.setStyle(Qt.DashLine)
                    #pen.setDashOffset(4.0)
                    pen.setDashPattern([1,4])
                    pen.setWidthF(0.8)


            qp.setPen(pen)

            # qp.drawLine(int(conn.a.x+25), int(conn.a.y+25), int(conn.c.x+5), int(conn.c.y+5))
            # qp.drawLine(int(conn.c.x+5), int(conn.c.y+5), int(conn.b.x+25), int(conn.b.y+25))

            # apply filter: does the connector contain any of the filter_vals?
            if conn in active_connectors:
                # draw only if filter applies

                tx,ty = self.curved_connector(qp,self.gox + int(conn.a.x+12.5), self.goy + int(conn.a.y+12.5),self.gox + int(conn.b.x+12.5), self.goy + int(conn.b.y+12.5), self.gox + int(conn.c.x+5), self.goy + int(conn.c.y+5))
                # self.curved_connector(qp,int(conn.a.x), int(conn.a.y),int(conn.b.x), int(conn.b.y),int(conn.c.x), int(conn.c.y))
                if conn.a not in active_boxes:
                    active_boxes.append(conn.a)
                if conn.b not in active_boxes:
                    active_boxes.append(conn.b)
                if conn.c not in active_boxes:
                    active_boxes.append(conn.c)


                if conn.c == self.highlighted:
                    pen = QtGui.QPen(self.parent().theme_manager.get_color(2),1)
                    qp.setPen(pen)
                else:
                    pen = QtGui.QPen(self.parent().theme_manager.get_color(1),1)
                    qp.setPen(pen)
                
                #qp.drawText(self.zoom_factor*(self.gox +conn.c.x),self.zoom_factor*(self.goy + conn.c.y-7),conn.c.name)
                qp.drawText(self.zoom_factor*(tx),self.zoom_factor*(ty),conn.c.name)
                
                
            else:
                self.curved_connector(qp,self.gox + int(conn.a.x+12.5), self.goy + int(conn.a.y+12.5),self.gox + int(conn.b.x+12.5), self.goy + int(conn.b.y+12.5), self.gox + int(conn.c.x+5), self.goy + int(conn.c.y+5),dashed=True)

        """
        if self.darkMode:
            pen = QtGui.QPen(QtGui.QColor(120, 120, 120, 255),1) # ,2, Qt.SolidLine)
        else:
            pen = QtGui.QPen(QtGui.QColor(0, 0,0 , 75),0) # ,2, Qt.SolidLine)
        """
        pen = QtGui.QPen(self.parent().theme_manager.get_color(2),1)
        pen.setStyle(Qt.SolidLine)

        #pen = QtGui.QPen(QtGui.QColor(0, 0,0 , 200),1) # ,2, Qt.SolidLine)

        qp.setPen(pen)

        # print("self.boxes",[b.name for b in self.boxes])
        for box in self.boxes:

            if box != self.selected: # and box != self.connected2:

                x = box.x
                y = box.y

            elif box == self.selected:

                x = self.mousex + self.offx
                y = self.mousey + self.offy

                #x = box.x * (1-self.zoom_factor) + new_x *(self.zoom_factor)
                #y = box.y * (1-self.zoom_factor) + new_y *(self.zoom_factor)


            #x = box.x
            #y = box.y

            if box == self.selected:
                br = QtGui.QBrush(QtGui.QColor(34, 34, 230, 255))
            elif box == self.connected1:
                br = QtGui.QBrush(QtGui.QColor(100, 10, 10, 255))
            elif box.ishelper and box != self.highlighted:

                """
                if self.darkMode:
                    br = QtGui.QBrush(QtGui.QColor(255, 255, 255, 255))
                else:
                    br = QtGui.QBrush(QtGui.QColor(74, 138, 217, 255))
                """
                br = QtGui.QBrush(self.parent().theme_manager.get_color(0),1)


            elif box == self.highlighted:
                br = QtGui.QBrush(self.parent().theme_manager.get_color(5))
            else:
                """
                if self.darkMode:
                    br = QtGui.QBrush(QtGui.QColor(20, 20, 20, 255))
                else:
                    br = QtGui.QBrush(QtGui.QColor(200, 200, 200, 255))
                """
                br = QtGui.QBrush(self.parent().theme_manager.get_color(0),1)

            x = int(x)
            y = int(y)

            if box.ishelper:
                pen = QtGui.QPen(self.parent().theme_manager.get_color(6),1)
                qp.setPen(pen)
                # qp.drawText(x,y-7,box.name)
            else:
                #qp.drawText(x,y-3,box.name.capitalize())
                pen = QtGui.QPen(self.parent().theme_manager.get_color(6),1)
                qp.setPen(pen)
                qp.drawText(self.zoom_factor*(x+self.gox) ,self.zoom_factor*(y-7+self.goy),box.name)

            qp.setBrush(br)

            if box.ishelper:

                x2 = x + 6
                y2 = y + 6


                pen = QtGui.QPen(QtGui.QColor(0, 0, 0 , 0),0) # ,2, Qt.SolidLine)
                qp.setPen(pen)
                # qp.drawRect(QtCore.QRect(QtCore.QPoint(x,y),QtCore.QPoint(x2,y2)))
                
                
                
                if box in active_boxes:
                    qp.drawEllipse(QtCore.QPoint(self.zoom_factor*(self.gox + x+4),self.zoom_factor*(self.goy + y+4)),6, 6)
                else:
                    qp.drawEllipse(QtCore.QPoint(self.zoom_factor*(self.gox + x+4),self.zoom_factor*(self.goy + y+4)),2, 2)
                
                
                
                """
                if self.darkMode:
                    pen = QtGui.QPen(QtGui.QColor(120, 120, 120 , 200),0) # ,2, Qt.SolidLine
                else:
                    pen = QtGui.QPen(QtGui.QColor(0, 0, 0 , 200),0) # ,2, Qt.SolidLine)
                """
                pen = QtGui.QPen(self.parent().theme_manager.get_color(2),1)
                qp.setPen(pen)

            else:

                W = 45

                x2 = x + W
                y2 = y + W

                pen = QtGui.QPen(QtGui.QColor(0, 0, 100 , 0),0) # ,2, Qt.SolidLine)
                #br = QtGui.QBrush(QtGui.QColor(0, 0, 100, 255))
                qp.setPen(pen)
                # qp.setBrush(br)

                """
                qp.drawEllipse(QtCore.QPoint(self.zoom_factor*(self.gox + x+W-5),self.zoom_factor*(self.goy + y+W-5)),11, 10)
                qp.drawEllipse(QtCore.QPoint(self.zoom_factor*(self.gox +x+W-5),self.zoom_factor*(self.goy +y+5)),11, 10)
                qp.drawEllipse(QtCore.QPoint(self.zoom_factor*(self.gox +x+5),self.zoom_factor*(self.goy +y+W-5)),11,10)
                qp.drawEllipse(QtCore.QPoint(self.zoom_factor*(self.gox +x+5),self.zoom_factor*(self.goy +y+5)),11, 10)

                qp.drawRect(QtCore.QRect(QtCore.QPoint(self.zoom_factor*(self.gox +x+5),self.zoom_factor*(self.goy +y-5)),QtCore.QPoint(self.zoom_factor*(self.gox +x2-5),self.zoom_factor*(self.goy +y+5))))
                qp.drawRect(QtCore.QRect(QtCore.QPoint(self.zoom_factor*(self.gox +x+5),self.zoom_factor*(self.goy +y+W-5)),QtCore.QPoint(self.zoom_factor*(self.gox +x2-5),self.zoom_factor*(self.goy +y+W+5))))

                qp.drawRect(QtCore.QRect(QtCore.QPoint(self.zoom_factor*(self.gox +x-6),self.zoom_factor*(self.goy +y+1)),QtCore.QPoint(self.zoom_factor*(self.gox +x+5),self.zoom_factor*(self.goy +y+W-5))))
                qp.drawRect(QtCore.QRect(QtCore.QPoint(self.zoom_factor*(self.gox +x+W-5),self.zoom_factor*(self.goy +y+1)),QtCore.QPoint(self.zoom_factor*(self.gox +x+W+5),self.zoom_factor*(self.goy +y+W-5))))
                """

                path = QPainterPath()
                x1 = self.zoom_factor*(self.gox +x)
                y1 = self.zoom_factor*(self.goy +y)
                x2 = self.zoom_factor*(self.gox +x2)
                y2 = self.zoom_factor*(self.goy +y2)
                path.addRoundedRect(QtCore.QRectF(x1,y1,x2-x1,y2-y1), self.zoom_factor*10, self.zoom_factor*10)
                qp.fillPath(path,br.color())

                """
                if self.darkMode:
                    pen = QtGui.QPen(QtGui.QColor(120,120,120 , 255),0) # ,2, Qt.SolidLine)
                else:
                    pen = QtGui.QPen(QtGui.QColor(0, 0,0 , 255),0) # ,2, Qt.SolidLine)
                """

                pen = QtGui.QPen(self.parent().theme_manager.get_color(3),1)
                qp.setPen(pen)
        self.dawPreview(qp)

    def mousePressEvent(self, event):
        
        self.parent().update_graphics_data(self.boxes,self.connectors)
        
        time_delay = time.time() - self.last_click 
        self.last_click = time.time()
        
        
        if event.buttons() & QtCore.Qt.RightButton:
            self.mode = "drag"

        elif event.buttons() & QtCore.Qt.LeftButton:
            self.mode = "select"

        x,y = event.pos().x(), event.pos().y()

        x = int(x/self.zoom_factor)
        y = int(y/self.zoom_factor)

        self.go_startx = x
        self.go_starty = y
        self.gox_start = self.gox
        self.goy_start = self.goy

        # check if any box was selected
        box_hit = False
        self.selected = None
        for box in self.boxes:
            if box.ishelper:
                w = 10
            else:
                w = 50

            if self.mode != "move" and self.zoom_factor*(box.x+self.gox)  < x*self.zoom_factor  < self.zoom_factor*(box.x+self.gox + w) and self.zoom_factor*(box.y+self.goy) < y*self.zoom_factor  < self.zoom_factor*(box.y + w+self.goy):

                # self.mode = "drag"

                box_hit = True
                if self.mode == "drag":
                    self.selected = box
                    self.offx = (box.x - x)
                    self.offy = (box.y - y)

                elif self.mode == "select":
                    if time_delay < 0.2: # double click on agent to edit 
                        if self.highlighted is None:
                            self.highlighted = box
                        self.edit_agent()
                        self.highlighted = None 
                        return 
                    elif self.highlighted == box:
                        self.highlighted = None
                        print("unhighlight", box)
                    else:
                        print("highlight",box)
                        self.highlighted = box


                    self.mode = "drag"

                    if box.ishelper:
                        self.parent().data_select_where(box.name)

                    self.update()

                elif self.mode == "edit":
                    print("EDIT", box)

                    if box.ishelper:
                        print("EDIT CONNECTOR")
                        new_editor = TransactionEditor(self,box)

                    else:
                        print("EDIT AGENT")
                        new_editor = AgentEditor(self,box)

                    self.mode = "select"

                elif self.mode == "delete":
                    raise RuntimeError("Delete mode not supported")
                    self.selected = None
                    self.connected1 = None
                    self.connected2 = None

                    if box.ishelper and box.parent is not None:
                        self.boxes.remove(box.parent.c)
                        # self.boxes.remove(box.parent.d)
                        self.connectors.remove(box.parent)
                        self.update()
                        self.mode = "drag"
                    else:


                        for conn in self.connectors:
                            if conn.a == box or conn.b == box:
                                self.connectors.remove(conn)
                                self.boxes.remove(conn.a)
                                self.boxes.remove(conn.b)
                                self.boxes.remove(conn.c)

                        if box in self.boxes:
                            self.boxes.remove(box)

                        self.update()
                        self.mode= "drag"


                elif self.mode == "connect":
                    self.selected = None

                    if self.connected1 is None:
                        self.connected1 = box
                        self.update()

                    elif self.connected2 is None and not box.ishelper:
                        self.connected2 = box

                        con = Connector(self.connected1,self.connected2)
                        self.connectors.append(con)
                        self.boxes.append(con.c)
                        # self.boxes.append(con.d)

                        self.connected1 = None
                        self.connected2 = None
                        self.mode = "drag"

        if not box_hit and event.buttons() & QtCore.Qt.LeftButton:
            #print("Move")
            self.mode = "move"

    def mouseMoveEvent(self,event):

        x,y = event.pos().x(), event.pos().y()

        x = int(x/self.zoom_factor)
        y = int(y/self.zoom_factor)

        self.mousex = x
        self.mousey = y

        if self.mode == "move":
            x = x
            y = y

            dx = x  - self.go_startx
            dy = y - self.go_starty
            # print("move", dx,dy)

            self.gox = self.gox_start + dx
            self.goy = self.goy_start + dy

        # print("move",x,y)

        hovering = False
        for box in self.boxes:
            if box.ishelper:
                w = 10
            else:
                w = 50
            if self.mode != "move" and self.zoom_factor*(box.x+self.gox)  < x*self.zoom_factor  < self.zoom_factor*(box.x+self.gox + w) and self.zoom_factor*(box.y+self.goy) < y*self.zoom_factor  < self.zoom_factor*(box.y + w+self.goy):

                if event.buttons() & QtCore.Qt.RightButton:
                    self.mode = "drag"

                self.setCursor(Qt.OpenHandCursor)
                hovering = True

        if not hovering:
            self.setCursor(Qt.ArrowCursor)
            #self.parent().setCursor(Qt.OpenHandCursor)

        self.update()

    def mouseReleaseEvent(self, event):

        self.gox_start = self.gox
        self.goy_start = self.goy
        self.go_startx = self.gox
        self.go_starty = self.goy

        if self.mode == "drag":
            x, y = event.pos().x(), event.pos().y()

            x = int(x/self.zoom_factor)
            y = int(y/self.zoom_factor)

            self.mousex = x
            self.mousey = y

            if self.selected is not None:

                self.selected.x = np.clip(self.mousex + self.offx,1-self.gox,self.maxx-self.gox)
                self.selected.y = np.clip(self.mousey + self.offy, 1-self.goy, self.maxy-self.goy)
                self.selected.x0 = self.selected.x
                self.selected.y0 = self.selected.y

                self.selected = None
            self.update()

        self.mode = "select"
