"""
     python/ui/view.py: CCP4MG Molecular Graphics Program
     Copyright (C) 2001-2008 University of York, CCLRC

     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.
"""

import pygl_coord
from global_definitions import *


#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
class CView:
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------

  names = []
  insts = []

#-----------------------------------------------------------------------------
  def __init__(self,name):
#-----------------------------------------------------------------------------
    self.recentre = 0
    self.centring = 'extent'
    self.names.append(name)
    self.insts.append(self)
    self.name = name
    self.previous = "weight"
    self.com = [0.0,0.0,0.0]
    self.origin = [0.0,0.0,0.0]

    self.continuous_on = 1
    self.continuous_mode = 'roll'
    self.continuous_sleep = 0.005
    self.continuous_last_step = 0
    self.continuous_last_time = 0
    self.continuous_transform = {
      'rotate_x' : 0.0,
      'rotate_y' : 360.0, 
      'rotate_z' : 0.0,
      'translate_x' : 0.0,
      'translate_y' : 0.0,
      'translate_z' : 0.0,
      'zoom' : 0.0 }
    self.continuous_rock = {
      'fraction' : 0.1,
      'nsteps' : 60 }
    self.continuous_roll = {
      'nsteps' : 180 }
    

#------------------------------------------------------------------
  def delete(self):
#------------------------------------------------------------------
    i = self.insts.index(self)
    self.names.pop(i)
    self.insts.pop(i)

#------------------------------------------------------------------
  def set_attribute(self,attribute,value):
#------------------------------------------------------------------
    if attribute == 'recentre':
      self.recentre = value
    elif attribute == 'centring':
      self.centring = value
      self.recentre = 1

#-------------------------------------------------------------------
  def GetMaxDimension(self,selAtoms,nSelAtoms,extent=0):
#-------------------------------------------------------------------
      import time
      #t1 = time.time()
      """
      Work out how big viewport should be and tell OpenGL.
      """
      import symmetry
      ext = symmetry.molecule_extents_t(selAtoms,nSelAtoms)
      left = ext.get_left()
      right = ext.get_right()
      top = ext.get_top()
      bottom = ext.get_bottom()
      front = ext.get_front()
      back = ext.get_back()
      minx = left.get_x()
      maxx = right.get_x()
      miny = bottom.get_y()
      maxy = top.get_y()
      minz = front.get_z()
      maxz = back.get_z()
   
      xdist = maxx - minx
      ydist = maxy - miny
      zdist = maxz - minz

      if extent:
        return [ minx, maxx, miny, maxy, minz, maxz]
      else:
        dist = max(xdist,ydist,zdist)
        #t2 = time.time()
        #print "time to calculate max dim", t2 - t1
        return dist

#-----------------------------------------------------------------------------
  def centreModels(self,molobj=[],dispobj=[],recentre=1,resize=1, \
                   _resize='',**keywords):
#-----------------------------------------------------------------------------
    """
    This doesn't work properly yet, but is close ...
    """

    # resize command from GUI requires a resize but not a recentre
    #print "centreModels",molobj,dispobj,recentre,resize,_resize
    if _resize: recentre = 0

    com = [0.0,0.0,0.0]
    origin = [0.0,0.0,0.0]
    nat = 0

    glthread = BUILD().glthread

    # Centre on input specified dispobj or on all currently
    # visible MolDisp objects 
    if dispobj:
      d_list = dispobj
    elif molobj:
      d_list = []
      for m in molobj: d_list.extend(m.get_dispobj(object_type='MolDisp')) 
    else:
      import dataobj
      d_list = dataobj.get_dispobj('MolDisp')

    dispobj_list = []
    for d in d_list:
      #print "centreModels getNofAtoms",d.SelHandle.getNofAtoms()
      if d.parent.visible and d.visible and d.SelHandle.getNofAtoms() > 0:
        dispobj_list.append(d)
        
    #print "dispobj_list",dispobj_list

    # Get out now if no visible object
    if len(dispobj_list) <= 0:
      glthread.glevent.centre_on(self.origin)
      return

    maxsize = 0.0
    maxdist = 0.0

    # Centre on the midpoint of max/min extent of all objects
    if self.centring == "extent":
      extent = [999999.9,999999.9,999999.9,-999999.9,-999999.9,-999999.9]
      for sel in dispobj_list:
        for i in range(0,3):
          if sel.extent[i] < extent[i]: extent[i] =  sel.extent[i]
        for i in range(3,6):
          if sel.extent[i] > extent[i]: extent[i] =  sel.extent[i]
  
      
      # Recentre
      if recentre:
        self.com = [0.0,0.0,0.0]  
        self.origin = []
        for i in range(0,3):
          self.origin.append( (extent[i+3] + extent[i])/2.0 )
        #print "view self.origin",self.origin
        if resize:
          maxextent = 6.0
          for i in range(0,3):
            d = extent[i+3] - extent[i]
            if d > maxextent: maxextent = d
          glthread.setmaxviewsize(maxextent/1.8)
          zoom = 1
        else:
          zoom = 0
        glthread.glevent.centre_on(self.origin,radius=60.0,zoom=zoom)
        
    else:
      for sel in dispobj_list:
        nSelAtoms,selAtoms = sel.SelHandle.getSelIndex()
        size =  self.GetMaxDimension(selAtoms,nSelAtoms)
        o1cart = pygl_coord.Cartesian(sel.origin[0],sel.origin[1],sel.origin[2])
        if size > maxsize:
          maxsize = size
        for sel2 in dispobj_list:
          o2cart = pygl_coord.Cartesian(sel2.origin[0],sel2.origin[1],sel2.origin[2])
          dist = (o1cart-o2cart).length()
          if dist > maxdist:
            maxdist = dist
            
          for i in [0,1,2]:
# Find weighted centre for all displayed models. # Possibly broken
            if self.centring == "weight":
              if self.previous == "weight":
                #print sel.com[i], sel.origin[i]
                com[i] = com[i] + (sel.com[i] * sel.nSelAtoms)
                origin[i] = origin[i] + (sel.origin[i] * sel.nSelAtoms)
              else:
                sel.origin[i] = origin[i]
                com[i] = com[i] + (sel.com[i] * sel.nSelAtoms)
# Overlay models # Probably broken!
            elif self.centring == "overlay":
              com[i] = com[i] + (sel.com[i] * sel.nSelAtoms)
              sel.origin[i] = -sel.com[i]
              if self.previous == "weight":
                 pass
          nat = nat + sel.nSelAtoms

      self.previous = self.centring

      if nat*nat < 0.00000001:
         return

      if maxsize > maxdist:
        re_size = maxsize
      else:
        re_size = maxdist + maxsize

        
      glthread.setmaxviewsize(re_size/2)
      #print "after setmaxviewsize 2 re_size",re_size
      for i in [0,1,2]:
        self.com[i] = com[i]  / nat
        self.origin[i] = origin[i]  / nat
      #print "self.origin",self.origin
      glthread.glevent.centre_on(self.origin)

    self.recentre = 0

#----------------------------------------------------------------------
  def SetContinuousTransform ( self,mode='rock'):
#----------------------------------------------------------------------
    if not mode:
      self.continuous_on = 1
      # ?Todo  Revert to initial view
    else:
      import time
      self.continuous_mode = mode
      self.continuous_on = 1
      self.continuous_last_time = time.time()
      self.continuous_last_step = 0

      if mode == 'rock':
        last_phase = 0.0
        rock_delta = []
        import math,copy
        for step in range (1,self.continuous_rock['nsteps']+1):
          phase = math.sin ( float(step) * math.pi * 2.0 / \
                             float(self.continuous_rock['nsteps'] ) )
          rock_delta.append(phase-last_phase)
          last_phase=copy.deepcopy(phase)

        #print "rock_delta",rock_delta
        self.continuous_rock['deltas'] = rock_delta
                          
      '''  
      if interrupt_label:
        GUI.GUI.insts.StartInterrupt(gui=interrupt_label)
      ''' 
      
#-----------------------------------------------------------------------
  def UpdateContinuousTransform ( self, jq=None ):
#-----------------------------------------------------------------------
    if not  self.continuous_on: return

    import time
    import utils
    import rebuild
    import copy

    glthread = BUILD().glthread

    self.continuous_last_step = self.continuous_last_step+1

    # make sure we are not going too fast
    delay = (self.continuous_last_time + self.continuous_sleep) -  time.time()
    if delay>0.0:
      #print "ApplyContinuousTransform delay",delay
      time.sleep(delay)
    self.continuous_last_time = time.time()
    
    glthread.Wait()
    glthread.Pause()
    t = self.continuous_transform
    if self.continuous_mode == 'roll':
      d = 1.0/float(self.continuous_roll['nsteps'])
    else:
      i = self.continuous_last_step % self.continuous_rock['nsteps']
      d = self.continuous_rock['deltas'][i] * self.continuous_rock['fraction']
    glthread.rotate_camera([t['rotate_x']*d,t['rotate_y']*d,t['rotate_z']*d])
    #glthread.move_camera(t['translate_x']*d,-t['translate_y']*d,t['translate_z']*d)
    #glthread.zoom_camera (t['zoom']*d )
    glthread.UnPause()

      
       
     

    '''
    # After recording a rock return to initial position
    if transform_rock:
      # return to original position
      glthread.Wait()
      glthread.Pause()
      #print "restore",float(rotate_y)/2
      glthread.rotate_camera((float(rotate_x)/2,float(rotate_y)/2,float(rotate_z)/2))
      glthread.move_camera( float(translate_x)/2, -float(translate_y)/2, float(translate_z)/2)
      glthread.zoom_camera ( float(zoom)/2 )
      glthread.UnPause()
    '''

    '''
    if interrupt_label:
      GUI.GUI.insts.EndInterrupt(interrupt_label)
    '''