"""
     qtgui/ContourSliceDialog.py: CCP4MG Molecular Graphics Program
     Copyright (C) 2001-2008 University of York, CCLRC
     Copyright (C) 2009-2010 University of York

     This library is free software: you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public License
     version 3, modified in accordance with the provisions of the 
     license to address the requirements of UK law.
 
     You should have received a copy of the modified GNU Lesser General 
     Public License along with this library.  If not, copies may be 
     downloaded from http://www.ccp4.ac.uk/ccp4license.php
 
     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 Lesser General Public License for more details.
"""
"""
Contour slice details dialog.
"""
import MGSimpleDialog
from PyQt4 import QtGui,QtCore
import mgWidgets
from global_definitions import *
import MapPlane

class MapSliceSpecifyingSelector(QtGui.QDialog):
  def __init__(self,parent=None):
     QtGui.QDialog.__init__(self,parent)
     self.specifying_layout = QtGui.QGridLayout()
     plane_defn_label = QtGui.QLabel(self.tr("Plane definition"))
     self.A_edit = QtGui.QLineEdit("1.0")
     self.B_edit = QtGui.QLineEdit("0.0")
     self.C_edit = QtGui.QLineEdit("0.0")
     self.D_edit = QtGui.QLineEdit("0.0")
     self.planeA_validator = QtGui.QDoubleValidator(-1000.0,1000.0,3,self.A_edit)
     self.planeB_validator = QtGui.QDoubleValidator(-1000.0,1000.0,3,self.B_edit)
     self.planeC_validator = QtGui.QDoubleValidator(-1000.0,1000.0,3,self.C_edit)
     self.planeD_validator = QtGui.QDoubleValidator(-1000.0,1000.0,3,self.D_edit)
     self.A_edit.setValidator(self.planeA_validator)
     self.B_edit.setValidator(self.planeB_validator)
     self.C_edit.setValidator(self.planeC_validator)
     self.D_edit.setValidator(self.planeD_validator)
     self.specifying_layout.addWidget(plane_defn_label,0,0)
     self.specifying_layout.addWidget(self.A_edit,0,1)
     self.specifying_layout.addWidget(self.B_edit,0,2)
     self.specifying_layout.addWidget(self.C_edit,0,3)
     self.specifying_layout.addWidget(self.D_edit,0,4)
     self.setLayout(self.specifying_layout)

class MapSliceFaceSelector(QtGui.QDialog):
  def __init__(self,parent=None):
     QtGui.QDialog.__init__(self,parent)
     self.crystal_layout = QtGui.QGridLayout()
     crystal_face_label = QtGui.QLabel(self.tr("Crystal face"))
     data_label = QtGui.QLabel(self.tr("of crystal"))
     self.crystal_face_combo = QtGui.QComboBox()
     self.crystal_face_combo.addItem("ab")
     self.crystal_face_combo.addItem("ac")
     self.crystal_face_combo.addItem("bc")

     self.crystal_data_dataCombo = QtGui.QComboBox()
     for item in get_dataobj(object_type='MapData'):
	     self.crystal_data_dataCombo.addItem("Map: "+item.name)
     for item in get_dataobj(object_type='MolData'):
        if hasattr(item,'originalSymmetry'):
		self.crystal_data_dataCombo.addItem("Mol: "+item.name)

     self.crystal_layout.addWidget(crystal_face_label,0,0)
     self.crystal_layout.addWidget(self.crystal_face_combo,0,1)
     self.crystal_layout.addWidget(data_label,1,0)
     self.crystal_layout.addWidget(self.crystal_data_dataCombo,1,1)
     self.setLayout(self.crystal_layout)

class MapSliceAtomSelector(QtGui.QDialog):
  def __init__(self,parent=None):
     QtGui.QDialog.__init__(self,parent)
     self.main_layout = QtGui.QVBoxLayout()
     self.atoms_layout = QtGui.QGridLayout()
     atom_label_1 = QtGui.QLabel(self.tr("1st Atom"))
     atom_label_2 = QtGui.QLabel(self.tr("2nd Atom"))
     atom_label_3 = QtGui.QLabel(self.tr("3rd Atom"))
     self.atoms_layout.addWidget(atom_label_1,0,0)
     self.atoms_layout.addWidget(atom_label_2,1,0)
     self.atoms_layout.addWidget(atom_label_3,2,0)
     self.atom_sel_1 = mgWidgets.mgAtomPicker()
     self.atom_sel_2 = mgWidgets.mgAtomPicker()
     self.atom_sel_3 = mgWidgets.mgAtomPicker()
     self.atoms_layout.addWidget(self.atom_sel_1,0,1)
     self.atoms_layout.addWidget(self.atom_sel_2,1,1)
     self.atoms_layout.addWidget(self.atom_sel_3,2,1)

     label = QtGui.QLabel('Draw plane through',self)
     self.atom_spec_combo = QtGui.QComboBox()
     for item in MapPlane.MapPlane.selection_mode_menu:
       self.atom_spec_combo.addItem(self.tr(item))
     self.atoms_layout.setContentsMargins(0,0,0,0)
     self.main_layout.addLayout(self.atoms_layout)
     self.spec_layout = QtGui.QHBoxLayout()
     self.spec_layout.setContentsMargins(0,0,0,0)
     self.spec_layout.addWidget(label)
     self.spec_layout.addWidget(self.atom_spec_combo)
     self.spec_layout.addStretch(2)
     self.main_layout.addLayout(self.spec_layout)
     self.setLayout(self.main_layout)

class ContourSliceDialog(MGSimpleDialog.MultiDialog):

  def hideWidgets(self,layout):
      for i in range(layout.count()):
        item = layout.itemAt(i)
	if hasattr(item,"widget") and callable(item.widget):
          widget = item.widget()
	  if hasattr(widget,"hide") and callable(widget.hide):
             widget.hide()

  def showWidgets(self,layout):
      for i in range(layout.count()):
        item = layout.itemAt(i)
	if hasattr(item,"widget") and callable(item.widget):
          widget = item.widget()
	  if hasattr(widget,"show") and callable(widget.show):
             widget.show()

  def __init__(self,parent=None):
     MGSimpleDialog.MultiDialog.__init__(self,parent)
     self.setWindowTitle(self.tr("Contour Slice Details"))
     self.CreateMenuEntry()
     mainLayout = QtGui.QVBoxLayout()
     space_label = QtGui.QLabel(self.tr("Contour spacing"))
     spaceLayout = QtGui.QHBoxLayout()
     spaceLayout.addWidget(space_label)
     self.contour_spacing_edit = QtGui.QLineEdit()
     self.contour_validator = QtGui.QDoubleValidator(0.0,1000.0,3,self.contour_spacing_edit)
     self.contour_spacing_edit.setText("1.0")
     self.contour_spacing_edit.setValidator(self.contour_validator)
     spaceLayout.addWidget(self.contour_spacing_edit)
     units_label = QtGui.QLabel(u"\u212B")
     spaceLayout.addWidget(units_label)
     plane_label = QtGui.QLabel(self.tr("Define plane"))

     self.tabWidget = QtGui.QTabWidget()
     mainLayout.addWidget(self.tabWidget)

     self.specifying_layout = MapSliceSpecifyingSelector()
     specifying_layout_layout = QtGui.QVBoxLayout() #Oh dear, need to rename members...
     specifying_layout_layout.addWidget(self.specifying_layout)
     specifying_layout_layout.addStretch(2)
     self.specifying_widget = QtGui.QWidget()
     self.specifying_widget.setLayout(specifying_layout_layout)
     self.atoms_layout = MapSliceAtomSelector()
     self.crystal_layout = MapSliceFaceSelector()
     crystal_layout_layout = QtGui.QGridLayout() #Oh dear, need to rename members...
     crystal_layout_layout.addWidget(self.crystal_layout,0,1)
     crystal_layout_layout.setColumnStretch(0,2)
     crystal_layout_layout.setColumnStretch(2,2)
     crystal_layout_layout.setRowStretch(1,2)
     self.crystal_widget = QtGui.QWidget()
     self.crystal_widget.setLayout(crystal_layout_layout)

     self.tabWidget.addTab(self.specifying_widget,self.tr("specifying plane"))
     self.tabWidget.addTab(self.atoms_layout,self.tr("through selected atoms"))
     self.tabWidget.addTab(self.crystal_widget,self.tr("parallel to crystal face"))

     raise_pretext = QtGui.QLabel(self.tr("Raise plane by "))
     raise_posttext = QtGui.QLabel(u"\u212B")
     self.raise_plane = QtGui.QLineEdit()
     self.raise_plane_validator = QtGui.QDoubleValidator(-1000.0,1000.0,3,self.raise_plane)
     self.raise_plane.setText("1.0")
     self.raise_plane.setValidator(self.raise_plane_validator)
     raiseLayout = QtGui.QHBoxLayout()
     raiseLayout.addWidget(raise_pretext)
     raiseLayout.addWidget(self.raise_plane)
     raiseLayout.addWidget(raise_posttext)
     raiseLayout.addStretch(2)
     spaceLayout.addStretch(2)
     mainLayout.addLayout(raiseLayout)
     self.camera_check = QtGui.QCheckBox(self.tr("Set camera perpendicular to this plane"))
     mainLayout.addWidget(self.camera_check)
     mainLayout.addLayout(spaceLayout)

     dialog_buttons = QtGui.QDialogButtonBox()
     ok_button = dialog_buttons.addButton(QtGui.QDialogButtonBox.Ok)
     apply_button = dialog_buttons.addButton(QtGui.QDialogButtonBox.Apply)
     cancel_button = dialog_buttons.addButton(QtGui.QDialogButtonBox.Cancel)
     self.connect(cancel_button,QtCore.SIGNAL('clicked()'),self.Close)
     self.connect(ok_button,QtCore.SIGNAL('clicked()'),self.ApplyAndClose)
     self.connect(apply_button,QtCore.SIGNAL('clicked()'),self.Apply)
     mainLayout.addWidget(dialog_buttons)

     self.setLayout(mainLayout)
     self.setSizeGripEnabled(1)
     self.UnClose() #?

  def Apply(self):
     self.emit(QtCore.SIGNAL('apply'))
     return
   
     plane = None
     raise_plane = float(self.raise_plane.text())
     import pygl_coord
     if self.tabWidget.tabText(self.tabWidget.currentIndex()) == self.tr("specifying plane"):
	plane = pygl_coord.Plane(float(self.specifying_layout.A_edit.text()),float(self.specifying_layout.B_edit.text()),float(self.specifying_layout.C_edit.text()),float(self.specifying_layout.D_edit.text()))
	self.emit(QtCore.SIGNAL("PlaneChanged"),(plane))
        plane.Normalize()
        orig_D = plane.get_D()
        new_D = orig_D + raise_plane
        plane.set_D(new_D)
	print plane.get_A(),plane.get_B(),plane.get_C(),plane.get_D()
     if self.tabWidget.tabText(self.tabWidget.currentIndex()) == self.tr("through selected atoms"):
	print "Through atoms"
	at1 = None
	at2 = None
	at3 = None
        dataName,model,chain,residue,atom = str(self.atoms_layout.atom_sel_1.selection()).split('/')
	print dataName,model,chain,residue,atom
        data = get_dataobj(name=dataName,object_type='MolData')
	if len(data)==1 and data[0] and hasattr(data[0],'interpretAtomID'):
	  atomid = "/"+model+"/"+chain+"/"+residue+"/"+atom
	  at1 = data[0].interpretAtomID(data[0],atomid)

        dataName,model,chain,residue,atom = str(self.atoms_layout.atom_sel_2.selection()).split('/')
	print dataName,model,chain,residue,atom
        data = get_dataobj(name=dataName,object_type='MolData')
	if len(data)==1 and data[0] and hasattr(data[0],'interpretAtomID'):
	  atomid = "/"+model+"/"+chain+"/"+residue+"/"+atom
	  at2 = data[0].interpretAtomID(data[0],atomid)

        dataName,model,chain,residue,atom = str(self.atoms_layout.atom_sel_3.selection()).split('/')
	print dataName,model,chain,residue,atom
        data = get_dataobj(name=dataName,object_type='MolData')
	if len(data)==1 and data[0] and hasattr(data[0],'interpretAtomID'):
	  atomid = "/"+model+"/"+chain+"/"+residue+"/"+atom
	  at3 = data[0].interpretAtomID(data[0],atomid)
	if not at1 or not at2 or not at3: return
	
        cart1 = pygl_coord.Cartesian(at1.x,at1.y,at1.z)
        cart2 = pygl_coord.Cartesian(at2.x,at2.y,at2.z)
        cart3 = pygl_coord.Cartesian(at3.x,at3.y,at3.z)

	if self.atoms_layout.atom_spec_combo.currentText() == self.tr("Through all atoms"):
		pass
        if self.atoms_layout.atom_spec_combo.currentText() == self.tr("Through atoms 1 and 2"):
                cross = pygl_coord.Cartesian.CrossProduct(cart1-cart3,cart2-cart3)
                cart3 = cart1 + cross
        if self.atoms_layout.atom_spec_combo.currentText() == self.tr("Through atoms 1 and 3"):
                cross = pygl_coord.Cartesian.CrossProduct(cart1-cart2,cart3-cart2)
                cart2 = cart1 + cross
        if self.atoms_layout.atom_spec_combo.currentText() == self.tr("Through atoms 2 and 3"):
                cross = pygl_coord.Cartesian.CrossProduct(cart2-cart1,cart3-cart1)
                cart1 = cart2 + cross
        if self.atoms_layout.atom_spec_combo.currentText() == self.tr("Through atom 1, perpendicular to 2, 3"):
                up = pygl_coord.Cartesian.CrossProduct(cart1-cart2,cart1-cart3)
                up.normalize()
                forward = cart1-pygl_coord.Cartesian.MidPoint(cart2,cart3)
                forward.normalize()
                right = pygl_coord.Cartesian.CrossProduct(up,forward)
                cart2 = cart1 + up
                cart3 = cart1 + forward
        if self.atoms_layout.atom_spec_combo.currentText() == self.tr("Through atom 1, parallel to 2, 3"):
                up = pygl_coord.Cartesian.CrossProduct(cart1-cart2,cart1-cart3)
                up.normalize()
                forward = cart1-pygl_coord.Cartesian.MidPoint(cart2,cart3)
                forward.normalize()
                right = pygl_coord.Cartesian.CrossProduct(up,forward)
                cart2 = cart1 + up
                cart3 = cart1 + right
        if self.atoms_layout.atom_spec_combo.currentText() == self.tr("Through atom 2, perpendicular to 1, 3"):
                up = pygl_coord.Cartesian.CrossProduct(cart2-cart1,cart2-cart3)
                up.normalize()
                forward = cart2-pygl_coord.Cartesian.MidPoint(cart1,cart3)
                forward.normalize()
                right = pygl_coord.Cartesian.CrossProduct(up,forward)
                cart1 = cart2 + up
                cart3 = cart2 + forward
        if self.atoms_layout.atom_spec_combo.currentText() == self.tr("Through atom 2, parallel to 1, 3"):
                up = pygl_coord.Cartesian.CrossProduct(cart2-cart1,cart2-cart3)
                up.normalize()
                forward = cart2-pygl_coord.Cartesian.MidPoint(cart1,cart3)
                forward.normalize()
                right = pygl_coord.Cartesian.CrossProduct(up,forward)
                cart1 = cart2 + up
                cart3 = cart2 + right
        if self.atoms_layout.atom_spec_combo.currentText() == self.tr("Through atom 3, perpendicular to 1, 2"):
                up = pygl_coord.Cartesian.CrossProduct(cart3-cart2,cart3-cart1)
                up.normalize()
                forward = cart3-pygl_coord.Cartesian.MidPoint(cart1,cart2)
                forward.normalize()
                right = pygl_coord.Cartesian.CrossProduct(up,forward)
                cart1 = cart3 + up
                cart2 = cart3 + forward
        if self.atoms_layout.atom_spec_combo.currentText() == self.tr("Through atom 3, parallel to 1, 2"):
                up = pygl_coord.Cartesian.CrossProduct(cart3-cart2,cart3-cart1)
                up.normalize()
                forward = cart3-pygl_coord.Cartesian.MidPoint(cart1,cart2)
                forward.normalize()
                right = pygl_coord.Cartesian.CrossProduct(up,forward)
                cart1 = cart3 + up
                cart2 = cart3 + right
        plane = pygl_coord.Plane(cart1,cart2,cart3)
        plane.Normalize()
        orig_D = plane.get_D()
        new_D = orig_D + raise_plane
        plane.set_D(new_D)
	print plane.get_A(),plane.get_B(),plane.get_C(),plane.get_D()
	self.emit(QtCore.SIGNAL("PlaneChanged"),(plane))

     if self.tabWidget.tabText(self.tabWidget.currentIndex()) == self.tr("parallel to crystal face"):
	if len(self.crystal_layout.crystal_data_dataCombo.currentText())<6: return
	name = str(self.crystal_layout.crystal_data_dataCombo.currentText())[5:]
	items = get_dataobj(object_type='MolData')
	map_items = get_dataobj(object_type='MapData')
        items.extend(map_items)
	for item in items:
		if item.name == name:
                        molobj_list = get_dataobj(name=item.name)
                        for obj in molobj_list[0].objinsts:
                          if obj.graphmod:
                            if str(self.crystal_layout.crystal_face_combo.currentText()) == "ab":
                                q = obj.graphmod.obj.GetUnitCellAlignmentRotation("C")
                            elif str(self.crystal_layout.crystal_face_combo.currentText()) == "ac":
                                q = obj.graphmod.obj.GetUnitCellAlignmentRotation("B")
                            elif str(self.crystal_layout.crystal_face_combo.currentText()) == "bc":
                                q = obj.graphmod.obj.GetUnitCellAlignmentRotation("A")
                            else:
                                q = None
                            if q:
                                quat = pygl_coord.QuatPtr(q)
                                zaxis = pygl_coord.Cartesian(0,0,1)
                                norm = pygl_coord.CartesianPtr(quat.getInvMatrix() * zaxis) # SJM 8/2/2010, this is to fix probable hackery below.
                                plane = pygl_coord.Plane(norm.get_x(),norm.get_y(),norm.get_z(),0)
	                        self.emit(QtCore.SIGNAL("PlaneChanged"),(plane))
			break

     if self.camera_check.checkState()>0 and plane:
        import math
        plane_norm = pygl_coord.Cartesian(plane.get_A(),plane.get_B(),plane.get_C())
        zaxis = pygl_coord.Cartesian(0.0, 0.0, 1.0);
        cross = zaxis.CrossProduct(plane_norm,zaxis);
        if abs(cross.get_x()*cross.get_x()+cross.get_y()*cross.get_y()+cross.get_z()*cross.get_z()>1.0e-6) :
          angle = math.acos(abs(plane.get_C()))
          if plane.get_C()>0.0: angle = -angle
          rotquat = pygl_coord.Quat(cross.get_x(),cross.get_y(),cross.get_z(),1,-angle*180.0/math.pi);
          MAINWINDOW().setQuat(rotquat)
        else:
          rotquat = pygl_coord.Quat(0.0,0.0,0.0,0)
          MAINWINDOW().setQuat(rotquat)

  def ApplyAndClose(self):
     self.Apply()
     self.Close()
     
  def getParams(self):
     params = {}
     params['plane_mode'] = ['plane','selection','crystal_face'][self.tabWidget.currentIndex()]


     try:
       params['selection1'] =  self.atoms_layout.atom_sel_1.modelNameAtomName()
       params['selection2'] =  self.atoms_layout.atom_sel_2.modelNameAtomName()
       params['selection3'] =  self.atoms_layout.atom_sel_3.modelNameAtomName()
     except:
       params['selection1'] = "None"
       params['selection2'] = "None"
       params['selection3'] = "None"
     sele_mode = str(self.atoms_layout.atom_spec_combo.currentText())
     if MapPlane.MapPlane.selection_mode_menu.count(sele_mode):
       params['selection_mode'] = MapPlane.MapPlane.selection_mode_alias[MapPlane.MapPlane.selection_mode_menu.index(sele_mode)]
     # Sanity check - three different atoms - could relax this for some of
     # the selection_mode
     if params['plane_mode'] == 'selection':
       err = 0
       for s1,s2 in [ ['selection1','selection2'],
                      ['selection1','selection3'],
                      ['selection2','selection3'] ] :
         if params[s1]==params[s2]: err = err+1
       if err:
         QtGui.QMessageBox.warning(self,'Error in Contour Slice Details','Please select three different atoms.')
         return {}


     params['specify_plane_A'] =  float(self.specifying_layout.A_edit.text())
     params['specify_plane_B'] =  float(self.specifying_layout.B_edit.text())
     params['specify_plane_C'] =  float(self.specifying_layout.C_edit.text())
     params['specify_plane_D'] =  float(self.specifying_layout.D_edit.text())

     
     params['crystal_face'] = str(self.crystal_layout.crystal_face_combo.currentText())
     if len(self.crystal_layout.crystal_data_dataCombo.currentText())>=6:
        params['crystal'] = str(self.crystal_layout.crystal_data_dataCombo.currentText()[5:])
       
     params['raise_plane'] = float(self.raise_plane.text())
     params['set_camera'] = int(self.camera_check.checkState())
     params['contour_spacing'] = float(self.contour_spacing_edit.text())
     #print "getParams",params
     return params

  def setParams(self,params):
     print "ContourSliceDialog.setParams",params

     if params.has_key('raise_plane'): self.raise_plane.setText(str(params['raise_plane']))
     if params.has_key('contour_spacing'): self.contour_spacing_edit.setText(str(params['contour_spacing']))

     if params.has_key('set_camera'): self.camera_check.setChecked(params['set_camera'])


     if params.has_key('specify_plane_A'):
       self.specifying_layout.A_edit.setText(str(params.get('specify_plane_A',1.0)))
       self.specifying_layout.B_edit.setText(str(params.get('specify_plane_B',0.0)))
       self.specifying_layout.C_edit.setText(str(params.get('specify_plane_C',0.0)))
       self.specifying_layout.D_edit.setText(str(params.get('specify_plane_D',0.0)))

     if params.has_key('selection1') and len(params['selection1'])>1:
       self.atoms_layout.atom_sel_1.setSelection(params['selection1'][0],params['selection1'][1])
     if params.has_key('selection2') and len(params['selection2'])>1:
       self.atoms_layout.atom_sel_2.setSelection(params['selection2'][0],params['selection2'][1])
     if params.has_key('selection3') and len(params['selection3'])>1:
       self.atoms_layout.atom_sel_3.setSelection(params['selection3'][0],params['selection3'][1])

     if params.has_key('crystal_face'):
       for i in range(self.crystal_layout.crystal_face_combo.count()):
         if str(params["crystal_face"]) == str(self.crystal_layout.crystal_face_combo.itemText(i)):
           self.crystal_layout.crystal_face_combo.setCurrentIndex(i)
           break

     if params.has_key('plane_mode'):
       if params['plane_mode'] == "plane":
         self.tabWidget.setCurrentIndex(self.tabWidget.indexOf(self.specifying_widget))
       if params['plane_mode'] == "selection":
         self.tabWidget.setCurrentIndex(self.tabWidget.indexOf(self.atoms_layout))
       if params['plane_mode'] == "crystal_face":
         self.tabWidget.setCurrentIndex(self.tabWidget.indexOf(self.crystal_widget))

     if params.has_key('selection_mode'):
        if params['selection_mode'] in MapPlane.MapPlane.selection_mode_alias:
          idx = MapPlane.MapPlane.selection_mode_alias.index(params['selection_mode'])
          self.atoms_layout.atom_spec_combo.setCurrentIndex(idx)

     if params.has_key('crystal'):
       for i in range(self.crystal_layout.crystal_data_dataCombo.count()):
         if "Map: "+str(params["crystal"]) == str(self.crystal_layout.crystal_data_dataCombo.itemText(i)):
           self.crystal_layout.crystal_data_dataCombo.setCurrentIndex(i)
           break
         if "Mol: "+str(params["crystal"]) == str(self.crystal_layout.crystal_data_dataCombo.itemText(i)):
           self.crystal_layout.crystal_data_dataCombo.setCurrentIndex(i)
           break
