from __future__ import absolute_import, division, print_function
import copy
from iotbx.detectors.adsc import ADSCImage
from scitbx.array_family import flex
from iotbx.detectors import image_divider

def ADSC_module_from_file_url(url):
  #backward compatibility with Python 2.5
  try: from urlparse import parse_qs
  except Exception: from cgi import parse_qs

  from urlparse import urlparse
  parsed = urlparse(url)
  assert parsed.scheme == "file"
  file = parsed.path.split("?")[0]
  if file == parsed.path:
    return ADSCImage(file)
  qs = parse_qs(parsed.path.split("?")[1])
  sliceindex = int(qs["slice"][0])
  object = ADSCImage(file)
  object.readHeader()
  return ADSC_module_from_object_and_slicenumber(object,sliceindex)

def ADSC_module_from_object_and_slicenumber(object,moduleindex):
  P = ADSCModule()
  P.moduleindex = moduleindex
  P.object = object
  P.copy_common_attributes_from_parent_instance(P.object)
  P.vendortype = "ADSC module"
  P.parameters = P.module_parameters(P.object)
  P.slice_callback = P.slice_callback_with_object_data
  return P

class ADSCModule(ADSCImage):
  def __init__(self):
    self.already_read_data = False

  def module_parameters(self,object):
    param = object.parameters
    #unchanged parameters first:
    result = {}
    for item in ['DISTANCE', 'PHI', 'WAVELENGTH',
    'TWOTHETA', 'OSC_RANGE',
    'CCD_IMAGE_SATURATION', 'OSC_START', 'DETECTOR_SN', 'PIXEL_SIZE',
    ]:
      result[item]=copy.copy(param[item])

    #other parameters keep a record of parent settings; but child
    # settings are deduced on-the-fly once the data are read.
    for item in ['SIZE1','SIZE2','BEAM_CENTER_X','BEAM_CENTER_Y',
    ]:
      result["PARENT_"+item]=copy.copy(param[item])

    #the final parameters require a knowledge of the module boundaries.
    # In this implementation, these boundaries are determined on the fly
    # from the raw data, implying an up-front file read.
    # The only way to avoid this (not implemented here) is to encode
    # the vendor- & model-specific module boundaries.

    object.read()
    nullvalue = vendor_specific_null_value(object)
    ID = image_divider(data = object.linearintdata, nullvalue=nullvalue)
    print("module slow interval",ID.tile_slow_interval(self.moduleindex).first, ID.tile_slow_interval(self.moduleindex).last)
    print("module fast interval",ID.tile_fast_interval(self.moduleindex).first, ID.tile_fast_interval(self.moduleindex).last)
    result["SIZE1"] = ID.tile_slow_interval(self.moduleindex).size()
    result["SIZE2"] = ID.tile_fast_interval(self.moduleindex).size()
    print("size1",result["SIZE1"],"size2",result["SIZE2"])
    return result

  def set_beam_center_convention(self,beam_center_convention):

    #previously part of the module_parameters function
    #from iotbx.detectors.context.config_detector import beam_center_convention_from_image_object
    #beam_center_convention = beam_center_convention_from_image_object(object)
    print("CC",beam_center_convention)
    assert self.object.beam_center_reference_frame == "instrument"
    nullvalue = self.object.vendor_specific_null_value
    ID = image_divider(data = self.object.linearintdata, nullvalue=nullvalue)
    from iotbx.detectors.beam_center_convention import convert_beam_instrument_to_module
    self.parameters['BEAM_CENTER_X'],self.parameters['BEAM_CENTER_Y'] = convert_beam_instrument_to_module(
      self.object, ID, self.moduleindex, beam_center_convention)
    self.beam_center_reference_frame = "imageblock"
    self.beam_center_convention = beam_center_convention
    print("old beam center",self.object.parameters['BEAM_CENTER_X'],self.object.parameters['BEAM_CENTER_Y'])
    print("for module",   self.moduleindex,"beam x,y is", self.parameters['BEAM_CENTER_X'],self.parameters['BEAM_CENTER_Y'])


  def slice_callback_with_high_performance_http_data(self):
    BYU = self.object.read()
    linearintdata = flex.int_from_byte_str(BYU)
    provisional_size = linearintdata.size()
    assert provisional_size == self.size1*self.size2
    print("size assertion OK",provisional_size)
    linearintdata.reshape(flex.grid((self.size1,self.size2)))
    del self.object #once the data are copied, close the stream
    return linearintdata

  def slice_callback_with_object_data(self):
    self.object.read()
    nullvalue = self.object.vendor_specific_null_value
    ID = image_divider(data = self.object.linearintdata, nullvalue=nullvalue)
    assert 0 <= self.moduleindex < ID.module_count()
    new_data_array = ID.tile_data(self.moduleindex)
    if self.object.linearintdata.__dict__.get("bin2by2")==True:
      new_data_array.bin2by2=True
    del self.object #once the data are copied, no need to keep the original
    return new_data_array

  def read(self):
    if self.already_read_data: return
    self.bin_safe_set_data(self.slice_callback())
    self.already_read_data = True

if __name__=='__main__':
  import sys
  sliceidx=4
  full_path_to_file = sys.argv[1]
  a = ADSCImage(full_path_to_file)
  a.read()
  print(a)
  print(a.parameters)
  print(a.rawdata, len(a.rawdata), a.size1*a.size2)
  for dataitem in ['bin', 'filename', 'header', 'headerlines', 'linearintdata', 'parameters', 'vendortype']:
    print(dataitem, end=' ')
    exec("print a.%s"%dataitem)
  print(ADSC_module_from_object_and_slicenumber(a,sliceidx))

  P = ADSC_module_from_file_url(url="file://%s?slice=%d"%(full_path_to_file,sliceidx))
  print("file://%s?slice=%d"%(full_path_to_file,sliceidx))
  print(P)
