def openObjectSelector(self):
     self.selector = ObjectSelector(self)
class AddObjectWindow(QtGui.QWidget):
    def __init__(self, world):
        super(AddObjectWindow, self).__init__()
        self.world = world
        self.reference = None   # reference object for new object
        self.selector = None    # variable for the selector pop-up window
        self.initUI()
        self.show()
        
    def initUI(self):
        self.setWindowTitle('Add/delete object')

        # variables
        self.color = QtGui.QColor(255,255,255)

        
        self.colorWidget = ColorWidget(self)

        # buttons
        btn_create = QtGui.QPushButton('Create', self)
        btn_color = QtGui.QPushButton('Color', self)
        btn_reference = QtGui.QPushButton('Reference', self)
        btn_delete = QtGui.QPushButton('Delete', self)


        # create status bar
        self.statusbar = QtGui.QStatusBar(self)
        self.statusbar.showMessage('Ready')

        # labels
        name_label = QtGui.QLabel('Name: ')
        mass_label = QtGui.QLabel('Mass (kg): ')
        radius_label = QtGui.QLabel('Radius (km): ')
        position_label = QtGui.QLabel('Position (km): ')
        velocity_label = QtGui.QLabel('Velocity (km/s): ')
        header_label = QtGui.QLabel('Create object')
        self.reference_label = QtGui.QLabel('Default')

        # line edits
        self.radius_value = QtGui.QLineEdit(self)
        self.name_value = QtGui.QLineEdit(self)
        self.mass_value = QtGui.QLineEdit(self)
        self.position_x = QtGui.QLineEdit(self)
        self.position_y = QtGui.QLineEdit(self)
        self.position_z = QtGui.QLineEdit(self)
        self.velocity_x = QtGui.QLineEdit(self)
        self.velocity_y = QtGui.QLineEdit(self)
        self.velocity_z = QtGui.QLineEdit(self)

        # set default parameters
        self.position_x.insert('0')
        self.position_y.insert('0')
        self.position_z.insert('0')
        self.velocity_x.insert('0')
        self.velocity_y.insert('0')
        self.velocity_z.insert('0')
        self.mass_value.insert('0')
        self.radius_value.insert('1') # default radius is one, since by default we want to be able to see the sphere
        
        # set grid layout
        grid = QtGui.QGridLayout()
        grid.addWidget(header_label,0,0)
        grid.addWidget(name_label, 1, 0)
        grid.addWidget(self.name_value, 1, 1)
        grid.addWidget(btn_color, 1, 2)
        grid.addWidget(self.colorWidget, 1, 3)
        grid.addWidget(mass_label, 2, 0)
        grid.addWidget(self.mass_value, 2, 1)
        grid.addWidget(btn_reference, 2,2)
        grid.addWidget(self.reference_label, 2,3)
        grid.addWidget(radius_label, 3, 0)
        grid.addWidget(self.radius_value, 3, 1)
        grid.addWidget(position_label, 4, 0)
        grid.addWidget(self.position_x, 4, 1)
        grid.addWidget(self.position_y, 4, 2)
        grid.addWidget(self.position_z, 4, 3)
        grid.addWidget(velocity_label, 5, 0)
        grid.addWidget(self.velocity_x, 5, 1)
        grid.addWidget(self.velocity_y, 5, 2)
        grid.addWidget(self.velocity_z, 5, 3)
        
        grid.addWidget(btn_create,8,0)
        grid.addWidget(btn_delete,8,1)
        grid.addWidget(self.statusbar,9,0,1,-1)
        self.setLayout(grid)

        # connect elements
        btn_create.clicked.connect(self.makeNew)
        btn_color.clicked.connect(self.getColor)
        btn_reference.clicked.connect(self.openObjectSelector)
        btn_delete.clicked.connect(self.deleteObject)

    # this function creates the new object and is connected to the create-button
    def makeNew(self):
        name = ''
        position = None
        velocity = None
        radius = 0
        mass = 0
        try:
            name = str(self.name_value.text())
            mass = float(self.mass_value.text())
            radius = float(self.radius_value.text())
            if self.reference is None:
                position = vector(float(self.position_x.text()), float(self.position_y.text()), float(self.position_z.text()))
                velocity = vector(float(self.velocity_x.text()), float(self.velocity_y.text()), float(self.velocity_z.text()))
            else:
                position = vector(float(self.position_x.text()), float(self.position_y.text()), float(self.position_z.text())) + self.reference.position
                velocity = vector(float(self.velocity_x.text()), float(self.velocity_y.text()), float(self.velocity_z.text())) + self.reference.velocity
        except:
            self.statusbar.showMessage('Invalid arguments!')
            return
        
        if name=='':
            self.statusbar.showMessage('The new object must have a proper name!')
            return

        color = None
        if self.color.isValid():
            color = (self.color.redF(), self.color.greenF(), self.color.blueF())    # create a color that is compatible with the visual library
        else:
            color = (1,1,1)                                                         # else use white color. This is highly unlikely to happen though.
        
        self.world.addObject(AstronomicalObject(position, velocity, mass,
                                                sphere(pos=position, radius=radius, color=color, make_trail=False),
                                                radius, self.world.show_vectors, self.world.make_trail,
                                                self.world.velocity_scale, self.world.acceleration_scale*self.world.acceleration_gain),
                                                name)
        self.statusbar.showMessage("Object '{}' created successfully!".format(name))

    def deleteObject(self):
        name = str(self.name_value.text())
        rval = self.world.deleteObject(name)
        if rval is True:
            self.statusbar.showMessage("Object '{}' deleted.".format(name))
        else:
            self.statusbar.showMessage("No object '{}' found to be deleted.".format(name))

    def getColor(self):
        self.color = self.colorWidget.getColor()


    def openObjectSelector(self):
        self.selector = ObjectSelector(self)
        
    def setReference(self, reference):
        if reference != 'Default':
            self.reference = self.world.objects[reference]
            self.reference_label.setText(reference)
        else:
            self.reference = None
            self.reference_label.setText('Default')

    def closeEvent(self, event):
        if self.selector is not None:
            self.selector.close()
            self.close()
class ControlWindow(QtGui.QWidget):
    def __init__(self):
        super(ControlWindow, self).__init__()
        self.world = World(self)
        self.simulation_on = False  # used to set the simulation speed display to zero if False
        self.add_object_window = None
        self.selector = None
        self.collision = False
        self.collision_text = ''
        self.initUI()

    def initUI(self):

        self.setGeometry(820, 30, 300, 0)
        self.setWindowTitle('Control Window')

        # create informational elements
        self.statusbar = QtGui.QStatusBar(self)
        self.statusbar.showMessage('Ready')
        self.sim_speed_label = QtGui.QLabel('Simulation speed: ')
        self.sim_speed_value = QtGui.QLabel('0 /s')
        velocity_scale = QtGui.QLabel('Velocity scale')
        acceleration_scale = QtGui.QLabel('Acceleration scale')
        self.reference_label = QtGui.QLabel('Default')
        
        # create control elements
        # buttons
        btn_start = QtGui.QPushButton('Start/Stop', self)
        btn_load = QtGui.QPushButton('Load simulation', self)
        btn_reload = QtGui.QPushButton('Reload simulation', self)
        btn_clear = QtGui.QPushButton('Clear', self)
        btn_camera = QtGui.QPushButton('Camera', self)
        
        # sliders
        velocity_vector_scale = QtGui.QSlider(QtCore.Qt.Horizontal, self)
        acceleration_vector_scale = QtGui.QSlider(QtCore.Qt.Horizontal, self)
        acceleration_vector_gain = QtGui.QSlider(QtCore.Qt.Horizontal, self)  # sets the gain value for acceleration
        acceleration_vector_gain.setMinimum(1)
        acceleration_vector_gain.setMaximum(100000)

        # add object window
        self.add_object_window = AddObjectWindow(self.world)

        # time step settings
        timestep_label = QtGui.QLabel('Timestep:')
        self.timestep_value_label = QtGui.QLabel('{}'.format(self.world.getTimeStep()))
        timestep_set_button = QtGui.QPushButton('Set', self)
        self.timestep_edit = QtGui.QLineEdit(self)

        # checkboxes
        self.chkbx_trails = QtGui.QCheckBox('Show trails', self)
        self.chkbx_vectors = QtGui.QCheckBox('Show vectors', self)

        # set timers
        self.speedtimer = QtCore.QTimer()   # updates the updates per second value
        self.speedtimer.setInterval(1000)
        self.speedtimer.start()
        
        # set widget layout
        grid = QtGui.QGridLayout()
        grid.addWidget(btn_start, 0, 0)
        grid.addWidget(btn_load, 0, 1)
        grid.addWidget(btn_reload, 0, 2)
        grid.addWidget(btn_clear, 0, 3)
        
        grid.addWidget(self.sim_speed_label, 1, 0)
        grid.addWidget(self.sim_speed_value, 1, 1)
        
        grid.addWidget(timestep_label, 3,0)
        grid.addWidget(self.timestep_value_label, 3, 1)
        grid.addWidget(self.timestep_edit, 3, 2)
        grid.addWidget(timestep_set_button, 3, 3)
        
        grid.addWidget(self.chkbx_trails, 4,0)
        grid.addWidget(btn_camera, 4, 2)
        grid.addWidget(self.reference_label, 4, 3)
        grid.addWidget(self.chkbx_vectors, 4, 1)

        grid.addWidget(velocity_scale, 6,0)
        grid.addWidget(velocity_vector_scale, 6,1)
        grid.addWidget(acceleration_scale, 7, 0)
        grid.addWidget(acceleration_vector_scale, 7, 1)
        grid.addWidget(acceleration_vector_gain, 7, 2)
        grid.addWidget(self.add_object_window,8,0,1,-1)
        grid.addWidget(self.statusbar, 9,0,1,-1)

        
        self.setLayout(grid)


        # set button tooltips
        btn_start.setToolTip('Start/pause simulation')
        timestep_set_button.setToolTip('Set simulation timestep')
        btn_clear.setToolTip('Clear all objects')

        # connect controls
        btn_load.clicked.connect(self.loadSimulation)
        btn_reload.clicked.connect(self.reloadSimulation)
        btn_start.clicked.connect(self.startStop)
        btn_clear.clicked.connect(self.clearObjects)
        btn_camera.clicked.connect(self.openObjectSelector)
        self.chkbx_trails.stateChanged.connect(self.showTrails)
        self.chkbx_vectors.stateChanged.connect(self.showVectors)
        timestep_set_button.clicked.connect(self.setTimeStep)
        velocity_vector_scale.valueChanged.connect(self.world.setVelocityVectorScale)
        acceleration_vector_scale.valueChanged.connect(self.world.setAccelerationVectorScale)
        acceleration_vector_gain.valueChanged.connect(self.world.setAccelerationGain)

        
        # connect timer
        self.speedtimer.timeout.connect(self.setSimSpeedDisplay)

        # set visible
        self.show()


        
    def start(self):    # call the start method of the World object to start the thread
        self.world.start()

    def setTimeStep(self):
        try:
            self.world.setTimeStep(str(self.timestep_edit.text()))
        except ValueError:
            self.statusbar.showMessage('Invalid argument for timestep')
            return
        self.timestep_value_label.setText(self.timestep_edit.text())
        self.statusbar.showMessage('Ready')

    def setSimSpeedDisplay(self):
        if self.simulation_on:
            self.sim_speed_value.setText('{} /s'.format(self.world.speed))
        else:
            self.sim_speed_value.setText('0 /s')
        if self.collision:
            self.collision = False
            self.statusbar.showMessage(self.collision_text)
    
    def loadSimulation(self):
        self.simulation_on = False
        self.world.stopSimulation()
        self.add_object_window.setReference('Default')
        self.world.loadSimulation()
        if self.world.errors == Loader.ERRORS_OCCURRED:
            self.statusbar.showMessage('Errors occurred when loading simulation. Check error log.')
        elif self.world.errors == Loader.OBJECTS_LOADED:
            self.statusbar.showMessage('Simulation loaded succesfully!')
        elif self.world.errors == Loader.OBJECTS_NOT_LOADED:
            self.statusbar.showMessage('Object loading canceled.')

    def reloadSimulation(self):
        self.simulation_on = False
        self.world.stopSimulation()
        self.add_object_window.setReference('Default')
        self.setReference('Default')
        self.world.reloadSimulation()
        if self.world.errors == Loader.ERRORS_OCCURRED:
            self.statusbar.showMessage('Errors occurred when loading simulation. Check error log.')
        elif self.world.errors == Loader.OBJECTS_LOADED:
            self.statusbar.showMessage('Simulation loaded succesfully!')
        elif self.world.errors == Loader.OBJECTS_NOT_LOADED:
            self.statusbar.showMessage('Object loading canceled.')
        elif self.world.errors == Loader.FILE_NOT_OPENED:       
            self.statusbar.showMessage('File could not be loaded.')

    def clearObjects(self):
        if  not self.world.paused:
            self.world.stopSimulation()
            self.simulation_on = False
        self.world.clearObjects()
        self.add_object_window.setReference('Default')
        self.setReference('Default')
        self.statusbar.showMessage('Objects cleared')

    def showTrails(self, state):
        if state == QtCore.Qt.Checked:
            self.world.showTrails(True)
        else:
            self.world.showTrails(False)

    def showVectors(self, state):
        if state:
            self.world.showVectors(True)
        else:
            self.world.showVectors(False)
        
    def startStop(self):    # start/pause simulation
        if  not self.world.paused:
            self.world.stopSimulation()
            self.simulation_on = False
            self.statusbar.showMessage('Simulation paused')
            print self.world.time_elapsed/86400
        else:
            self.world.startSimulation()
            self.simulation_on = True
            self.statusbar.showMessage('Simulation started')
            
    def reportCollision(self, error):
        #self.statusbar.showMessage(error)
        self.collision_text = error
        self.collision = True
        self.simulation_on = False

    def openObjectSelector(self):
        self.selector = ObjectSelector(self)

    def setReference(self, reference):
        if reference != 'Default':
            self.world.setReference(reference)
            self.reference_label.setText(reference)
        else:
            self.world.setReference(None)
            self.reference_label.setText('Default')

    def closeEvent(self, event):    # handle closing the program
        scene.visible = 0           # close the scenery window
        self.world.close()
        self.add_object_window.close() # this is necessary so that the reference selector will also close automatically
        if self.selector is not None:
            self.selector.close()
        self.close()
        event.accept()