"""
     python/ui/Animation.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.
"""
import mmut
import mmdb2 as mmdb
from global_definitions import *
import utils
import os

class Animation:

  ParamsManager = None

#-------------------------------------------------------------------
  def __init__(self,parent='',params={},load=1,**kw):
#-------------------------------------------------------------------
    params.update(kw)
    self.parent = parent
    # current_frame = [current_frame, increment to next frame]
    self.current_frame = [-999,1]
    self.frame_range = [1,9999]
    self.legend_obj=None
    self.nowRunning=0

    setattr(self,'filename',params.get('filename',[]))
    setattr(self,'file_format',params.get('file_format','multiple_PDBs'))
    
    if load and self.filename:
      rv = self.loaddata()
      if rv: self.delete()
    
      AnimationParamsManager()

      self.update_data_status(params=params)

#------------------------------------------------------------------------
  def update_data_status(self,params={}):
#------------------------------------------------------------------------
    rv = 0
    if params.has_key('current_frame') and \
           params['current_frame'] != self.current_frame and \
            params['current_frame'][0] != -999:
      rv = 1
      self.set_frame(frame= params['current_frame'][0], \
                     direction=params['current_frame'][1])

    #print "Animation update_data_status",params['legend_name'],self.legend_obj
    #if self.legend_obj: print "legend_obj.name",self.legend_obj.name
    if  params.has_key('legend_name'):
      if params['legend_name'] and not self.legend_obj:
        rv = 1
        self.add_legend()
      elif (not params['legend_name']) and self.legend_obj:
        rv = 1
        self.legend_obj.delete()
        self.legend_obj = None
    return rv
        
  def delete(self):
    if self.legend_obj: self.legend_obj.delete()
    if hasattr('self','NMR_models_molHnd'): del self.NMR_models_molHnd
    if hasattr(self.parent,'animation'): del self.parent.animation


  def params(self):
    pars = {}
    for item in ['filename','file_format','current_frame','frame_range']:
      pars[item] = getattr(self,item)
    if self.legend_obj:
      pars['legend_name'] = self.legend_obj.name
    else:
      pars['legend_name'] = ''
      
    return pars
  
#-------------------------------------------------------------------
  def loaddata(self,pdbfile='',DIR_pdbfile='',last_frame='',**keywords):
#-------------------------------------------------------------------

    if not pdbfile: pdbfile = self.filename[1]
    if not DIR_pdbfile:DIR_pdbfile =  self.filename[0]

    # Try to figure out what the file selection means!
    if pdbfile and self.file_format=='multiple_PDBs':
      return self.set_filebase(pdbfile=pdbfile,DIR_pdbfile=DIR_pdbfile,last_frame=last_frame)
      
    elif  pdbfile and self.file_format=='NMR_models':
      path = utils.get_filename(DIR_pdbfile,pdbfile)
      if not os.path.exists(path): return 1
      molHnd = mmdb.Manager()
      #rv = molHnd.ReadCoorFile(path)
      rv = utils.ReadCoorFile(molHnd,path)
      if rv:
        del molHnd
        MGGUI().WarningMessage("Error attempting to read 'NMR' model file")
        return 1
      self.frame_range=[1,molHnd.GetNumberOfModels()]
      self.filename=[DIR_pdbfile,pdbfile,path]
      self.NMR_models_molHnd = molHnd
      if self.parent.nmr_models>1:
        MGGUI().WarningMessage("Animation successfully loaded. The first model in the structure will be updated with trajectory coordinates when you run the animation")
        return 0

#--------------------------------------------------------------------------
  def set_filebase(self,pdbfile='',DIR_pdbfile='FULLPATH',last_frame=''):
#--------------------------------------------------------------------------
    import GUI
    digits = ['0','1','2','3','4','5','6','7''8','9']
    path = utils.get_filename(DIR_pdbfile,pdbfile)
    #print "set_filebase path",path
    if not os.path.exists(path): return 1
    split =  os.path.splitext(path)

    #We expect the filename of the form blahN.pdb
    #and the first N is probably 1
    for nnum in range(-1,-5,-1):
      if not digits.count(split[0][nnum]): break
    #print "nnum",nnum
      
    if nnum==-1:
      MGGUI().WarningMessage('Animation file name does not have expected format')
      return 1
    else:
      nnum=nnum+1
      
    self.filebase = [ split[0][0:nnum], split[1] ]
    self.filename=[DIR_pdbfile,pdbfile,path]

    #Find the last frame
    iframe = int(split[0][nnum:])
    continu = 1
    while continu:
      if os.path.exists(self.filebase[0]+str(iframe+1)+ self.filebase[1]):
        iframe = iframe+1
      else:
        continu = 0
          
    if last_frame and utils.safeInt(last_frame,9999)<iframe:
      self.frame_range = [int(split[0][nnum:]),utils.safeInt(last_frame,9999)]
    else:
      self.frame_range = [int(split[0][nnum:]), iframe]

    #print "Animation.set_filebase frame range",self.frame_range
    #import glob
    #files = glob.glob(split[0][0:nnum]+'*'+split[1])
    #print "Animation.set_filebase files",files

    return 0
      
#--------------------------------------------------------------------------
  def get_filelist(self,pdbfile='',DIR_pdbfile=''):
#--------------------------------------------------------------------------
    # Given name of one example file return list of all PDB files
    # This method not currently used
    
    digits = ['0','1','2','3','4','5','6','7''8','9']
    path = utils.get_filename(DIR_pdbfile,pdbfile)
    #print "set_filebase path",path
    if not os.path.exists(path): return []
    split =  os.path.splitext(path)

    #We expect the filename of the form blahN.pdb
    #and the first N is probably 1
    for nnum in range(-1,-5,-1):
      if not digits.count(split[0][nnum]): break
    #print "nnum",nnum
      
    if nnum==-1:
      return []
    else:
      nnum=nnum+1
      
    filebase = [ split[0][0:nnum], split[1] ]

    #Find the last frame
    filename_list = [pdbfile]
    iframe = int(split[0][nnum:])
    continu = 1
    while continu:
      file = filebase[0]+str(iframe+1)+ filebase[1]
      if os.path.exists(file):
        filename_list.append(file)
        iframe = iframe+1
      else:
        continu = 0

    return filename_list

#-----------------------------------------------------------------      
  def toggle_animation(self,mode=-1):
#-----------------------------------------------------------------
    # This method not used if program running with GUI but might be needed
    # for some none-GUI functioning
    if mode==0 or (mode<0 and self.nowRunning):
      self.nowRunning = 0
      MAINWINDOW().removeAnimationHandler('Animation of '+self.parent.name_label)
    elif mode>0 or (mode<0 and not self.nowRunning):
      self.nowRunning = 1
      MAINWINDOW().addAnimationHandler('Animation of '+self.parent.name_label,self.next_frame)
    
#--------------------------------------------------------------------------
  def set_frame(self,frame=0,direction='',**keywords):
#--------------------------------------------------------------------------
    if frame:
      #print "set_frame frame",frame
      if frame != self.current_frame[0]:
        rv = self.load_frame(iframe=frame)
        if rv[0]: MGGUI().WarningMessage(rv[1])

    if direction:
      self.current_frame[1] = int(direction)

  def getFrame(self):
    return  self.current_frame[0]

  def setIncrement(self,inc):
    self.current_frame[1] = inc

  def getIncrement(self):
    return self.current_frame[1]

#----------------------------------------------------------------------
  def next_frame(self):
#----------------------------------------------------------------------
    import copy,rebuild
    iframe = self.current_frame[0] + self.current_frame[1]
    if iframe < self.frame_range[0]:
      iframe =  copy.deepcopy(self.frame_range[0])
      self.current_frame = [self.frame_range[0],1]
    elif iframe>self.frame_range[1]:
      iframe =  copy.deepcopy(self.frame_range[1])
      self.current_frame = [self.frame_range[1],-1]
    else:
      self.current_frame[0]=iframe
    rv = self.load_frame(iframe=iframe)
    #print "Animation.next_frame  next",iframe,self.current_frame,rv
    return rv

#----------------------------------------------------------------------
  def load_frame(self,iframe=''):
#----------------------------------------------------------------------
    import mmdb2 as mmdb

    #print "Animation.load_frame",iframe, self.file_format 

    if iframe == -1:
      #Restore the original coordinates
      molHnd = mmdb.Manager()
      path = self.parent.filename[2]
      #rv = molHnd.ReadCoorFile(path)
      rv = utils.ReadCoorFile(molHnd,path)
      if rv:
        del molHnd
        return[1,'Error loading PDB file '+path]

      rv = self.parent.molHnd.CopyCoordinates(molHnd)
      del molHnd
      if rv:
        return [1,"Error setting coordinates from "+path]
      
    #print "load_frame iframe",iframe,self.current_frame,self.frame_range
    elif self.file_format == 'multiple_PDBs':
      path = self.filebase[0]+str(iframe)+ self.filebase[1]
      #print "loading",path
      if not os.path.exists(path):
        return[1,'Error PDB file '+path+' does not exist for frame '+str(iframe)]

      molHnd = mmdb.Manager()
      #rv = molHnd.ReadCoorFile(path)
      rv = utils.ReadCoorFile(molHnd,path)
      if rv:
          del molHnd
          return[1,'Error loading PDB file '+path]

      rv = self.parent.molHnd.CopyCoordinates(molHnd)
      #Apply any transformation currently set for this model
      self.parent.molHnd.ReApplyTransform()
      
      del molHnd
      if rv:
        return [1,"Error setting coordinates from "+path]

    elif  self.file_format == 'NMR_models':
      #print "NMR_models_molHnd",self.NMR_models_molHnd
      rv = self.parent.molHnd.CopyCoordinates(self.NMR_models_molHnd,iframe)
      if rv:
        return [1,"Error setting coordinates for frame "+str(iframe)]

    #Update the GUIs and possible legend
    self.current_frame[0] = iframe
    if self.current_frame[1] == '1':
      dirn = 'forwards'
    else:
      dirn = 'backwards'  
    if self.legend_obj:
      self.legend_obj.set_selection(text=str(self.current_frame[0]+1))

    if AnimationParamsManager().get('update_properties'):
      self.parent.update_dependents('reconfigure')
    else:
      self.parent.update_dependents('transform')

    return [0]

#-------------------------------------------------------------------
  def get_number_of_frames(self):
#-------------------------------------------------------------------
    return self.frame_range[1]-self.frame_range[0]+1


#--------------------------------------------------------------------
  def add_legend(self,legend='',master='',attribute=''):
#--------------------------------------------------------------------
    #print "Animation add_legend",attribute
    if attribute == 'delete':
      self.legend_obj = None
    else:  
      if self.legend_obj: return
      import Legend
      self.legend_obj = Legend.Legend(parent=GL2DMANAGER().name, \
                        name='animation_legend'+self.parent.name,
                        master=self.parent.name)
      self.legend_obj.set_selection(text=str(self.current_frame[0]+1))
      self.parent.add_dependency(self.legend_obj,['delete'],[self.add_legend])


#-----------------------------------------------------------------
  def copy_data_files(self,project='',dir='',overwrite=1):
#-----------------------------------------------------------------
    '''
    Copy the data file(s) to another directory
    '''
    import shutil,glob
    copied = []
    errors = []
    if not self.filename: return [len(errors),errors,copied]
    if self.file_format == 'multiple_PDBs':
      self.set_filebase(pdbfile=self.filename[1],DIR_pdbfile=self.filename[2])
      if not self.filebase: return [1,['No animation files defined'],[]]
      filnlist = glob.glob(self.filebase[0]+'*'+ self.filebase[1])
    else:
      filnlist = [self.filename]

    #print "animation copy_data_files filnlist",filnlist
    for filn in filnlist:
      ret=utils.copy_data_file(filename=filn,dir=dir,overwrite=overwrite)
      errors.append(ret[1])
      copied.append(ret[2])

    if project: self.filename=[project,self.filename[1],ret[1]]

    return [len(errors),errors,copied]

#--------------------------------------------------------------------
#--------------------------------------------------------------------


def AnimationParamsManager():
   import services
   if not Animation.ParamsManager:
     Animation.ParamsManager =  services.ParamsManager( name='Animation_param', \
              title='Animation and flashing',
              picture_definition = 1,
              gui=[ 'update_properties','delay','flash_delay'],
              default = {'update_properties': 1,
                         'delay' : 0.1,
                         'flash_delay' : 0.5
                        },
              definition={ 'update_properties': dict(type='bool',label='Update properties (e.g. secondary str.) each step'),
                           'delay' : dict(type=float, label='Minimum time delay between updates',
                                         style='spinbox',max=10.0,min=0.1,step=0.1 ),
                           'flash_delay' : dict(type=float, label='Minimum delay in flashing',
                                         style='spinbox',max=10.0,min=0.1,step=0.1 )
                           },
              help = 'animation')

   return Animation.ParamsManager
 
