#   Copyright 2010,2011 P.Petrov, B.Neggers, N.Ramsey www.nick-ramsey.eu
#
#   Project name: Braincarta
#   
#   Project homepage : http://www.braincarta.com   
#	
#   File name: Braincarta.py
#    
#   Description: main executable
#    
#
#   Author: Petar Petrov
#    -revison <date> author <name/email> : <change>
#
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
#   This file is part of Braincarta.
#
#   Braincarta is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   Braincarta is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with Braincarta.  If not, see <http://www.gnu.org/licenses/>.
#
#   UMC University Medical Center, Utrecht NL, hereby disclaims all 
#   copyright interest in the program Braincarta (which makes# passes at interpreters) 
#   written by Petar Petrov.
#
#   Nick Ramsey, 17 February 2011
#   Professor in Cognitive Neuroscience at the University Medical Center of Utrecht
#   Rudolf Magnus Institute for Neuroscience
#   Dept Neurology en Neurosurgery
 
 
 
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# core imports
import os
import sys
from sys import argv
import getopt
import struct
import array
 
# reads and renders nifti volume datasets.
import vtk
from vtk import *
from vtk.util.misc import vtkGetDataRoot
VTK_DATA_ROOT = vtkGetDataRoot()
 
#from PyQt4.QtCore import *
 
#**************************** Braincarta Headers *******************************
from Braincarta_misc import *
from Braincarta_qtgui import *
from Braincarta_vtkrnd import *
from Braincarta_nifti import NiftiFileIO
#*********************************************************************
 
 
class ObjectsManager(QtCore.QObject) :
    "Manages the loaded vol files. integrates with GUI. arbiter with the rendering"
    #TODO: singleton, communicate via thread-safe queue with the rendering, rename,
    OnRequest_DataBind_Tree = pyqtSignal(dict)
    OnRequest_DataBind_Props = pyqtSignal(object)
    OnRequest_Render = pyqtSignal()
    def __init__(self,renderManager):
	QtCore.QObject.__init__(self)
	self._objects = {}
	self._RenderManager = renderManager
	self._selectedID = -1
 
 
	#self.OnRequest_DataBind_Tree = pyqtSignal(dict)
	#self.OnRequest_DataBind_Props = pyqtSignal(object)
	#self.OnRequest_Render = pyqtSignal()
	pass
 
    @pyqtSlot(str)
    def AddObj(self,niftiFilePath):
	"Opens new file and loads the iso surface from it"	
 
	niftiFileIO = NiftiFileIO(str(niftiFilePath))
 
	if niftiFileIO.ERR == 1 :
	    return 1
 
 
        _objProps = self._RenderManager.CreateObject(niftiFileIO)
 
	#set default color for the qt gui
	_color = QColor()
	_color.setRgbF(_objProps.RED, _objProps.GREEN, _objProps.BLUE)
	_objProps.QT_COLOR = _color
 
	self._objects[_objProps.ID] = _objProps
 
	#self.emit(QtCore.SIGNAL("OnRequest_DataBind_Tree(objects)"), self._objects.itervalues())
	self.OnRequest_DataBind_Tree.emit(self._objects)
 
	#no need since the GUI will call self.SetSelectedObj(...
	#self.SetSelectedObj(_objProps.ID)
 
	return 0
 
    @pyqtSlot(int)
    def SetSelectedObj(self,_id):
	assert(_id != 0)
	self._selectedID = int(_id)
	obj_prop = self._objects[_id]
	#self.emit(QtCore.SIGNAL("OnRequest_DataBind_Props(object_prop)"), obj_prop)
	self.OnRequest_DataBind_Props.emit(obj_prop)
	pass
 
    @pyqtSlot(object,object)
    def SetSelectedObjProp(self, prop_enum_type, value):
	if self._selectedID == -1 :
	    return
 
	#TODO: check if not the same value, then no need to call render
	if prop_enum_type == EnumPropType.All :
	    #applying a template settings
	    self._objects[self._selectedID].VOL_TRESHOLD = value.threshold
	    self._objects[self._selectedID].VOL_DECIMATION = value.decimation
	    self._objects[self._selectedID].VOL_SMOOTH = value.smooth
	    self._objects[self._selectedID].VOL_OPACITY = value.opacity
	    self._objects[self._selectedID].VOL_TYPE = EnumVolToText(value.voltypeID)
	    self._objects[self._selectedID].VOL_TYPE_ID = value.voltypeID
	    #self.emit(QtCore.SIGNAL("OnRequest_DataBind_Tree(objects)"), self._objects.itervalues())
	    self.OnRequest_DataBind_Tree.emit(self._objects)
 
	elif prop_enum_type == EnumPropType.Decimation :
	    self._objects[self._selectedID].VOL_DECIMATION = value
 
	elif prop_enum_type == EnumPropType.Treshold :
	    self._objects[self._selectedID].VOL_TRESHOLD = value
 
	elif prop_enum_type == EnumPropType.Smooth :
	    self._objects[self._selectedID].VOL_SMOOTH = value
 
        elif prop_enum_type == EnumPropType.Opacity :
	    self._objects[self._selectedID].VOL_OPACITY = value
 
	elif prop_enum_type == EnumPropType.Visibility :
	    if( value == Qt.Checked):
	        self._objects[self._selectedID].HIDDEN = 1
	    else :
		self._objects[self._selectedID].HIDDEN = 0
 
	elif prop_enum_type == EnumPropType.Color :
	    self._objects[self._selectedID].QT_COLOR = value
	    #r = g = b = 0.0	    
	    rgba = value.getRgbF()
	    self._objects[self._selectedID].RED = rgba[0]
	    self._objects[self._selectedID].GREEN = rgba[1]
	    self._objects[self._selectedID].BLUE = rgba[2]
	    #self.emit(QtCore.SIGNAL("OnRequest_DataBind_Tree(objects)"), self._objects.itervalues())
	    self.OnRequest_DataBind_Tree.emit(self._objects)
 
	else :
	    return
 
	self._RenderManager.ApplyObjectProperties(self._objects[self._selectedID])
	#self.emit(QtCore.SIGNAL("OnRequest_Render()"))
	self.OnRequest_Render.emit()
	pass
 
    @pyqtSlot()
    def DelObj(self):
	self._RenderManager.DestroyObject(self._selectedID)
	del self._objects[self._selectedID]
 
	#gets the first item in the list
	for item in self._objects.itervalues():
	    self.SetSelectedObj(item.ID)
	    break
 
	#self.emit(QtCore.SIGNAL("OnRequest_DataBind_Tree(objects)"), self._objects.itervalues())
	self.OnRequest_DataBind_Tree.emit(self._objects.itervalues())
	#self.emit(QtCore.SIGNAL("OnRequest_Render()"))    
	self.OnRequest_Render.emit()
	pass
 
class MainApp(QtCore.QObject) :
    "Main GUI Application class"
    def __init__(self):
	QtCore.QObject.__init__(self)
 
    #timer for GUI responsive
        self._timer = QTimer()
        self._timer.timeout.connect(self.__update)
        self._timer.start(500)
 
    #Qt
	self.qtApp = QtGui.QApplication(['Braincarta'])
	self.qtApp.setApplicationName('Braincarta')
	self.qtApp.setOrganizationDomain('www.nick-ramsey.eu')
	self.qtApp.setOrganizationName('UMC Utrecht')
	self.qtWin = MainWindow()
	#self.connect(self.qtWin, QtCore.SIGNAL("OnAction_FileOpen(QString)"),self.OnFileOpen)
	self.qtWin.OnAction_FileOpen.connect(self.OnFileOpen)
 
 
	#tbr
	self.qtWin.qtApp = self.qtApp
	self.win = self.qtWin.qtVTKWidget.GetRenderWindow()
 
 
	#to be replaced by a singleton manager
	self.RenderManager = MainRender()
	self.win.AddRenderer(self.RenderManager.aRenderer)
 
	self.ObjectsManager = ObjectsManager(self.RenderManager)
 
	#bind signal-slots between different application objects
 
 
	#QObject.connect(self.ObjectsManager,SIGNAL("OnRequest_DataBind_Tree(object)"),self.qtWin.BindTreeData)
	self.ObjectsManager.OnRequest_DataBind_Tree.connect(self.qtWin.BindTreeData)
 
	#QObject.connect(self.qtWin,SIGNAL("OnAction_TreeItemSelected(int)"),self.ObjectsManager.SetSelectedObj)
	self.qtWin.OnAction_TreeItemSelected.connect(self.ObjectsManager.SetSelectedObj)
 
	#QObject.connect(self.ObjectsManager,SIGNAL("OnRequest_DataBind_Props(RenderObjectProperties)"),self.qtWin.BindPropData)
	self.ObjectsManager.OnRequest_DataBind_Props.connect(self.qtWin.BindPropData)
 
	#QObject.connect(self.qtWin,SIGNAL("OnAction_ObjPropChanged(enum,val)"),self.ObjectsManager.SetSelectedObjProp)
	self.qtWin.OnAction_ObjPropChanged.connect(self.ObjectsManager.SetSelectedObjProp)
 
	#QObject.connect(self.ObjectsManager,SIGNAL("OnRequest_Render()"),self.qtWin.WinRender)
	self.ObjectsManager.OnRequest_Render.connect(self.qtWin.WinRender)
 
	#QObject.connect(self.qtWin,SIGNAL("OnAction_ObjDelete()"),self.ObjectsManager.DelObj)
	self.qtWin.OnAction_ObjDelete.connect(self.ObjectsManager.DelObj)
	pass
 
    def __update(self):
        self.qtApp.processEvents(QEventLoop.ExcludeUserInputEvents)
        pass
 
    def Exit(self) :
	self.qtWin.saveState()
	self.qtApp.quit()
	pass
 
 
    # initialize all static objects of the VTL pipeline
    def OnFileOpen(self, file_path) :
        # The following reader is used to read a brain nifti volume (segmented GM)
 
	self.qtWin.SetStatusText("Trying to load a volume from file : " + file_path)
 
 
 
	if self.ObjectsManager.AddObj(file_path) == 1:
	    self.qtWin.ShowMsgError('Incorrect nifi file format!\nAborted...')
	    return                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
 
 
 
	self.qtWin.progessBar.setValue(0.1)
	#self.__timer.start(0)
	#self.qtApp.processEvents()
 
	#self.qtWin.qtVTKWidget.Render()
    #    self.__timer.stop()
	self.qtWin.progessBar.setValue(0.5)
    pass
 
 
    def Start(self):
        ret = 0
 
	self.qtWin.qtVTKWidget.Render()
 
	self.qtWin.show()
 
	ret = self.qtApp.exec_()
 
        return ret
 
 
 
def main(argv):
 
    app = MainApp()
 
    #app.InitGUI()
 
    #app.InitRenderPipeline()
 
    sys.exit(app.Start())
 
    # Note that when camera movement occurs (as it does in the Dolly()
    # method), the clipping planes often need adjusting. Clipping planes
    # consist of two planes: near and far along the view direction. The
    # near plane clips out objects in front of the plane the far plane
    # clips out objects behind the plane. This way only what is drawn
    # between the planes is actually rendered.
    #aRenderer.ResetCameraClippingRange()
 
 
 
if __name__ == "__main__":
    main(sys.argv)
 
 
#TODO:
#define how to use from the command line and paramteres
def usage() :
    print "Usage: Braincarta.py [-h,--help]"