#!/usr/bin/env python

from __future__ import print_function, division
import bluetooth
import math
import numpy as np
from threading import Thread

# index is 0 indexed.
# for dim 16,4;
# 0 -> (0,0)
# 15 -> (0,15)
# 16 -> (1,0)
# 17 -> (1,1)
# 32 -> (2,0)
# 31 -> (1,15)
def get_array_index(index, dimensions=(4,16)):
    x, y = dimensions
    row = math.floor(index / x)
    column = index - row*x
    if(row > y):
        raise ValueError("Value given was outside of the dimensions")
    return (row, column)

#inserts a list of tuples of index,value into the array, and returns the array
def intoarray(items, arr):

    def setarr(item):
        loc, val = item
        x,y = loc
        arr[int(x)][int(y)] = val

    map(setarr,items)

    return arr

def maplisttoindex(datalist, intConversion=False):
    mapped_tuples = []

    for i in range(0, len(datalist)):
        index = get_array_index(i)
        if intConversion:
            value = int(datalist[i])
        else:
            value = datalist[i]

        mapped_tuples.append((index,value))

    return mapped_tuples

def two_d_map(array, func):
    x,y = array.shape
    result = np.zeros(array.shape)
    for x_d in range(0,x):
        for y_d in range(0,y):
            result[x_d][y_d] = func((x_d,y_d),array[x_d,y_d])
    return result


#I'm not 100% sure if the address changes on a per device level
#Or if it changes from device power-cycling, or other things
#The name remains the same, but we're really just concerned about the
#Address for establishing a connection.
#So, setting to the address of our device for now
DEFAULT_ADDRESS = "EC:FE:7E:10:DB:42"

#Default eeprom tuple for dalpha, a, b
default_data = (2.9097e-8, -42, -63)

class Eeprom:

    #init the cell_vals for the eeprom
    def __init_cell_vals(self):
        def init_empty_array(dimensions = (4, 16)):
            arr = []
            x, y = dimensions
            for i in range(0,y):
                arr.append([])
                for j in range(0,x):
                    arr[i].append(0)
            return arr

        #Make an empty array
        self.CellAlphaData = init_empty_array()
        default_Dalpha, default_Ai, default_Bi = default_data
        item_tuple = (default_Dalpha, default_Ai, default_Bi)

        #repeat the item_tuple 64 times
        toInsert = [item_tuple]*64
        #insert into the two dimensional array from the 64 elem list
        two2coords = maplisttoindex(toInsert)
        intoarray(two2coords,self.CellAlphaData)

    def __init__(self):
        #This needs to stay an array of arrays because the data is a
        # tuple of (Dalpha, ai, bi)
        # (Don't be naive and replace it with a numpy array)
        self.__init_cell_vals()
        #Defaults from example code.
        #Sigil value to show if defaults are being used.
        self.DefaultEeprom = True
        self.Kt1 = 22.7998
        self.Kt2 = -0.0044441
        self.VTH = 6752
        self.TGC = 35
        self.Acp = -48
        self.Bcp = -54
        self.Bi_scale = 8
        self.Emission = 0.949981
        self.Alpha_0_scale = 42
        self.D_alpha_scale = 33
        self.Alpha_common = (256*146+87)/math.pow(2, self.Alpha_0_scale)
        self.Alpha_CP = 0
        #Defaults in the eeprom. Sounds crazy, no?
        #But here in our little project of VisIR,
        #You may say every one of us is a default in the eeprom
        #Trying to scratch out a simple little value without breaking our necks.
        # It isn't easy...
        #You may ask: Why do we put these values here, if it's so arbitrary.
        # We put them here because they belong here.
        #You may ask Where do these default values come from?
        # Tradition.
        #And you may ask "How did this tradition get started?"
        #I'll tell you.
        #...
        #I know. They're borrowed from an example in the Melaxis Datasheet. Fancy that, huh?
        #But it's a tradition. And because of our traditions, every bit of data knows who he is
        #and what Python expects it to do.


    #Prints eeprom to string
    def __str__(self):
        thestr = ""

        if(self.DefaultEeprom):
            thestr += 'Default data\n'

        def map_cell_data(celltup):
            dalpha, ai, bi = celltup
            s = 'Dal: {} A: {} B: {}'
            return s.format(dalpha,ai,bi)

        coredata = 'Kt2: {}\tVTH: {}\tTGC: {}\tAcp: {}\n'
        therest = 'Bcp: {}\tEm:  {}\tbiS:{}\ta0S: {}\tadS: {}\n'
        thestr+=coredata.format(self.Kt2, self.VTH, self.TGC, self.Acp)
        thestr+=therest.format(self.Bcp, self.Emission, self.Bi_scale, self.Alpha_0_scale, self.D_alpha_scale)
        if(self.DefaultEeprom):
            d, a, b = default_data
            thestr+='Default cell data of Dalpha: {}\tAi: {}\tB: {}\n'.format(d, a, b)
        else:
            thestr+=str(map(lambda x:map(map_cell_data,x), self.CellAlphaData))
        return thestr


    #Todo: refactor to "unsigned scaled"
    def two_comp_256(self,h,l):
        x = (256*h) + l
        if(x >32767):
            return x - 65536
        else:
            return x

    def two_comp_8(self, v):
        if(v > 127):
            return v - 256
        else:
            return v

    def vth_comp(self, vth_h, vth_l):
        return self.two_comp_256(vth_h, vth_l)

    def kt1_comp(self, kt1_h, kt1_l):
        return self.two_comp_256(kt1_h, kt1_l) / 2**10

    def kt2_comp(self, kt2_h, kt2_l):
        return self.two_comp_256(kt2_h, kt2_l) / 2**20

    def emission_comp(self, e_h, e_l):
        compd = self.two_comp_256(e_h,e_l)
        return compd/32768

    def acp_comp(self, aeep):
        return self.two_comp_8(aeep)

    def bcp_comp(self, beep):
        return self.two_comp_8(beep)

    def aij_comp(self, aijv):
        return self.two_comp_8(aijv)

    def bij_comp(self, bijv):
        return self.two_comp_8(bijv)

    def tgc_comp(self, tgcv):
        return self.two_comp_8(tgcv)

    def alphaij_comp(self,alph_comm,alpha_0_scale,tgc,alpha_cp,dscale, dalpha_ij):
        rhs = alph_comm
        lhs = ((tgc/32) * (alph_comm))/2**alpha_0_scale
        dside = dalpha_ij / 2**dscale

        return (rhs-lhs)+dside

        return alph_comm+(dalpha_ij / 2**dscale)

    def alpha_common_comp(self, a_0_h, a_0_l,scale):
        numerator = self.two_comp_256(a_0_h, a_0_l)
        denomin = 2**scale
        return numerator / denomin

    def alpha_cp_comp(self, cp_h, cp_l):
        return self.two_comp_256(cp_h, cp_l)

    #updates the values within the eeprom given eeprom data from the irblue
    def update(self, data):

        def keep_negative(value):
            if value > 32767:
                return value - 65536
            else:
                return value

        def keep_8b_neg(value):
            if value > 127:
                return value - 256
            else:
                return value

        #Eeeprom data is formetted as:
        #EAAADDDAAADDDAAADDD... EX
        #remove the E at the beginning
        data = data[1:]
        data = data.rstrip()
        data = data.rstrip("X")
        data = data.rstrip("E")
        data = data.rstrip("Ex")
        step = 3
        eep = [int(data[i:i+step]) for i in range(0, len(data), step)]

        #most of these values are stored as two's complement
        # or some pattern to store the data correcly.
        #Unpack from that pattern, apply math, place into the datastructure
        #The numbers indexing into eep are the memory addresses in eeprom
        #To double check in vim, type :echo 0xDB

        #vth_h at 0xDB, vth_l at 0xDA
        self.VTH =self.vth_comp(eep[219],eep[218])

        #kt1_h at 0xDD, kt1_l at 0xDC
        self.Kt1 =self.kt1_comp(eep[221], eep[220])

        #kt2_h at 0xDF, kt2_l at 0xDE
        self.Kt2 = self.kt2_comp(eep[223], eep[222])

        #e_H at 0xE5, e_L a 0xE4

        self.Emission = self.emission_comp(eep[229],eep[228])

        #Bi_scale is at 0xD9 (unsigned)
        self.Bi_scale = eep[217]

        #TGC is stored at 0xD8 in 2s complement
        self.TGC = self.tgc_comp(eep[216])

        #Acp is stored at 0xD4 in 2s complement
        self.Acp = self.acp_comp(eep[212])

        #Bcp is stored at 0xD5 in 2s complement
        self.Bcp = self.bcp_comp(eep[213])

        #We convert the D,A,B data into a 2d array of tuples
        #Since the data is only relevant as a tuple.
        #We'll make an array that we hold onto for the exmaple data
        #Then effectively zip the three arrays into tuples manually
        D_alphas = []

        #Dalpha found in 0x80 to 0xBF
        for x in range(128,191):
            #D_alpha is unsigned in eeprom
            D_alphas.append(eep[x])

        Bis = []
        #Bij's are found in 0x40, 0x7F
        for x in range(64,127):
            Bis.append(self.bij_comp(eep[x]))

        Ais = []
        #Aij's are found in range 0x00, 0x3F
        for x in range(0,63):
            Ais.append(self.aij_comp(eep[x]))

        #If we dont' have arrays of equal length, we have some data issues
        assert len(Bis) == len(Ais) == len(D_alphas)

        #Alpha0_scale is at 0xE2
        self.Alpha_0_scale = eep[226]
        #Dalpha scaling is at 0xE3
        self.D_alpha_scale = eep[227]

        #Alpha common is also alpha_sub_0 in the datasheet
        #Alpha_common_h is at 0xE1, l at 0xE0
        self.Alpha_common = self.alpha_common_comp(eep[225],eep[224],self.Alpha_0_scale)

        tuplist = []

        #Dalpha recalc must happen before zipping the tups together
        for i in range(0, len(Bis)):
            print(D_alphas[i])
            #Recalc in place. :)
            D_alphas[i] = self.alphaij_comp(self.Alpha_common,self.Alpha_0_scale,self.TGC,self.Alpha_CP, self.D_alpha_scale, D_alphas[i])
            print(D_alphas[i])

            #Dalpha-> becomes alpha ij

        for i in range(0, len(Bis)):
            tuplist.append((D_alphas[i], Ais[i], Bis[i]))


        tuplist.pop()

        #Insert into the 2d array @ self.CellAlphaData
        two2coords = maplisttoindex(tuplist)
        intoarray(two2coords,self.CellAlphaData)

        self.DefaultEeprom = False
        print(self)

#These tests all use example data from the Melaxis datasheet
#For that datasheet, google melaxis MLX90620

def test_two_comp():
    eep = Eeprom()
    assert eep.two_comp_256(91,51)==23347
    assert eep.two_comp_256(237, 204)==-4660

#Some of the datasheet responses have estimates w/in 5 decimal points
#Allow us to assert with this. :)
def sortaclose(a,b):
    return round(a-b, 5) == 0

def test_vth_comp():
    eep = Eeprom()
    assert eep.vth_comp(26, 120) == 6776

def test_kt1_comp():
    eep = Eeprom()
    assert sortaclose(eep.kt1_comp(91,51), 22.7998)

def test_kt2_comp():
    eep = Eeprom()
    assert sortaclose(eep.kt2_comp(237, 204), -0.004444)

def test_emissoin_com():
    eep = Eeprom()
    assert sortaclose(eep.emission_comp(121,153),.949981)

def test_cp_comp():
    eep = Eeprom()
    assert eep.acp_comp(208) == -48
    assert eep.bcp_comp(202) == -54

def test_alphaij_comp():
    eep = Eeprom()
    alph_scale = 42
    alp_common = eep.alpha_common_comp(213,228,alph_scale)
    tgc = 35
    alpha_cp = eep.alpha_cp_comp(0,0)
    result = eep.alphaij_comp(alp_common,alph_scale,tgc,alpha_cp, 33,143)
    assert sortaclose(result, 2.9097e-8)

def is_ir_data(data):
    data = data.rstrip()
    return data[0] =='R' and data[19] == 'I'

def is_eeprom_data(data):
    data = data.rstrip()
    return data[0] == 'E'and data[-2:] == "EX"

class mock_socket():
    def __init__(self, s):
        self.content = s

    def recv(self, i):
        if(len(self.content) == 0):
            return None

        to_return = self.content[0:i]
        self.content = self.content[i:]
        return to_return

def test_mock_socket():

    content = "this is my content; look at it go. Isn't it pretty?"
    m = mock_socket(content );
    assert m.recv(1) == "t"
    assert m.recv(10) == content[1:11]
    assert m.recv(10) == content[11:21]

def yield_block(socket, buffer_size = 406):
    #Looks like an infinite loop, but this is a generator so it's not. :)
    while(True):
        try:
            #Receive a block from the socket
            data = socket.recv(buffer_size);

            if data is None:
                raise StopIteration

            #yield it
            yield data
            #Unless we get an error
        except bluetooth.BluetoothError, a:
            print("error in the yield_block")
            sending = False
            print(a)
            #Then stop iterating.
            raise StopIteration

#Splits on a delimitter, with a special case for the case that we get a \n first
def delim_split(my_str):
    line_break = "\n";
    res = []

    spl = my_str.split(line_break)

    for ine in range(0, len(spl)):
        s = spl[ine]
        if( ine != len(spl) -1):
            s = s+line_break
            res.append(s)
        else:
            if(s!= ""):
                res.append(s)
    return res

def test_delim_split():
    a = "this is a basic\r\nsplit that makes sense\r\n";
    assert len(delim_split(a)) == 2
    assert delim_split(a)[0] == "this is a basic\r\n";
    b = "\r\nthis split is trickier\r";
    assert len(delim_split(b)) == 2
    assert delim_split(b)[0]== "\r\n"
    assert delim_split(b)[1][-3:] == "er\r"
    c = "\nthis is also tricky\r\nsee how this could fix this problem here\r"
    f = delim_split(c)
    assert len(f) == 3
    assert f[0] == "\n"
    assert f[1] == "this is also tricky\r\n"
    assert f[2] == "see how this could fix this problem here\r"

#406 is the size of one IR message
def yield_lines(socket, buffer_size = 406):
    current_line = ""

    for f in yield_block(socket, buffer_size):
        #We've recieved a block of 406 characters from the socket.
        split = delim_split(f)

        for s in split:
            if(s[-1:] == "\n"):
                yield current_line + s
                current_line = ""
            else:
                current_line = current_line + s

def test_yield_lines():
    data_str = ""
    with open("irdump","r") as ird:
        data_str = ird.read()


    def test_at_buffer(n):
        m = mock_socket(data_str)
        res = []
        for l in yield_lines(m,n):
            res.append(l)
        return res

    def validate_results(arr):
        print(arr)
        assert len(arr) ==  10
        assert is_eeprom_data(arr[0]) == True
        assert is_ir_data(arr[1]) == True
        assert is_ir_data(arr[9]) == True

    #Doing this test this way makes sure we can change the buffer size we recieve if we need to
    validate_results(test_at_buffer(406))
    validate_results(test_at_buffer(40))
    validate_results(test_at_buffer(1300))


#Class to wrap the socket and allow for disposal
#Todo: see note below, probably beef this up and move some of the info into there
class IRBlue:
    def __init__(self):
        #set up default eeprom
        self.eeprom = Eeprom()
        #set up the socket.
        self.socket = None
        self.connected = False
        self.new_data = None
        self.data_event = None
        self.killed = False
        pass

    def get_data(self):
        while self.new_data is None:
            pass
        return self.new_data

    def connect(self, suppr_r =False):
        #So, the IPhone app code uses the notion of services here instead of creating a serial connection
        #this approach could be much much much better for our purposes.
        #and also would allow us to get rid of the address abstraction
        #Their app code has the guids for the services. PyBluez will support this
        def obtain_connection():
            #Create a connection to the IRBlue device.
            #default port is 1
            port = 1
            socket = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
            socket.connect((DEFAULT_ADDRESS, port))
            #HUGE LIFE PROTIP.
            # ARE YOU READY FOR IT?
            #NO. BUT HERE WE GO ANYWAYS.
            # THIS IS IN ALL CAPS SO THAT NOONE EVER SPENDS 3 WEEKS WORKING
            #ON THIS SORT OF PROBLEM EVER AGAIN.
            # (NOONE BEING ME)
            # LET US SUPPOSE THAT YOU HAVE A SENSOR WHICH SENDS YOU A MESSAGE
            #ONCE EVERY, SAY, SECOND.
            #IF YOU SET YOUR TIME OUT ON THE BT CHANNEL
            #TO BE 1 SECOND,
            #YOU ARE GOING TO HAVE A BAD TIME.
            #WHY?
            #YOU WILL ALWAYS TIME OUT
            #WHY?
            # BECAUSE YOU WILL JUST BARELY RECIEVE THE MESSAGE BEFORE YOU
            #TIME OUT.
            # SO SET THE TIMEOUT TO BE LARGER THAN THE FREQUENCY THAT YOU EXPECT
            # TO GET MESSAGES, DUMMY.
            #HASHTAGS #WOW #SMH #SHAMEONME #NEEDSMOARCODEREVIEW
            socket.settimeout(10.0)
            return socket

#TODO: Make better general error handling for the device code. :(

        try:
            self.socket = obtain_connection()
            self.connected = True
            return True
        except bluetooth.BluetoothError, a:
            print("error on connect()")
            print(a)
            self.connected = False
            return False

    def __start_dataflow(self, suppr_r = False):
        def _start_dataflow(socket):
            if(suppr_r== False):
                print("Sending R001")
                socket.send("R001")
            #R001 is the start message for the device.
            #The only other documented message is
            # H; which speeds up sample rate

        if not self.connected:
            print("calling connect")
            self.connect()
        _start_dataflow(self.socket)


    def ta_comp(self, kt1, kt2, vth_25, ptat):
        longpart = ((-1*kt1) + math.sqrt(kt1**2 - 4*kt2*(vth_25-ptat)))/(2*kt2)
        return longpart + 25

    def scale_b(self, b, b_scale):
        return b / (2**b_scale)

    def vcp_off_compensated_comp(self,vcp, acp, bcp,b_scale, ta):
        return vcp - (acp + (self.scale_b(bcp,b_scale)*(ta-25)))

    def virij_compensated_comp(self, reading, aij, bij,b_scale, ta):
        return reading - (aij + (self.scale_b(bij,b_scale)*(ta-25)))

    def virj_tgc_comp(self, virij_comp, vcp_comp, tgc):
        return virij_comp - ((tgc/32)*vcp_comp)

    def virj_comped(self, vir_tgc, emmi):
        return vir_tgc / emmi

    def t0_ij(self, virj_comp, alpha, ta):
        fourth_root = (((virj_comp)/alpha)+(ta+273.15)**4)**.25
        return fourth_root - 273.15

    def t0_full(self, vcp, ta,tgc, acp, bcp, b_scale, emission, alpha_ij, aij, bij, reading):

        vcp_comp = self.vcp_off_compensated_comp(vcp, acp, bcp, b_scale, ta)

        virj_comp = self.virij_compensated_comp(reading,aij,bij,b_scale, ta)

        vir_tgc = self.virj_tgc_comp(virj_comp, vcp_comp, tgc)
        vir_compd = self.virj_comped(vir_tgc, emission)
        return self.t0_ij(vir_compd, alpha_ij, ta)

    def calculate_pixel(self,regVal, Vcp,location, reading):
        if(reading > 127):
            reading = reading - 256
        x,y = location

        eep = self.eeprom
        alphaij, aij, bij = eep.CellAlphaData[y][x]

        ta = self.ta_comp(eep.Kt1, eep.Kt2, eep.VTH, regVal)

        too = self.t0_full(Vcp, ta, eep.TGC, eep.Acp, eep.Bcp, eep.Bi_scale, eep.Emission, alphaij, aij, bij, reading)
       #$ print("r:{} ta:{} t:{}".format(reading,ta, too))
        return too
   # def t0_full(self, vcp, ta, acp, bcp, b_scale, alpha_ij, tgc, emission, aij, bij, reading):

    def parse_irdata(self,data):
        split = data.split("I")
        regdata = split[0]
        irdata = split[1]

#I'm not actually 100% sure if this is the right data to use
#But it looks strangely like the data in question.
#And VCP looks like it's always supposed to be negative. I could check the rom values (given by [1:4] and [10:12] but I don't want that work right now. ;) .
        reg0 = regdata[4:10]
        reg1 = regdata[13:19]

        #make them integers
        rVal = int(reg0)
        Vcp = int(reg1)

        if Vcp > 32767:
            Vcp = Vcp - 65536
            #I have no clue why this is done; just following the pattern from their implementation

        #Remove pesky trailing X
        irdata = irdata.rstrip()
        irdata = irdata.rstrip("X")

        #Split up the ir data into its numbers, 6 char a piece
        step = 6
        splitdata = [irdata[i:i+step] for i in range(0, len(irdata), step)]
        #convert that data into integers
        splitdata = map(int, splitdata)

        # Get corresponding array coordinates
        two2coords = maplisttoindex(splitdata)

        #insert them into the data array
        data_array = np.zeros((16, 4))
        intoarray(two2coords, data_array)
        d = np.transpose(data_array)

        return (rVal, Vcp, d)

    def start(self):
        self.__start_dataflow()
        self.update_thread = Thread(target = self.data_stream, args = (yield_lines(self.socket),))
        self.update_thread.start()

    def try_start(self):
        self.start()
        #Sometimes you need to start twice in a row.
#        if(self.new_data is None):
#            for i in range(0,9):
#                if(self.new_data is None):
#                    self.start()

    def stop(self):
        self.killed = True

    def data_stream(self, stream):
        for line in stream:
            if(self.killed):
                break

            if(is_ir_data(line)):
                rval, vcp, arr = self.parse_irdata(line)

                def calc_map(location, reading):
                    return self.calculate_pixel(rval, vcp, location,reading)

                self.new_data = (rval, vcp, two_d_map(arr, calc_map), arr)

                if(self.data_event is not None):
                    self.data_event(self.new_data)

            elif(is_eeprom_data(line)):
                self.eeprom.update(line)
            else:
                print("unidentified line. Continuing.")
                print(line)
        self.new_data = None

    def __enter__(self):
        #nothing here
        self.start()

    #Disposal logic
    def __exit__(self, type, value, traceback):
        if self.socket is not None:
            #Close the socket when we're done with it.
            self.socket.close()
            print('closed')
        else:
            print('no socket to close')
        self.stop()

def test_ta_comp():
    irb = IRBlue()
    #def ta_comp(self, kt1, kt2, vth_25, ptat):
    assert sortaclose(irb.ta_comp(22.7998, -0.0044441,6776, 6848), 28.159868)
    #Datasheet lists 28.16; just used the extra digits here

    #def t0_full(self, vcp, ta,tgc, acp, bcp, b_scale, emission, alpha_ij, aij, bij, reading):
def test_full_comp():
    irb = IRBlue()

    def teehee(x):
        return irb.t0_full(-40, 28.16, 35, -48, -54, 8, 0.949981, 2.9097e-8, -42, -63, x)

    def no_tgc(x):
        return irb.t0_full(-40, 28.16, 0, -48, -54, 8, 0.949981, 2.9097e-8, -42, -63, x)

    def neg_em(x):
        return irb.t0_full(-40, 28.16, 0, -48, -54, 8, -1, 2.9097e-8, -42, -63, x)

    print(teehee(144))
    print(teehee(-33))

    print(no_tgc(144))
    print(no_tgc(-33))

    print(neg_em(144))
    print(neg_em(-33))
    assert sortaclose(irb.t0_full(-40, 28.16, 35, -48, -54, 8, 0.949981, 2.9097e-8, -42, -63, 144), 74.7931924)
    assert 1 == 2


#Determines if our device is available by scanning for the addr via the bt stack
def device_available():
    nearby_devices = bluetooth.discover_devices()
    return DEFAULT_ADDRESS in nearby_devices

#Todo: Need to parse the lines into some sort of structure;
#Also need to figure out the format of the data
#I know the following:
# 1. there's a line of eeprom daa tha will need to be parsed differently
# 2. there's register information R [number numbers number] - [numbesr that change] followed by IR data: I ..... terminated with x\r\n
# 3. IR data has the sensor values tab delimitted into 64 values.
# 4. I think it's tab delimted, it renders that way
# 5. The data is just a string, literally.

# Course of action for now: Split up the data into a tuple for the register data and the IR data
# The first part -> reg, the second part, IR
# Parse the IR data into a 16x4 numpy array for starters, with the plain numerical values on hand
# These numbers will ultimately get run through the calculate funciton ot get temps

#Write the temp function w/ default EEPROM values given in the android source with a function that implements the mehtod described in the data sheet

#map the temp function over the other part of the tuple whenever the processing needs to happen. :)

# Then parse the eeprom data.

# also write a testharness.

#maps a data string to a list of tuples containing array location and parsed value
def mapindexes(datastring):
    splitdata = datastring.split("\t")
    return maplisttoindex(splitdata, True)

#inserts a list of tuples of index,value into the array, and returns the array
def intoarray(items, arr):

    def setarr(item):
        loc, val = item
        x,y = loc
        arr[int(x)][int(y)] = val

    map(setarr,items)

    return arr

def main():

    print("starting")
    irb = IRBlue()
    if device_available():
        print("connecting")
        irb.connect()
        print("setting data event")
        #irb.data_event = print_data
        print("starting")
        irb.try_start()
        k = irb.get_data()
        print(k)
        while True:
            import time
            time.sleep(1.4)
            print(irb.get_data())


def old_main():
    print("in main")
    lines = []
    toget = 100
    i = 0
    irb = IRBlue()
    #use with to allow the socket to be closed
    if device_available():
        print("device availalbe")
        for d in irb.data_stream():
            if(toget > i):
                print(d)
                lines.append(d)
                i=i+1
            else:
                sock.close()
                with open('irdatadump', 'w') as thefile:
                    for line in lines:
                        thefile.write(line)
                break
    else:
        print("Device is not available at the moment! Make sure the bluetooth is on and that the device is on.")

def test_parse_eeprom():
    ourEep = Eeprom()
    print(ourEep)
    line = ""
    with open('irdump','r') as dump:
        line = dump.readline()
    ourEep.update(line)
    print(ourEep)
    assert ourEep.DefaultEeprom == False
    assert is_eeprom_data(line) == True
    assert is_ir_data(line) == False
    assert 1 == 2

def test_parse_irdata():
    irb = IRBlue()
    irxample = 'R144006861145-00036I   -38   -37   -36   -36   -36   -35   -33   -34   -35   -33   -32   -32   -34   -32   -30   -32   -33   -30   -29   -32   -32   -30   -29   -30   -32   -30   -29   -30   -32   -29   -28   -29   -31   -29   -27   -28   -30   -27   -27   -26   -32  - 28   -28   -28   -30   -28   -26   -26   -30   -26   -25   -23   -29   -25   -24   -24   -30   -27   -26   -25   -31   -29   -27   -27X'
    rval, vcp, arr = irb.parse_irdata(irxample)
    assert rval == 6861
    assert vcp == -36
    assert (4,16) == arr.shape
    assert -38 == arr[0][0]
    assert -27 == arr[3][15]
    assert is_ir_data(irxample) == True
    assert is_eeprom_data(irxample) == False

    rval, vcp, arr = irb.parse_irdata(irxample)
    def calc_map(location, reading):
        return irb.calculate_pixel(rval, vcp, location,reading)

def test_2d_map():
    vals = np.zeros((2,3))

    def map_func(loc, val):
        x,y = loc
        return val+x+y

    result = two_d_map(vals, map_func)

    assert result.shape == vals.shape
    assert result[0,0] == 0
    assert result[1,2] == 3
    assert result[1,1] == 2

#call the main function when this file is executed
if __name__ == "__main__": main()
