#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''PolTest provides developer methods to interact with the polarimeter.

PolTest can do separate and repetitive measurements and rotation and also call
 the high level methods for zeroing and analysis. It is useful for
calibration at and after hardware assembly, e.g. to position motor and light and to 
assess device properties, which may impact the search algorithm for analysis.
For this it provides the option for recording data to a file in csv format.
Developping info can be turned off, also. 

@author: guido lutterbach
@contact: guido@smartypies.com
@version: 1.0
@copyright: guido lutterbach, July 2018
'''

import cmd, time, threading, logging
from Polarimeter import Polarimeter, Measurement


class PolTest(cmd.Cmd):
    polmeter = Polarimeter()

    intro = '''usage: type following commands
            l       - toggle light
            s       - stop running loop (measure, rotation)
            r [n [m]] - rotate with stepwidth n (pos/neg/0) m times 
            o/a/m [f] - one/auto/multiple measurements, latter with
                        f seconds break, default 0.1
            z       - set zero /calibrate polarimeter
            ana [r l] - find delta angle, calculate concentration when specific rotation and length are given 
            max     - find max
            rec [name]|off - record measurement data to file name or stop it
            tune sc mw rw - type help tune for more info
            q (quit)'''
    prompt = '>>'
    file = None

    __runthread = None
    __loop = False
    __logfile = None
    __stepwidth = 0  # 0, -n, +n
    __automeasure = False
    __lighton = False

    def do_s(self, arg):
        '''Stop any loop.'''
        self.__loop = False
        if self.__runthread:
            self.__runthread.join()
            self.__runthread = None

    def do_l(self, arg):
        '''Toggle light.'''
        self.__lighton = not self.__lighton
        if self.__lighton:
            self.polmeter.lighton()
        else:
            self.polmeter.lightoff()
        print('light on:' + str(self.__lighton))

    def do_r(self, arg):
        '''Rotate with step width n (+/-/0) m times.'''
        larg = tuple(map(int, arg.split()))
        if len(larg) == 0:
            self.__stepwidth = 1
        else:
            self.__stepwidth = larg[0]
        mtimes = 1
        if len(larg) >= 2:
            mtimes = larg[1]
        # put action into thread when m > 1
        if mtimes > 1:
            if self.__runthread == None:
                self.__loop = True
                ar = ('repetition=', mtimes)
                self.__runthread = threading.Thread(
                    target=self.__runner, args=ar, daemon=None)
                self.__runthread.start()
            else:
                print('please stop other loop with ' 's' ' first.')
        else:
            self.__action()

    def do_a(self, arg):
        '''Toggle auto measure.'''
        self.__automeasure = not self.__automeasure
        print('auto measure on:' + str(self.__automeasure))

    def do_o(self, arg):
        ''' Do one measurement.'''
        self.__measure()

    def do_m(self, arg):
        '''Do multiple measurements with [f] seconds break.'''
        if self.__runthread == None:
            # turn automatic measure on
            self.__automeasure = True
            # turn rotation off
            self.__stepwidth = 0
            self.__loop = True
            if len(arg) == 0:
                arg = '0.1'
            ar = [float(arg)]
            self.__runthread = threading.Thread(
                target=self.__runner, args=ar, daemon=None)
            self.__runthread.start()
        else:
            print('please stop other loop with ' 's' ' first.')

    def do_z(self, arg):
        '''Reset position and angle.'''
        self.polmeter.setzero()

    def do_rec(self, arg):
        '''Record measurements to file.'''
        if arg == 'off':
            if self.__logfile is not None:
                print('Close record file: ', self.__logfile.name)
                self.__logfile.close()
                self.__logfile = None
            else:
                print('No record file open')
        else:
            if self.__logfile is not None:
                print('Close record file: ', self.__logfile.name)
                self.__logfile.close()
                self.__logfile = None

            if len(arg) == 0:
                now = time.localtime()
                fname = "{:4d}{:02d}{:02d}{:02d}{:02d}{:02d}.csv".format(
                    now.tm_year, now.tm_mon, now.tm_mday, now.tm_hour,
                    now.tm_min, now.tm_sec)
                self.__logfile = open(fname, 'w')
            else:
                self.__logfile = open(arg, 'w')

            print('Open record file: ', self.__logfile.name)

    def do_max(self, arg):
        '''Aqcuire data points and run polynomial (2nd order) regression starting from current position.
        
        Make sure that you are positioned well before the peak.'''
        print(self.polmeter.findmax())

    def do_tune(self, arg):
        '''Tune requires 3 arguments int, float,float:
        
        tune samplingcount measurewait movewait
        used for fine tuning device parameters, not for regular use.
        argument values are persisted in file 'tuning.txt'.
        '''
        if self.__loop:
            print('please stop loop first with "s"')
            return
        try:
            largs = tuple(map(float, arg.split()))
            if len(largs) != 3:
                raise Exception('please call with exactly 3 arguments')
            self.polmeter.tune(int(largs[0]), largs[1], largs[2])
            with open('tuning.txt', 'w') as f:
                f.write(arg)
        except Exception as ex:
            print(ex.args)

    def do_q(self, arg):
        '''Quit.'''
        if self.__runthread:
            self.__loop = False
            self.__runthread.join()
        if self.__logfile:
            self.__logfile.close()
        return True

    def do_ana(self, arg):
        '''Run the analysis.'''
        larg = tuple(map(float, arg.split()))
        angl = float(self.polmeter.analyze())
        valstring = '{:.2f}'.format(angl)
        if len(larg) > 0:
            fac = 1.0
            if len(larg) >= 2:
                fac = 100.0 / (larg[0] * larg[1])
            elif len(larg) == 1:
                fac = 100.0 / larg[0]
            conc = angl * fac
            valstring+=', {:.2f}'.format(conc)
            
        valstring+=', {}'.format(time.asctime())
        print(valstring)
        if self.__logfile:
            self.__logfile.write(valstring + '\n')

    def default(self, line):
        print('undefined key')

    def __runner(self, sleeptime=0, repetition=0):
        '''Function to run in thread.'''
        cnt = 0
        while self.__loop:
            self.__action()
            if repetition > 0:
                cnt += 1
                if cnt == repetition:
                    self.__loop = False
                    self.__runthread = None
                    break
            else:
                time.sleep(sleeptime)

    def __action(self):
        '''Rotate and measure according to set variables'''
        if self.__stepwidth != 0:
            # which direction
            if self.__stepwidth > 0:
                target = self.polmeter.forward
            elif self.__stepwidth < 0:
                target = self.polmeter.backward
            for i in range(0, abs(self.__stepwidth)):
                target()
        if self.__automeasure:
            self.__measure()

    def __measure(self):
        '''retrieve raw data from device, format and log here'''
        val = self.polmeter.measure()
        #valstring = ",".join("{} {} {}".format(val.position, val.value, val.time))
        valstring = '{}, {}, {}'.format(val.position, val.value, val.time)
        print(valstring)
        if self.__logfile:
            self.__logfile.write(valstring + '\n')


if __name__ == "__main__":
    try:
        logging.basicConfig(
            level=logging.INFO,
            #format='%(name)-12s: %(levelname)-8s %(message)s')
            format='%(message)s')
        logger = logging.getLogger('Polarimeter')
        PT = PolTest()
        PT.cmdloop()

    finally:
        PT.polmeter.shutdown()
