"""
     python/ui/EditHistory.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
class EditHistory:

#-------------------------------------------------------------------
  def __init__(self):
#-------------------------------------------------------------------
    EditHistory.insts = self
    self.edit_history = []
    self.counter = -1


#-----------------------------------------------------------------
  def EditHistory_Append(self,application='',selection='all',action='',supercede=0,**keywords):
#-----------------------------------------------------------------
    # Keep track of whether data has been editted - mostly to
    # handle save/restore issues
    # In future need to have some sort of version number for each
    # update and track the type of changes between versions

    # action = transform/reconfigure/edit
    import history
    item = {
        'application' : application,
        'action'      : action,
        'selection'   : selection,
        'command_no'  : history.CHistory.insts.n_commands }
    self.edit_history.append(item)
    self.counter = self.counter + 1
      
    # Mark all graphical objects for display update and
    # recalculate the extent
    for obj in  self.objinsts:
      obj.do_updatedisplay = 1
      if obj.object_type == 'MolDisp':obj.set_extent()
    
    return self.counter

#-------------------------------------------------------------------
  def EditHistory_Save (self,application='',action='',mode='save',**keywords):
#-------------------------------------------------------------------
    self.edit_history = []
    self.counter = -1
    
#-------------------------------------------------------------------
  def EditHistory_Undo (self,application='',action='',mode='save',**keywords):
#-------------------------------------------------------------------
    self.edit_history.pop()
    self.counter = self.counter -1
  
#--------------------------------------------------------------------
  def saveMMDBF(self):
#--------------------------------------------------------------------
    '''
    Save the current data to an MMDB binary file
    '''
    import os
    import utils
    if not hasattr(self,'saveMMDBF_files'):
      self.saveMMDBF_files = 1
    else:
      self.saveMMDBF_files = self.saveMMDBF_files +1
    #print " saveMMDBF_files",self.saveMMDBF_files
    dir = os.path.join(utils.get_CCP4MG(),'saved_files')
    if not os.path.exists(dir): os.mkdir(dir)
    dir = os.path.join(dir,self.name)
    if not os.path.exists(dir): os.mkdir(dir)
    filename = os.path.join(dir,str(self.saveMMDBF_files)+'.'+'mmdb')
    #print "filename",filename
    self.molHnd.WriteMMDBF(filename)
    return  self.saveMMDBF_files



#---------------------------------------------------------------------
  def restoreMMDBF(self,id=0,mode=mmut.MMAN_COORDINATES):
#---------------------------------------------------------------------
    '''
    Restore model from MMDB binary file
    mode = MMUT_COORDINATES : restore coordinates only
    '''
    import os
    import utils
    import mmut
    import mmdb2 as mmdb
    import resources
    if id==0:
    # Restore from the original loaded PDB file
      # Create new mmdb object and load from the saved file
      new_molHnd = mmut.CMMANManager(RESOURCES().SRS,RESOURCES().CMolBondParams)
      new_molHnd.SetFlag(mmdb.MMDBF_AutoSerials)
      new_molHnd.SetFlag(mmdb.MMDBF_IgnoreRemarks)
      new_molHnd.SetFlag(mmdb.MMDBF_IgnoreBlankLines)
      #new_molHnd.SetFlag(mmdb.MMDBF_EnforceUniqueChainID)
      new_molHnd.SetFlag(mmdb.MMDBF_IgnoreHash)
      #rv = new_molHnd.ReadCoorFile(self.filename)
      rv = utils.ReadCoorFile(new_molHnd,self.filename)
    else:
      if not hasattr(self,'saveMMDBF_files'): return 1
      if id<0:id = self.saveMMDBF_files
      dir = os.path.join(utils.get_CCP4MG(),'saved_files',self.name)
      filename =  os.path.join(dir,str(id)+'.'+'mmdb')
      if not os.path.exists(filename): return 2

      # Create new mmdb object and load from the saved file
      new_molHnd = mmut.CMMANManager(RESOURCES().SRS,RESOURCES().CMolBondParams)
      rv = new_molHnd.ReadMMDBF(filename)
    #print "restoreMMDBF rv",rv
    if rv: return 3
    
    if [mmut.MMAN_COORDINATES,mmut.MMAN_XTLDATA].count(mode):
      # Copy just coordinates or xtl data (xyz B occ)
      rv = self.molHnd.RestoreData(new_molHnd,mode)
      #print "RestoreData rv",rv
      if rv: return 4
      self.update_dependents('reconfigure')
      del new_molHnd
      return 0
    else:
      # Save the existing display objects (see dataobj.py DataObjShadow for
      # similar mechanism)
      import dataobj
      save_objnames = []
      save_objinsts = []
      delete_list = []
      for i in range(0,len(self.objinsts)):
        #Save objects which do not have a master controlling application
        if not self.objinsts[i].master_application:
          #print "saving",self.objinsts[i]
          save_objnames.append(self.objnames[i])
          save_obj = dataobj.DispObjShadow()
          save_objinsts.append( save_obj )
          save_obj.save(self.objinsts[i])
          delete_list.append(self.objinsts[i])

      for item in delete_list: item.delete()
      #print "save_objinsts",save_objinsts
      #print "self.objinsts",self.objinsts

      # Update the MolData
      del self.molHnd
      self.molHnd = new_molHnd
      print "FIXME (EditHistory.py) - A preference here for aromaticize."
      output = self.molHnd.GetMolBonds()

      #for model in range(1,self.molHnd.GetNumberOfModels()+1):
      #  self.molHnd.GetModel(model).CalcSecStructure(PM('SecStr_param').params['flagBulge'])
      
      # Reload the display objects
      for obj in save_objinsts:
        obj.restore(self.name)
        
      # Clean up the shadow objects
      del save_objinsts

#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
class EditHistoryManager:
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
  insts = None
  initialise = {
    'multi_save_mode' : 'append',
    'save_all' : 1}
  
#-------------------------------------------------------------------------
  def __init__(self,params={}):
#-------------------------------------------------------------------------
    EditHistoryManager.insts = self
    for key,value in EditHistoryManager.initialise.items():
      setattr(self,key,value)
    for key,value in params.items():
      setattr(self,key,value)

    self.open_windows = {}

#-------------------------------------------------------------------
  def getparams(self):
#--------------------------------------------------------------------
    params = {}
    for key in EditHistoryManager.initialise.keys():
      params[key] = getattr(self,key)
    return params

  def setparams(self,params):
    for key,value in params.items():
      setattr(self,key,value)

    
#----------------------------------------------------------------------
  def SaveMultipleFiles(self,DataObj_types=['MolData'],DataObj_list=[],
                        application_tag='',title='',command='',offset=''):
#----------------------------------------------------------------------
    import GUI,os,glob

    version_tag = self.next_version_tag(multi_save_mode=self.multi_save_mode,
                                        application_tag=application_tag)
    self.open_windows[application_tag] = [title,DataObj_types,DataObj_list,command]

    GUI.GUI.insts.CreateGUIWindow('save_multi', self, [

      ['TITLE','Save files from '+title],
      ['RETURN','EH().handle_save_multi'],
      ['HELP','editing'],
      ['OFFSET',offset],
      ['PARAM','save_all',
       ['TYPE','_logical'],
       ['INITIAL',str(self.save_all)]],
      ['PARAM','multi_save_mode',
       ['TYPE','_text'],
       ['RETURN','EH().handle_save_multi',
        ['RETURN_LIST','multi_save_mode','application_tag']],
       ['INITIAL',self.multi_save_mode]],
      ['PARAM','application_tag',
       ['TYPE','_text10'],
       ['INITIAL',application_tag]],
      ['PARAM','version_tag',
       ['TYPE','_text10'],
       ['INITIAL',version_tag]],
      ['DISPLAY','WINDOW',
       #['LINE',
       #  ['RADIOBUTTON','multi_save_mode','dir','Save to directory with tag name']],
       ['LINE',
         ['RADIOBUTTON','multi_save_mode','append','Save to filename with tag appended']],              
       #['LINE',
       #  ['RADIOBUTTON','multi_save_mode','prepend','Save to filename with tag prepended']],              
       ['LINE',
         ['RADIOBUTTON','multi_save_mode','backup','Save to same filename and backup to file with tag']],
       ['LINE',
         ['RADIOBUTTON','multi_save_mode','individual','Save to each file individually']],
       ['LINE',
        ['WIDGET','save_all'],
        ['LABEL','Save all files even if unchanged']],

       ['LINE',
        ['LABEL',"Tag files '"+application_tag],
        ['WIDGET','version_tag'],
        ['LABEL',"'"]],         

       ['BUTTONS', ['ok',['LABEL','Save files'],['CLOSE']],
                ['dismiss',['LABEL','Quit'],['CLOSE']]]]] )


#------------------------------------------------------------------------
  def handle_save_multi(self,multi_save_mode='',application_tag='',
                        version_tag='',exit='',**keywords):
#------------------------------------------------------------------------
    import GUI,os,dataobj,string

    if multi_save_mode and multi_save_mode != self.multi_save_mode:
      self.multi_save_mode = multi_save_mode
      self.next_version_tag(multi_save_mode=self.multi_save_mode,application_tag=application_tag,update_GUI=1)
      return


    if exit != 'ok': return

    if not self.open_windows.has_key(application_tag):
      GUI.GUI.insts.WarningMessage('Error handling file save for '+application_tag)
      return 1

    
    for key in ['save_all']:
      if keywords.has_key(key) and keywords[key]:
        setattr(self,key,keywords[key])
    self.save_all = int(self.save_all)


    if self.save_all:
      self.to_save_list = []
      for type in  self.open_windows[application_tag][1]:
        self.to_save_list.extend(dataobj.get_dataobj(object_type=type))
    else:
      self.to_save_list =  self.open_windows[application_tag][2]

    if not self.to_save_list:
      self.exit_save_multi(message='no files designated to save',application_tag=application_tag)
      return

    # Keep list of saved dataobj to return to the calling application
    self.saved_list = []
    
    if self.multi_save_mode == 'dir':
      project = CCP4DATABASE().get_current_project()
      project_dir = CCP4DATABASE().get_project_dir(project)
      if not os.path.exists( project_dir ):
        self.exit_save_multi(message='project directory does not exist\\n'+project_dir,application_tag=application_tag)
        return
      
      save_dir = os.path.join(project_dir,application_tag+version_tag)
      #print "save_dir",save_dir
      if os.path.exists(save_dir):
         self.exit_save_multi(message='save directory already exist\\n'+save_dir,application_tag=application_tag)
         return
      try:
        os.mkdir(save_dir)
      except:
        self.exit_save_multi(message='error creating directory\\n'+save_dir,application_tag=application_tag)
        return

      for dobj in self.to_save_list:
        save_file = os.path.join(save_dir,dobj.filename[1])
        #print "save_file",save_file
        rv = dobj.save_restore_data(save_file=os.path.join(application_tag+version_tag,dobj.filename[1]),DIR_save_file=project,exit=1,save_selection='save')
        if rv:
          return self.exit_save_multi(application_tag=application_tag,saved_list=self.saved_list)
        else:
          self.saved_list.append(dobj)

        
    elif self.multi_save_mode == 'backup':

      for dobj in self.to_save_list:
        save_file = dobj.filename[2]+'.'+application_tag+version_tag+'.backup'
        try:
          os.rename(dobj.filename[2],save_file)
        except:
           self.exit_save_multi(message='error renaming file\\n'+save_file+'\\nAborting further saves',application_tag=application_tag)
           return
        try:
          rv = dobj.save_restore_data(save_file=dobj.filename[1],DIR_save_file=dobj.filename[0],exit=1,save_selection='save')
          if rv:
            self.exit_save_multi(message='error saving file '+dobj.filename[2]+'\\nAborting further saves',application_tag=application_tag,saved_list=self.saved_list)
            return
          else:
            self.saved_list.append(dobj)
        except:
          self.exit_save_multi(message='error saving file\\n'+dobj.filename[2]+'\nAborting further saves',application_tag=application_tag)
          return
      
    elif ['append','prepend'].count(self.multi_save_mode):

      for dobj in self.to_save_list:
        file0 = os.path.splitext(dobj.filename[1])
        # Remove any previous tags
        p = string.find(file0[0],application_tag)
        if p>=0:
          if self.multi_save_mode == 'prepend':
            #This will not work with user supplied tag containing underscore!!
            q = string.find(file0[0],'_',p+len(application_tag))
            if q>=0:
              fnam = file0[0][q+1:]
          else:
            fnam = file0[0][0:p-1]
        else:
          fnam = file0[0]
          #print "file0,fnam",file0,fnam
          
        if self.multi_save_mode == 'append':
          save_file = fnam+'_'+application_tag+version_tag+file0[1]
        else:
          save_file = application_tag+version_tag+'_'+fnam+file0[1]
        #print "save_file",save_file
        
        rv = dobj.save_restore_data(save_file=save_file,DIR_save_file=dobj.filename[0],exit=1,save_selection='save')
        if rv:
          self.exit_save_multi(message='error saving file '+save_file+'\\nAborting further saves',application_tag=application_tag,saved_list=self.saved_list)
          return
        else:
          self.saved_list.append(dobj)

    elif self.multi_save_mode == 'individual':
      self.to_save_index = -1
      self.handle_save_individual(exit='no',application_tag=application_tag)
      return

    self.exit_save_multi(application_tag=application_tag,saved_list=self.saved_list)


#-------------------------------------------------------------------------
  def handle_save_individual(self,application_tag='',save_file='',DIR_save_file='',exit='',**keywords):
#-------------------------------------------------------------------------
    import GUI
    #print "handle_save_individual",exit
    if exit == '0':
      #Aborted save
      pass
    elif exit == '1':
      dobj = self.to_save_list[self.to_save_index]
      rv = dobj.save_restore_data(save_file=save_file,DIR_save_file=DIR_save_file,exit=1,save_selection='save')
      if rv:
        GUI.GUI.insts.WarningMessage('Error saving file '+save_file)
      else:
        self.saved_list.append(dobj)

          
    self.to_save_index = self.to_save_index +1
    if self.to_save_index>= len(self.to_save_list):
      self.exit_save_multi(application_tag=application_tag,saved_list=self.saved_list)
    else:
      dobj = self.to_save_list[self.to_save_index]
      
      GUI.GUI.insts.CreateGUIWindow ('save_'+dobj.name,self, [
            ['TITLE','Save '+dobj.name_label],
            ['RETURN','EH().handle_save_individual'],
            ['PARAM','application_tag',
               ['TYPE','_text'],
               ['INITIAL',application_tag]],
            ['PARAM','save_file',
               ['TYPE','_coord_file'],
               ['INITIAL',dobj.filename[0]]],
            ['DISPLAY','SELECTFILE',
              ['FILE','save_file','-filetype','_pdb_file','coord_file_types', \
               '-unknown','-filter','*.pdb','-default',dobj.filename[2] ]]
           ])


#--------------------------------------------------------------------------
  def exit_save_multi(self,message='',application_tag='',saved_list=[]):
#--------------------------------------------------------------------------
    import GUI
    if message:
      #print "message",message
      GUI.GUI.insts.WarningMessage('Error handling file save for '+self.open_windows[application_tag][0]+'\\n'+message)
      ret = 1
    else:
      ret = 0
    if self.open_windows[application_tag][3]:
      apply(self.open_windows[application_tag][3],[],{'saved_list': saved_list})
    return ret

#--------------------------------------------------------------------------
  def next_version_tag(self,multi_save_mode='dir',application_tag='',update_GUI=0):
#--------------------------------------------------------------------------

    import os,glob,string,utils,GUI

    project_dir = CCP4DATABASE().get_project_dir(CCP4DATABASE().get_current_project())

    maxtag = 0
    if multi_save_mode == 'dir':
      dlist = glob.glob(os.path.join(project_dir,application_tag+'*'))
      #print "dlist",dlist
      for d in dlist:
        if os.path.isdir(d):
          p = string.rfind(d,application_tag)
          if p>0:
            maxtag = max(maxtag,utils.safeInt(d[p+len(application_tag):],-1))
    else:
      if multi_save_mode == 'backup':
        dlist = glob.glob(os.path.join(project_dir,'*.'+application_tag+'*.backup'))
        term = '.backup'
      elif multi_save_mode == 'append':      
        dlist = glob.glob(os.path.join(project_dir,'*_'+application_tag+'*.*'))
        term='.'
      else:
        dlist = glob.glob(os.path.join(project_dir,application_tag+'*_*.*'))
        term='_'
      #print "dlist",dlist
      for d in dlist:
        p = string.rfind(d,application_tag)
        if p>=0:
          q = string.find(d,term,p+len(application_tag))
          if q>=0:
            #print "vtag",d[p+len(application_tag):q]
            maxtag = max(maxtag,utils.safeInt(d[p+len(application_tag):q],-1))


    if update_GUI:
      GUI.GUI.insts.UpdateGUIWindow('save_multi', [
        ['PARAM','version_tag',
          ['TYPE','_text10'],
          ['INITIAL',str(maxtag+1)]]] )



    return str(maxtag+1)
