#!/usr/bin/env python
# Chooch.py
#   Copyright (C) 2006 CCLRC, Graeme Winter
#
#   This code is distributed under the BSD license, a copy of which is
#   included in the root directory of this package.
#
# A wrapper for Chooch, for use in deciding what has happened during
# collection of experimental phasing data, and also for helping with
# MAD experiments.
#
# 18th December 2006
#
#

import os
import sys
import math
import string
import exceptions

if not os.environ.has_key('XIA2CORE_ROOT'):
  raise RuntimeError, 'XIA2CORE_ROOT not defined'

if not os.environ.has_key('XIA2_ROOT'):
  raise RuntimeError, 'XIA2_ROOT not defined'

if not os.path.join(os.environ['XIA2CORE_ROOT'],
                    'Python') in sys.path:
  sys.path.append(os.path.join(os.environ['XIA2CORE_ROOT'],
                               'Python'))

if not os.environ['XIA2_ROOT'] in sys.path:
  sys.path.append(os.environ['XIA2_ROOT'])

from Driver.DriverFactory import DriverFactory

# helper functions

def energy_to_wavelength(energy):
  h = 6.6260693e-34
  c = 2.9979246e8
  e = 1.6021765e-19

  return 1.0e10 * (h * c) / (e * energy)

def preprocess_scan(scan_file):
  '''Preprocess the scan file to a form that chooch will accept.'''

  try:
    i = int(open(scan_file, 'r').readlines()[1])
    return scan_file
  except:
    # assume that this is not in the friendly format...
    data = open(scan_file, 'r').readlines()
    more_data = []
    for d in data:
      if not '#' in d and d.strip():
        more_data.append(d)
    data = more_data
    count = len(data)
    out = open('xia2-chooch.raw', 'w')
    out.write('Chooch Scan File from xia2\n%d\n' % count)
    for d in data:
      out.write('%f %f\n' % tuple(map(float, d.split(',')[:2])))
    out.close()
    return 'xia2-chooch.raw'

def Chooch(DriverType = None):
  '''Factory for Chooch wrapper classes, with the specified
  Driver type.'''

  DriverInstance = DriverFactory.Driver(DriverType)

  class ChoochWrapper(DriverInstance.__class__):
    def __init__(self):

      DriverInstance.__class__.__init__(self)

      self.set_executable('chooch')
      self._scan = None
      self._edge_table = { }

      self._atom = 'se'

      self._data = []

    def set_scan(self, scan):
      '''Set a scan file for chooch to parse.'''

      self._scan = preprocess_scan(scan)
      return

    def set_atom(self, atom):
      '''Set the atom which should be in the scan.'''

      self._atom = atom
      return

    def scan(self):
      '''Run chooch.'''

      self.add_command_line('-e')
      self.add_command_line(self._atom)
      self.add_command_line(self._scan)

      self.start()
      self.close_wait()

      self.check_for_errors()

      self._data = []
      for o in open(os.path.join(self.get_working_directory(),
                                 'output.efs'), 'r').readlines():
        self._data.append(map(float, o.split()))

      output = self.get_all_output()
      collect = False

      for o in output:
        if collect:

          if '-------' in o:
            collect = False
            continue

          name, energy, fp, fpp = tuple(map(string.strip,
                                            o.split('|')[1:5]))
          self._edge_table[name] = {
              'energy':float(energy),
              'fp':float(fp),
              'fpp':float(fpp),
              'wave':energy_to_wavelength(float(energy))}

        if 'energy' in o and 'f\'' in o and 'f\'\'' in o:
          collect = True

    def get_edges(self):
      return self._edge_table

    def get_fp_fpp(self, wave):
      '''Get the fp, fpp for a wavelength.'''

      if not self._data:
        raise RuntimeError, 'data array empty'

      fp = 0.0
      fpp = 0.0
      closest = 1.0e100

      for d in self._data:
        w = energy_to_wavelength(d[0])
        if math.fabs(w - wave) < closest:
          fp = d[2]
          fpp = d[1]
          closest = math.fabs(w - wave)

      return fp, fpp

    def id_wavelength(self, wave):
      '''Try to identify a wavelength.'''

      min_wave_diff = 1.0e100
      name = None

      waves = []

      for edge in self._edge_table.keys():
        waves.append(self._edge_table[edge]['wave'])
        if math.fabs(self._edge_table[edge]['wave'] - wave) < \
           min_wave_diff:
          min_wave_diff = math.fabs(self._edge_table[edge]['wave'] -
                                    wave)
          name = edge

      if min_wave_diff > 0.01:
        # assume that this is a remote...
        if wave > max(waves):
          return 'LREM'
        else:
          return 'HREM'

      return name.upper()

  return ChoochWrapper()

if __name__ == '__main__':

  if len(sys.argv) < 2:

    c = Chooch()
    c.set_scan(os.path.join(os.environ['XIA2_ROOT'], 'Data',
                            'Test', 'Scans',
                            'TM0486-9172-Se.raw'))
    c.set_atom('se')
    c.scan()

    edges = c.get_edges()

    for key in edges.keys():
      print '%s %6.2f %6.2f %8.6f' % (key,
                                      edges[key]['fp'],
                                      edges[key]['fpp'],
                                      edges[key]['wave'])

  else:
    for scan in sys.argv[1:]:

      print os.path.split(scan)[-1]

      try:

        c = Chooch()
        c.set_scan(scan)
        c.set_atom('se')
        c.scan()

        edges = c.get_edges()

        for key in edges.keys():
          print '%s %6.2f %6.2f %8.6f' % (key,
                                          edges[key]['fp'],
                                          edges[key]['fpp'],
                                          edges[key]['wave'])
      except exceptions.Exception, e:
        print 'failed (%s)' % str(e)


      print ''
