#!/usr/bin/env python
# Sortmtz.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.
#
# 5th June 2006
#
# A wrapper for the CCP4 program sortmtz.
#
#
# Provides:
#
# Reflection sorting functionality for going from Mosflm to Scala.
#

import os
import sys

if not os.environ.has_key('XIA2CORE_ROOT'):
  raise RuntimeError, 'XIA2CORE_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
from Decorators.DecoratorFactory import DecoratorFactory
from Handlers.Streams import Chatter

def Sortmtz(DriverType = None):
  '''A factory for SortmtzWrapper classes.'''

  DriverInstance = DriverFactory.Driver(DriverType)
  CCP4DriverInstance = DecoratorFactory.Decorate(DriverInstance, 'ccp4')

  class SortmtzWrapper(CCP4DriverInstance.__class__):
    '''A wrapper for Sortmtz, using the CCP4-ified Driver.'''

    def __init__(self):
      # generic things
      CCP4DriverInstance.__class__.__init__(self)

      self.set_executable(os.path.join(
          os.environ.get('CBIN', ''), 'sortmtz'))

      self._sort_order = 'H K L M/ISYM BATCH'

      self._hklin_files = []

      return

    def add_hklin(self, hklin):
      '''Add a reflection file to the list to be sorted together.'''
      self._hklin_files.append(hklin)
      return

    def check_sortmtz_errors(self):
      '''Check the output for "standard" errors.'''

      lwbat_warning = ''

      for l in self.get_all_output():

        if 'From ccp4_lwbat: warning:' in l:
          lwbat_warning = l.split('warning:')[1].strip()

        if 'error in ccp4_lwbat' in l:
          raise RuntimeError, lwbat_warning

        if 'Sorting failed' in l:
          raise RuntimeError, 'sorting failed'

        if 'Inconsistent operator orders in input file' in l:
          raise RuntimeError, 'different sort orders'

    def sort(self, vrset = None):
      '''Actually sort the reflections.'''

      # if we have not specified > 1 hklin file via the add method,
      # check that the set_hklin method has been used.
      if not self._hklin_files:
        self.check_hklin()

      self.check_hklout()

      if self._hklin_files:
        task = ''
        for hklin in self._hklin_files:
          task += ' %s' % hklin
        self.set_task('Sorting reflections%s => %s' % \
                     (task,
                      os.path.split(self.get_hklout())[-1]))
      else:
        self.set_task('Sorting reflections %s => %s' % \
                     (os.path.split(self.get_hklin())[-1],
                      os.path.split(self.get_hklout())[-1]))

      self.start()

      # allow for the fact that large negative reflections may
      # result from XDS output...

      if vrset:
        self.input('VRSET_MAGIC %f' % vrset)

      self.input(self._sort_order)

      # multiple mtz files get passed in on the command line...

      if self._hklin_files:
        for m in self._hklin_files:
          # FIXME have removed the quotes as this breaks the parser
          # it is assumed that the file is a comment! This is not
          # a problem in 6.0.2 - so allow for the quotes to
          # be added if and only if there is a space...
          if ' ' in m:
            Chatter.write(
                'Quoting input files - you have been warned!')
            self.input('"%s"' % m)
          else:
            self.input('%s' % m)

      self.close_wait()

      try:

        # general errors - SEGV and the like
        self.check_for_errors()

        # ccp4 specific errors
        self.check_ccp4_errors()
        if 'Error' in self.get_ccp4_status():
          raise RuntimeError, '[SORTMTZ] %s' % status

        # sortmtz specific errors
        self.check_sortmtz_errors()

      except RuntimeError, e:
        # something went wrong; remove the output file
        try:
          os.remove(self.get_hklout())
        except:
          pass
        raise e

      return self.get_ccp4_status()

  return SortmtzWrapper()

if __name__ == '__main_2_':
  # run some tests

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

  dpa = os.environ['XIA2_ROOT']

  hklin1 = os.path.join(dpa,
                        'Data', 'Test', 'Mtz', '12287_1_E1_1_10.mtz')
  hklin2 = os.path.join(dpa,
                        'Data', 'Test', 'Mtz', '12287_1_E1_11_20.mtz')

  s = Sortmtz()
  s.add_hklin(hklin1)
  s.add_hklin(hklin2)
  s.set_hklout('null.mtz')

  try:
    print s.sort()
  except RuntimeError, e:
    print 'Error => %s' % e

  s = Sortmtz()
  s.add_hklin(hklin1)
  s.add_hklin(hklin1)
  s.set_hklout('null.mtz')

  try:
    print s.sort()
  except RuntimeError, e:
    print 'Error => %s' % e

if __name__ == '__main__':
  s = Sortmtz()
  s.add_hklin('TS00_13185_unmerged_INFL.mtz')
  s.set_hklout('TS00_13185_sorted_INFL.mtz')
  s.sort()
