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

     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 pickle
import os
import re
from global_definitions import *

# colours stored as a list of colour definitions in rgbreps
# The individual colour definition is a list of two items:
# the name of the colour and the rgb representation which 
# is a three integer list

# The colour of atoms etc is usually represented by an integer
# number which is the index to the colour in the rgbreps list 

# The initial colours are stores as python list in resource file
#execfile(os.path.join(os.environ["CCP4MG"],"python","etc","colour.py"))
# or are restored from $HOME/.CCP4MG/mg_colours.pkl

class COLOUR:
  insts = ''

#-------------------------------------------------------------------
  def __init__(self):
#-------------------------------------------------------------------
    COLOUR.insts = self
    usingOldColours = False
    # A number code for the next user defined colour
    self.new_name_number = 1
    self.rgbreps = []
    self.n_standard_rgbreps = 0
    self.load_default()
    import cprim
    self.RGBReps = cprim.RGBReps()
    self.initialiseRGBReps()
    
#-----------------------------------------------------------------
  def initialiseRGBReps(self):
#-----------------------------------------------------------------    
    # Initiallise the C class
    ii = 0
    for name,rep in self.rgbreps:
      self.RGBReps.AddColour(name,rep[0],rep[1],rep[2],rep[3])
      #print "COLOUR.initialiseRGBReps",ii,name,rep,self.RGBReps.GetNumberOfColours()
      ii = ii + 1

#-------------------------------------------------------------------
  def new(self,newname='',newrgb=[],rep=None):
#-------------------------------------------------------------------
# Add a new colour representation to the rgbreps list
    if not newname:
      newname = 'user_'+str(self.new_name_number)
      while self.index(newname,safe=0)>=0:
        self.new_name_number = self.new_name_number +1
        newname = 'user_'+str(self.new_name_number)
      self.new_name_number = self.new_name_number +1
    else:
      if newname[0:5] == 'user_':
        import utils
        nn = utils.safeInt(newname[5:],-1)
        if nn>0: self.new_name_number = max(self.new_name_number,nn+1)
    if not newrgb:
      newrgb = [0.5,0.5,0.5,1.0]
    elif len(newrgb) == 3:
      newrgb.append(1.0)
      
    for rgbrep in self.rgbreps:
      if newname == rgbrep[0]:
        #print "Colour already defined:",newname
        self.reset(newname,newrgb)
        return newname
      
    self.rgbreps.append([newname,newrgb])
    self.RGBReps.AddColour(newname,newrgb[0],newrgb[1],newrgb[2],newrgb[3])
    return newname
 
#-------------------------------------------------------------------
  def index(self,name,safe=1):
#-------------------------------------------------------------------
# Return the list index for the colour name
# Also test if there is a match for the name with underscore
# replaced by spaces 
    i = -1
    for rgbrep in self.rgbreps:
      i = i + 1
      if name == rgbrep[0]:
        return i

    # Usually return a 'safe' value
    if safe:
      return 0
    else:
      return -1
    
#-------------------------------------------------------------------
  def colour_index(self,name,safe=1):
#-------------------------------------------------------------------
# Return the list index for the colour name
# Also test if there is a match for the name with underscore
# replaced by spaces 
    i = -1
    for rgbrep in self.rgbreps:
      i = i + 1
      if name == rgbrep[0]:
        return i

    # Usually return a 'safe' value
    if safe:
      return 0
    else:
      return -1

#------------------------------------------------------------------
  def RGB(self,name):
#------------------------------------------------------------------
    #col = self.RGBReps.GetColour(name)
    #colv = []
    #for item in col: colv.append(int(item))
    #print "RGB colv",colv
    #return colv

    i = self.index(name,safe=0)
    if i < 0:
      if name == 'complement':
        col = self.RGBReps.GetColour(name)
        colv = []
        for item in col: colv.append(int(item))
        return colv
      else:
        i = 0
    return self.rgbreps[i][1]

#------------------------------------------------------------------
  def RGB255(self,name):
#------------------------------------------------------------------
    rgb = self.RGB(name)
    return [int(rgb[0]*255.0), int(rgb[1]*255.0),int(rgb[2]*255.0)]
    
#------------------------------------------------------------------
  def hexadecimal(self,name):
#------------------------------------------------------------------
    #col = self.RGBReps.GetColour(name)
    #colv = []
    #for item in col: colv.append(int(item))
    #print "RGB colv",colv
    #return colv
    import math
    conv = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']

    i = self.index(name,safe=0)
    if i < 0:
      if name == 'complement':
        col = self.RGBReps.GetColour(name)
        colv = []
        for item in col: colv.append(int(item))
        return colv
      else:
        i = 0

    ret = ''
    for item in self.rgbreps[i][1][0:3]:
      ff,ii =  math.modf(min(item,0.9961)*16.0)
      ret = ret + conv[int(ii)]
      ff,ii = math.modf(ff*16.0)
      ret = ret + conv[int(ii)]
                   
    return ret

  
#-------------------------------------------------------------------
  def next(self,name,exclude=[]):
#-------------------------------------------------------------------
# Return next colour in list after name
    i = 0
    incr = 1
    if name:
      nm = re.sub('_',' ',name)
      # nb the first item in list is 'default' - skip this
      for rgbrep in self.rgbreps[1:]:
        i = i + 1
        if nm == rgbrep[0]:
          break

    while incr != 0:
      i = i + incr
      incr = 0
      if i >= len(self.rgbreps):
        incr =  1 - i
      elif exclude.count( self.rgbreps[i][0] ) > 0:
        incr = 1

    return  self.rgbreps[i][0]
  

#-------------------------------------------------------------------
  def colourlist(self):
#-------------------------------------------------------------------
# Return a list of the text representations - mostly for
# use in menus
    l = []
    for rgbrep in self.rgbreps: 
      if rgbrep[0] != None:
        l.append(rgbrep[0])
    return l

#--------------------------------------------------------------------
  def colour_menu (self,complement=0,interactive=0):
#--------------------------------------------------------------------
    if complement:
      mm = ['complement']
      nn = ['complement']
    else:
      mm = []
      nn = []
    for rep in self.rgbreps[1:]:
      if rep[0] != None:
        mm.append(rep[0])
        nn.append(re.sub(' ','_',rep[0]))
    if interactive:
      mm.append('Interactive')
      nn.append('interactive')
    #print "colour_menu",mm,nn
    return [mm  ,nn]

#-------------------------------------------------------------------
  def colourkeys(self):
#-------------------------------------------------------------------
# Return a list of the text representations with no space
# characters - replace by underscore
    l = []
    for rgbrep in self.rgbreps: 
      if rgbrep[0] != None:
        l.append(re.sub(' ','_',rgbrep[0]))
    return l



#-------------------------------------------------------------------
  def delete(self,name):
#-------------------------------------------------------------------
# Replace the name of colour definition by None
# This should mean that the colour is no longer used but the
# rgb definition is still there just in case
    i = self.index(name,safe=0)
    if i >= 0:
      rgb = self.rgbreps[i][1]
      self.rgbreps.pop(i)
      self.rgbreps.insert(i,[None,rgb])
      return 1
    else:
      return 0

#-------------------------------------------------------------------
  def clear_custom_colours(self):
#-------------------------------------------------------------------
    for ii in range(self.n_standard_rgbreps,len(self.rgbreps)):
      self.rgbreps[ii][0] = None
      

#-------------------------------------------------------------------
  def rename(self,name,newname):
#-------------------------------------------------------------------
# Give a new name to a colour
    i = self.index(name,safe=0)
    if i >= 0:
      rgb = self.rgbreps[i][1]
      self.rgbreps.pop(i)
      self.rgbreps.insert(i,[newname,rgb])
      return 1
    else:
      return 0

    ### BEWARE - THIS WILL NOT WORK 
    #self.RGBReps.AddColour(name,rgb[0],rgb[1],rgb[2],rgb[3])

#-------------------------------------------------------------------
  def reset(self,name='', newrgb=[]):
#-------------------------------------------------------------------
    if not name:
      for nm,rgb in self.rgbreps:
        # Beware nm may be set to None
        if nm:
          self.RGBReps.AddColour(nm,rgb[0],rgb[1],rgb[2],rgb[3])
    else:
      i = self.index(name,safe=0)
      if i >= 0:
        self.rgbreps.pop(i)
        self.rgbreps.insert(i,[name,newrgb])
        self.RGBReps.AddColour(name,newrgb[0],newrgb[1],newrgb[2],newrgb[3])

#-----------------------------------------------------------------------
  def getparams(self):
#-----------------------------------------------------------------------
    # Remove any cancelled colours which will not have a colour name
    rgbreps = []
    for rep in self.rgbreps:
      if rep[0]: rgbreps.append(rep)
    n_standard_rgbreps = self.n_standard_rgbreps
    return [rgbreps,n_standard_rgbreps]

  def get_standard_colours(self):
    cols = []
    for n in range(0,self.n_standard_rgbreps):
      
      if self.rgbreps[n][0] != 'default':
        col = [self.rgbreps[n][0]]
        for ii in range(0,3):
          col.append(int(self.rgbreps[n][1][ii]*255.0))
        cols.append(col)
    #print "COLOUR.get_standard_colours",cols
    return cols
  
  def get_custom_colours(self):
    cols = []
    for n in range(self.n_standard_rgbreps,len(self.rgbreps)):
      if self.rgbreps[n][0]:
        name = self.rgbreps[n][0]
        col = [self.rgbreps[n][0]]
        for ii in range(0,3):
          col.append(int(self.rgbreps[n][1][ii]*255.0))
        cols.append(col)
    #print "COLOUR.get_custom_colours",cols
    return cols

#--------------------------------------------------------------------
  def save_picture_definition(self,pretty_print,style='method',all=1):
#--------------------------------------------------------------------
    import utils,re
    #print "self.rgbreps",self.rgbreps
    indent=5
    if style == 'method':
      b_end = ')'
      stream = 'Colours ( '
    else:
      b_end = '}'
      stream = self.name + ' = {'

    reps = {}
    for item in self.rgbreps:
      if item[0]: reps[item[0]] = item[1]

    stream=stream+"colour_definitions = {\n"
    for item in self.rgbreps:
      if item[0]:
        stream=stream+"          '"+item[0]+"' : ["
        for ii in item[1]:  stream=stream+ str(ii)+','
        stream=stream[0:-1]+'],\n'
    stream=stream[0:-2] + '  } \n'

  #  stream=stream+utils.picture_defn_item('colour_definitions',reps,pretty_print,style=style)
#    ii = 0
#    for item in self.rgbreps:
#      # Make sure don't save same colour def twice (fouls up reading
#      # the pic def file cos have duplicate argements to initialise method
#      safe = re.sub(' ','_',item[0])
#      if self.rgbreps.index(item)==ii:
#        stream = stream+utils.picture_defn_item(safe,item[1],pretty_print,style=style,indent=indent)
#      ii=ii+1
      
    stream = stream[0:-1]+ '\n      '+b_end+'\n\n'
    return stream

#---------------------------------------------------------------------
  def setparams(self,params=[]):
#---------------------------------------------------------------------
    if len(params)==2:
      rgbreps,n_standard_rgbreps=params
      # Remove any null definitions
      if self.n_standard_rgbreps<=0 : self.n_standard_rgbreps = n_standard_rgbreps
      for name,rgb in rgbreps:
        self.new( name,rgb )
    #print 'COLOUR.setparams',self.rgbreps

#--------------------------------------------------------------------
  def compare_objects(self,initial_status=None,final_status=None):
#--------------------------------------------------------------------
    final = {}
    initial_changed = {}
    final_changed = {}
    for item in final_status[0]:final[item[0]]=item[1]

    for item in initial_status[0]:
      if final.has_key(item[0]) and final[item[0]] != item[1]:
        initial_changed[item[0]]=item[1]
        final_changed[item[0]] = final[item[0]]

    return [initial_changed,final_changed]


#-------------------------------------------------------------------
  def update_data_status(self,status):
#-------------------------------------------------------------------
    #self.setparams(params=status)
    if len(status)==2:
      rgbreps,nrgbreps=status
      for name,rgb in rgbreps:
        #print "COLOUR update_data_status", name,rgb
        self.reset(name=name, newrgb=rgb)
      self.handle_colour_change()
    
#------------------------------------------------------------------
  def movie_interpolation(self,initial_status=None,final_status=None, \
                          fraction=0.0,step='',**keywords):
#------------------------------------------------------------------
    #print "COLOUR",fraction,initial_status,final_status
    for key,value in initial_status.items():
      rgb=[]
      for i in [0,1,2]:
        rgb.append(((1-fraction)*value[i]) + (fraction*final_status[key][i]))
      rgb.append(255)
      #print "COLOUR",key,rgb
      self.reset(name=key, newrgb=rgb)
    self.handle_colour_change()
    
#-------------------------------------------------------------------
  def dump(self,filename=None):
#-------------------------------------------------------------------
    # Remove any cancelled colours which will not have a colour name
    rgbreps = []
    for rep in self.rgbreps:
      if rep[0]: rgbreps.append(rep)
    n_standard_rgbreps = self.n_standard_rgbreps
    
    if not filename:
      import utils
      filename = utils.get_HOME() + "/.CCP4MG/mg_colours.pkl"
    f = open(filename,'w')
    p = pickle.Pickler(f)
    p.dump([rgbreps,n_standard_rgbreps])
    f.close()

#-------------------------------------------------------------------
  def  load(self,filename=None):
#-------------------------------------------------------------------

    if not filename:
      import utils
      filename =  utils.get_HOME() + "/.CCP4MG/mg_colours.pkl"
    if os.path.exists(filename):
      try:
        f = open(filename,'r')
        p = pickle.Unpickler(f)
        self.rgbreps,self.n_standard_rgbreps = p.load()
        f.close()
      except:
        f.close()
        self.load_default()
        print "Error loading colour data from file:", filename
        return 0
    else:
      #print "calling load_default"
      self.load_default()

    #print "COLOUR.load rgbreps",self.rgbreps
    '''
# Remove any null definitions
    i = 0
    maxj = 0
    for rgbrep in self.rgbreps:
      if not rgbrep[0]:
        self.rgbreps.pop(i)
      else:
        i = i +1
        if rgbrep[0][0:5] == 'user_':
          try:
            jj = int(rgbrep[0][5:])
            if jj > maxj: maxj = jj
          except:
            pass
    self.new_name_number = maxj + 1
    '''

#---------------------------------------------------------------------
  def load_old_default(self,filename = None,colour=''):
#---------------------------------------------------------------------
   filename = os.path.join(os.environ["CCP4MG" ],'python','etc','colour_252.py')
   self.load_default(filename,colour)
#---------------------------------------------------------------------
  def load_default(self,filename = None,colour=''):
#---------------------------------------------------------------------
    if not filename:
      filename = os.path.join(os.environ["CCP4MG" ], \
                            'python','etc','colour.py')

    # etc/colour.py defines a list called rgbreps
    globals = {}
    locals= {}
    execfile(filename,globals,locals)
    if colour:
      # Reset specific colour to default
      i = -1
      for rgb in locals['rgbreps']:
        i = i+1
        if colour == rgb[0]:
          self.rgbreps.pop(i)
          self.rgbreps.insert(i,rgb)
          return
    else:
      # Load all colours to rgbreps
      # Do NOT reinititiallise self.rgbreps - this may be for
      # just resetting all 'standard' colours to default
      for colour,defn in locals['rgbreps']:
        ii = self.index(colour,safe=0)
        if ii >= 0:
          self.rgbreps.pop(ii)
          self.rgbreps.insert(ii,[colour,defn])
        else:
          self.rgbreps.append([colour,defn])

      # If the number of standard colours is not known set it now
      if not self.n_standard_rgbreps:
        self.n_standard_rgbreps=len(locals['rgbreps'])
    #print " load_default rgbreps ", self.rgbreps
    


#-------------------------------------------------------------------
  def handle_colour_change(self,name='',rename='',delete=0):
#-------------------------------------------------------------------
    import dataobj
    if delete:
      for obj in dataobj.get_dispobj():
         obj.handle_colour_change(name=name,delete=delete)
    if rename:
      for obj in dataobj.get_dispobj():
         obj.handle_colour_change(name=name,rename=rename)
    #print "handle_colour_change",name
    else:
      standard = 1
      if not name or self.index(name)>=self.n_standard_rgbreps: standard = 0
      for obj in dataobj.get_dispobj():           
        obj.handle_colour_change(name=name,standard=standard)
    return
