#!/usr/bin/env python
# PrintpeaksMosflm.py
#   Copyright (C) 2011 Diamond Light Source, Graeme Winter
#
#   This code is distributed under the BSD license, a copy of which is
#   included in the root directory of this package.
#
# A replacement for the printpeaks tool and wrapper, using Mosflm.
#

import os
import sys
import copy
import math

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
from Diffdump import Diffdump

from Experts.FindImages import image2template_directory, image2image, \
     template_number2image

def PrintpeaksMosflm(DriverType = None):
  '''A factory for wrappers for the printpeaks/mosflm.'''

  DriverInstance = DriverFactory.Driver(DriverType)

  class PrintpeaksMosflmWrapper(DriverInstance.__class__):
    '''Provide access to the spot finding functionality in Mosflm.'''

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

      self.set_executable('ipmosflm')

      if 'CCP4_SCR' in os.environ:
        self.set_working_directory(os.environ['CCP4_SCR'])

      self._image = None
      self._peaks = { }

      return

    def set_image(self, image):
      '''Set an image to read the header of.'''
      self._image = image
      self._peaks = { }
      return

    def get_maxima(self, threshold = 5.0):
      '''Run diffdump, printpeaks to get a list of diffraction maxima
      at their image positions, to allow for further analysis.'''

      if not self._image:
        raise RuntimeError, 'image not set'

      if not os.path.exists(self._image):
        raise RuntimeError, 'image %s does not exist' % \
              self._image

      dd = Diffdump()
      dd.set_image(self._image)
      header = dd.readheader()

      beam = header['raw_beam']
      pixel = header['pixel']

      template, directory = image2template_directory(self._image)
      image_number = image2image(os.path.split(self._image)[-1])

      spot_file = '%s.spt' % template_number2image(
          template, image_number)

      self.start()

      self.input('template "%s"' % template)
      self.input('directory "%s"' % directory)
      self.input('findspots local find %d file %s' % \
                 (image_number, spot_file))
      self.input('go')

      self.close_wait()

      self.check_for_errors()

      output = open(os.path.join(self.get_working_directory(),
                                 spot_file)).readlines()

      os.remove(os.path.join(self.get_working_directory(),
                             spot_file))

      peaks = []

      for record in output[3:-2]:
        lst = record.split()
        x = float(lst[0])
        y = float(lst[1])
        i = float(lst[4]) / float(lst[5])
        x /= pixel[0]
        y /= pixel[1]

        if i < threshold:
          continue

        # this is Mosflm right? Swap X & Y!!

        peaks.append((y, x, i))

      return peaks

    def printpeaks(self):
      '''Run printpeaks and get the list of peaks out, then decompose
      this to a histogram.'''

      if not self._image:
        raise RuntimeError, 'image not set'

      if not os.path.exists(self._image):
        raise RuntimeError, 'image %s does not exist' % \
              self._image

      _peaks = self.get_maxima()
      peaks = []

      for peak in _peaks:
        intensity = peak[2]
        peaks.append(intensity)

      # now construct the histogram

      log_max = int(math.log10(peaks[0])) + 1
      max_limit = int(math.pow(10.0, log_max))

      if False:

        limit = math.pow(10.0, log_max)

        while limit > 2.0:
          self._peaks[limit] = len([j for j in peaks if j > limit])
          limit *= 0.5

      else:

        for limit in [5, 10, 20, 50, 100, 200, 500, 1000]:
          if limit > max_limit:
            continue
          self._peaks[float(limit)] = len(
              [j for j in peaks if j > limit])


      return self._peaks

    def threshold(self, nspots):
      if not self._peaks:
        peaks = self.printpeaks()
      else:
        peaks = self._peaks
      keys = peaks.keys()
      keys.sort()
      keys.reverse()
      for thresh in keys:
        if peaks[thresh] > nspots:
          return thresh
      return min(keys)

    def screen(self):
      if not self._image:
        raise RuntimeError, 'image not set'

      if not os.path.exists(self._image):
        raise RuntimeError, 'image %s does not exist' % \
              self._image

      _peaks = self.get_maxima()

      peaks = []

      for peak in _peaks:
        if peak[2] > 10:
          peaks.append(peak)

      if len(peaks) < 10:
        return 'blank'

      return 'ok'

  return PrintpeaksMosflmWrapper()

if __name__ == '__main-old__':

  import time

  def printer(peaks):
    keys = peaks.keys()
    keys.sort()
    for k in keys:
      print '%.5f %d' % (k, peaks[k])

  directory = os.path.join(os.environ['XIA2_ROOT'],
                           'Data', 'Test', 'Images')

  if len(sys.argv) == 1:
    p = Printpeaks()
    image = os.path.join(directory, '12287_1_E1_001.img')
    p.set_image(image)
    peaks = p.printpeaks()
    printer(peaks)

  else:

    # for image in sys.argv[1:]:
    if 1 == 0:

      print image
      p = Printpeaks()
      p.set_image(image)

      peaks = p.printpeaks()
      printer(peaks)

      thresh = p.threshold(200)
      print '200 peak threshold: %f' % thresh

    t0 = time.time()
    count = 0
    for image in sys.argv[1:]:
      count += 1
      p = Printpeaks()
      p.set_image(image)
      status = p.screen()
      print os.path.split(image)[-1], status
    t1 = time.time()

    print 'Total time: %.1f' % (t1 - t0)
    print 'Per image: %.3f' % ((t1 - t0) / count)

if __name__ == '__main__':
  # run a test of some of the new code...

  p = Printpeaks()
  p.set_image(sys.argv[1])
  peaks = p.get_maxima()

  for m in peaks:
    print '%6.1f %6.1f %6.1f' % m
