#!/usr/bin/env python
# Rebatch.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.
#
# 21/SEP/06
#
# A wrapper for the CCP4 program REBATCH.
#
# FIXME 30/NOV/06 need to add a facility to use this to select batches
#                 from the input reflection file...
#

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'))

from Driver.DriverFactory import DriverFactory
from Decorators.DecoratorFactory import DecoratorFactory

def Rebatch(DriverType = None):
    '''A factory for RebatchWrapper classes.'''

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

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

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

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

            self._first_batch = 0
            self._add_batch = 0

            self._exclude_batches = []

            self._pname = None
            self._xname = None
            self._dname = None

            return

        def set_project_info(self, pname, xname, dname):
            self._pname = pname
            self._xname = xname
            self._dname = dname
            return

        def set_first_batch(self, first_batch):
            self._first_batch = first_batch
            return

        def set_add_batch(self, add_batch):
            self._add_batch = add_batch
            return

        def exclude_batch(self, batch):
            if not batch in self._exclude_batches:
                self._exclude_batches.append(batch)
            return

        def exclude_batches(self):

            self.check_hklin()
            self.check_hklout()

            if not self._exclude_batches:
                raise RuntimeError, 'no batches to exclude'

            self.start()

            for batch in sorted(self._exclude_batches):
                self.input('batch %d reject' % batch)

            self.close_wait()

            # check for errors...
            try:
                self.check_for_errors()
                self.check_ccp4_errors()

            except RuntimeError, e:
                try:
                    os.remove(self.get_hklout())
                except:
                    pass

                raise e

            output = self.get_all_output()

            return

        def limit_batches(self, first, last):
            '''Limit the batches to first to last.'''

            self.check_hklin()
            self.check_hklout()

            if self._first_batch > 0 or self._add_batch > 0:
                raise RuntimeError, 'limiting when batches set to renumber'

            self.start()

            if first - 1 > 0:
                self.input('batch 0 to %d reject' % (first - 1))
            self.input('batch %d to 1000000 reject' % (last + 1))

            self.close_wait()

            # check for errors...
            try:
                self.check_for_errors()
                self.check_ccp4_errors()

            except RuntimeError, e:
                try:
                    os.remove(self.get_hklout())
                except:
                    pass

                raise e

            # get out the new batch range...

            output = self.get_all_output()

            min = 10000000
            max = -10000000

            for i in range(len(output)):
                o = output[i]
                if o.split()[:5] == ['Old', 'batch', 'New', 'batch', 'Max']:
                    j = i + 2
                    m = output[j]
                    while not 'SUMMARY_END' in m:
                        l = m.split()
                        if len(l) == 3:
                            batch = int(l[1])
                            if batch < min:
                                min = batch
                            if batch > max:
                                max = batch
                        j += 1
                        m = output[j]

            new_batches = (min, max)

            return new_batches

        def rebatch(self):
            self.check_hklin()
            self.check_hklout()

            if self._first_batch > 0 and self._add_batch > 0:
                raise RuntimeError, 'both first and add specified'

            if self._first_batch == 0 and self._add_batch == 0:
                raise RuntimeError, 'neither first nor add specified'

            self.start()

            if self._first_batch > 0:
                command = 'batch all start %d' % self._first_batch
            else:
                command = 'batch all add %d' % self._add_batch

            self.input(command)

            if self._pname and self._xname and self._dname:
                command = 'batch all pname %s xname %s dname %s' % \
                          (self._pname, self._xname, self._dname)
                self.input(command)

            self.close_wait()

            # check for errors...
            try:
                self.check_for_errors()
                self.check_ccp4_errors()

            except RuntimeError, e:
                try:
                    os.remove(self.get_hklout())
                except:
                    pass

                raise e

            # get out the new batch range...

            output = self.get_all_output()

            min = 10000000
            max = -10000000

            for i in range(len(output)):
                o = output[i]
                if o.split()[:5] == ['Old', 'batch', 'New', 'batch', 'Max']:
                    j = i + 2
                    m = output[j]
                    while not 'SUMMARY_END' in m:
                        l = m.split()
                        if len(l) >= 3:
                            batch = int(l[1])
                            if batch < min:
                                min = batch
                            if batch > max:
                                max = batch
                        j += 1
                        m = output[j]

            new_batches = (min, max)

            return new_batches

    return RebatchWrapper()

if __name__ == '__main__':
    # add a test here

    hklin = os.path.join(os.environ['XIA2_ROOT'],
                         'Data', 'Test', 'Mtz', '12287_1_E1_1_10.mtz')
    hklout = 'temp.mtz'

    rb = Rebatch()

    rb.set_hklin(hklin)
    rb.set_hklout(hklout)
    rb.set_first_batch(100)

    new = rb.rebatch()

    print new
