from __future__ import absolute_import
import traceback


import diffcalc
from diffcalc import settings
from diffcalc.hkl.you.geometry import SixCircle
from diffcalc.hardware import HardwareAdapter
from diffcalc.ub.persistence import UbCalculationNonPersister
from diffcalc.gdasupport.minigda.scannable import *
from diffcalc.gdasupport.minigda import command

from diffcalc.hardware import HardwareAdapter


#import diffcalc.util  # @UnusedImport
#
#
# Disable error handling designed for interactive use
#diffcalc.util.DEBUG = True


### Create dummy scannables ###

Dummy = SingleFieldDummyScannable


class PositionerScannable(ScannableBase):

    def __init__(self, positioner):
        self.positioner = positioner
        self.name = positioner.name
        self.inputNames = [self.name]
        self.outputFormat = ['% 6.4f']
        self.level = 3

    def isBusy(self):
        return self.positioner.state == State.Busy

    def waitWhileBusy(self):
        self.positioner.waitReady(-1)

    def asynchronousMoveTo(self, new_position):
        #print "Moving " , self.name, " to: ", new_position 
        self.positioner.moveAsync(float(new_position), -1)

    def getPosition(self):
        return self.positioner.getPosition()


class PositionerScannableGroup(ScannableGroup):
    def __init__(self, name, motors):
        self.name = name
        [mu, delta, gam, eta, chi, phi] = motors
        self.mu = PositionerScannable(mu)
        self.delta = PositionerScannable(delta)
        self.gam = PositionerScannable(gam)
        self.eta = PositionerScannable(eta)
        self.chi = PositionerScannable(chi)
        self.phi = PositionerScannable(phi)
        ScannableGroup.__init__(self, self.name, [self.mu, self.delta, self.gam, self.eta, self.chi, self.phi])

class MotorGroupScannable(PositionerScannableGroup):        
    def __init__(self, motor_group):
        self.motor_group = motor_group
        PositionerScannableGroup.__init__(self, motor_group.name, motor_group.motors)
      



class ScannableAdapter(HardwareAdapter):
    def __init__(self, diffractometer, energy, energy_multiplier_to_kev=1):
        self.diffractometer = diffractometer
        self.energy = energy
        self.energy_multiplier_to_kev = energy_multiplier_to_kev    
        input_names = diffractometer.getInputNames()
        HardwareAdapter.__init__(self, input_names)

    #Returns the current physical POSITIONS
    def get_position(self):
        """
        pos = getDiffractometerPosition() -- returns the current physical
        diffractometer position as a list in degrees
        """
        return self.diffractometer.getPosition()

    #returns energy in 
    def get_energy(self):
        """energy = get_energy() -- returns energy in kEv (NOT eV!) """
        multiplier = self.energy_multiplier_to_kev
        energy = self.energy.getPosition() * multiplier
        if energy is None:
            raise DiffcalcException("Energy has not been set")
        return energy

    @property
    def name(self):
        return self.diffractometer.getName()

class MotorGroupAdapter(ScannableAdapter):
    def __init__(self, diffractometer, energy, energy_multiplier_to_kev=1):
        self.sixc = MotorGroupScannable(sixc)
        self.en = PositionerScannable(en)
        self.en.level = 3    
        ScannableAdapter.__init__(self, self.sixc, self.en, energy_multiplier_to_kev)

        
en.move(20.0)
delta.move(1.0)

you = None
settings.hardware = MotorGroupAdapter(sixc, en)
settings.geometry = SixCircle()
settings.ubcalc_persister = UbCalculationNonPersister()
settings.axes_scannable_group = settings.hardware.sixc
settings.energy_scannable = settings.hardware.en
settings.ubcalc_strategy = diffcalc.hkl.you.calc.YouUbCalcStrategy()
settings.angles_to_hkl_function = diffcalc.hkl.you.calc.youAnglesToHkl
from diffcalc.gdasupport import you
reload(you)

#settings.hardware = DummyHardwareAdapter(('mu', 'delta', 'gam', 'eta', 'chi', 'phi'))


# These must be imported AFTER the settings have been configured
from diffcalc.dc import dcyou as dc
from diffcalc.ub import ub
from diffcalc import hardware
from diffcalc.hkl.you import hkl



_wl = you.wl
_hkl= you.hkl



# Set some limits
hardware.setmin('gam', 0) #-179)
hardware.setmax('gam', 179)

hardware.setmin('delta', 0) 
hardware.setmax('delta', 179)


def demo_orient():

    ub.listub()
    
    # Create a new ub calculation and set lattice parameters
    ub.newub('test')
    ub.setlat('cubic', 1, 1, 1, 90, 90, 90)
    
    # Add 1st reflection (demonstrating the hardware adapter)
    settings.hardware.wavelength = 1
    ub.c2th([1, 0, 0])                 # energy from hardware
    settings.hardware.position = 0, 60, 0, 30, 0, 0
    ub.addref([1, 0, 0])# energy and position from hardware
    
    # Add 2nd reflection (this time without the harware adapter)
    ub.c2th([0, 1, 0], 12.39842)
    ub.addref([0, 1, 0], [0, 60, 0, 30, 0, 90], 12.39842)
    
    # check the state
    ub.ub()
    ub.checkub()


def demo_motion():
    dc.angles_to_hkl((0., 60., 0., 30., 0., 0.))
    hkl.con('qaz', 90)
    hkl.con('a_eq_b')
    hkl.con('mu', 0)
    
    hardware.setmin('delta', 0)
    hardware.setcut('phi', -180.0)
    dc.hkl_to_angles(1, 0, 0)
    hardware.hardware()

# These demos reproduce the outline in the developer guide
def demo_all():
    demo_orient()
    demo_motion()


try:
    demo_all()
except:
    traceback.print_exc()
    


pos = command.Pos()
scan = command.Scan(command.ScanDataPrinter())    
sim=command.sim

help(ub.ub)
help(hkl.con)


#pos(settings.hardware.sixc, [0, 60, 0, 30, 90, 0])
pos(settings.hardware.sixc)
pos(_wl, 1)
pos(_wl)



ub.lastub()
ub.setu ([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
ub.showref()
ub.swapref(1,2)


print "---"
#print you.hkl
pos(_hkl)
#you.hkl.simulateMoveTo([0,1,1])
sim(_hkl, [0,1,1])
pos(_hkl, [0,1,1])


#--------------------------------------------------------------------------------------------------

import ch.psi.pshell.device.PositionerConfig as PositionerConfig

import ch.psi.pshell.device.RegisterConfig as RegisterConfig
import ch.psi.pshell.device.Register as Register

class HklPositoner (PositionerBase):
    def __init__(self, name, index, hkl_group):
         PositionerBase.__init__(self, name, PositionerConfig())
         self.setParent(hkl_group)
         self.index = index
                  
    def doRead(self):
        return self.getParent()._setpoint[self.index]

    def doWrite(self, value): 
        print "Setting " , self.getName(), "to: ", value
        pos = [None, None, None]
        pos[self.index] = value
        self.getParent().write(pos)
        
    def doReadReadback(self):
        if java.lang.Thread.currentThread() != self.getParent()._updating_thread:
             self.getParent().update()
        return self.getParent()._readback[self.index]

class HklGroup(RegisterBase, Register.RegisterArray):
    def __init__(self):        
        RegisterBase.__init__(self, "hkl", RegisterConfig())
        self.h, self.k, self.l = HklPositoner("h", 0, self), HklPositoner("k", 1, self), HklPositoner("l", 2, self)
        add_device(self.h, True)
        add_device(self.k, True)
        add_device(self.l, True)           
        self._setpoint = self.doRead()
        self._updating = False
        
    def getSize(self):
        return 3
                
    def doRead(self):      
        self._readback = _hkl.getPosition()  
        self._updating_thread = java.lang.Thread.currentThread()
        try:
            self.h.update()
            self.k.update()
            self.l.update()
        except:
            self._updating_thread = None
            print "!!!"
            traceback.print_exc()
            print "---"
        return self._readback 

    def doWrite(self, pos):
        self._setpoint = pos
        print "Moving to: " + str(pos)
        _hkl.asynchronousMoveTo(pos) 


add_device(HklGroup(), True)






#h.polling = 250; k.polling = 250; l.polling = 250; 
hkl.polling = 250



def hklscan(vector, readables,latency = 0.0,  passes = 1, **pars):
    """
    HKL Scan: 

    Args:
        vector(list of lists): HKL values to be scanned
        readables(list of Readable): Sensors to be sampled on each step.
        latency(float, optional): settling time for each step before readout, defaults to 0.0.
        passes(int, optional): number of passes
        pars(keyworded variable length arguments, optional): scan optional named arguments:
            - title(str, optional): plotting window name.     
            - hidden(bool, optional): if true generates no effects on user interface.     
            - before_read (function, optional): callback on each step, before sampling. Arguments: positions, scan
            - after_read (function, optional): callback on each step, after sampling. Arguments: record, scan.
            - before_pass (function, optional): callback before each scan pass execution. Arguments: pass_num, scan.
            - after_pass (function, optional): callback after each scan pass execution. Arguments: pass_num, scan.
            - Aditional arguments defined by set_exec_pars.
    Returns:
        ScanResult object.

    """
    print "1"
    readables=to_list(string_to_obj(readables))
    print "2"
    pars["initial_move"]  = False
    #names = [readable.name for readable in readables]
    #print "3"
    #scan = ManualScan([h, k, l], readables, dimensions = 1)
    #scan = ManualScan(["h", "k", "l"], names ,vector[0], vector[-1], [len(vector)-1] * 3, dimensions = 1)
    scan = ManualScan([h,k,l], readables ,vector[0], vector[-1], [len(vector)-1] * 3, dimensions = 1)
    print "4"
    if not "domain_axis" in pars.keys(): 
        pars["domain_axis"] = "Index"
    print "5"
    processScanPars(scan, pars)
    print "6"
    scan.start()
    try:
        for pos in vector:
            print "Writing ", pos
            hkl.write(pos)
            print "Done"
            time.sleep(1.0)
            scan.append ([h.take(), k.take(), l.take()], [h.getPosition(), k.getPosition(), l.getPosition()], [readable.read() for readable in readables ])
    finally:
        scan.end()    
    return scan.result


#lscan(l, [sin], 1.0, 1.5, 0.1)

#vector = [[1.0,1.0,1.0], [1.0,1.0,1.1], [1.0,1.0,1.2], [1.0,1.0,1.4]]
#hklscan(vector, [sin, arr], 0.9) #,  = "Index" )#, line_plots = [sin])