#! /usr/bin/python -i

import PPdev

from types import StringType
import sys
sys.ps1 = "DIRENA-> "
sys.ps2 = "DIRENA-. "
from time import sleep

import struct

def wREG(a,d):
    PPdev.write(struct.pack("<BHLB", 0x87, a, d, 0x7e))

def wCMD(c):
    PPdev.waddr(c)

def reset(what=0xf):
    wCMD(0xf0|what)

def enable(on=1):
    wCMD(on&1)

def adcmask(m=0x3ffff):
    wREG(2,m)

def ppstatus():
    r = {"data": PPdev.status(0)}
    s = PPdev.status(1)
    r["status"] = s
    r["nWAIT"] = s & 0x80
    r["INTR"] = s & 0x40
    r["paper out"] = s & 0x20
    r["sel in"] = s & 0x10
    r["ERROR"] = s & 0x08
    r["IRQ"] = s & 0x04
    r["timeout"] = s & 0x01
    c = PPdev.status(2)
    r["control"] = c
    r["nDSTB"] = c & 0x08
    r["RESET"] = c & 0x04
    r["nASTB"] = c & 0x02
    r["nWRITE"] = c & 0x01
    return r

def f2i(f):
    s = f&0x8000
    e = (f>>11)&15
    m = f&0x7ff
    if e:
        m |= 0x0800
        if e>1:
            m<<=e-1
    if s:
        return -m
    return m

def i2f(i):
    if i<0:
        i = -i;
        s = 0x8000
    else:
        s = 0
    if i<0x800:
        return i|s
    e = 1
    while i>=0x1000:
        e+=1
        i>>=1
    if e>=16:
        return 0x7fff|s
    return s | (e<<11) | i&0x7ff

class Sample:

    def __init__(self, t):
        if isinstance(s, StringType):
            t = PPdev.parse(t)[1]
        if len(t) != 2:
            raise ValueError, "Not a Sample Record"
        self.tuple = t
        self.time = t[0]
        self.data = t[1]

    def __str__(self):
        return "S %12.7f: %4d %4d" % (
            self.time/3e6, self.data[0], self.data[1],
            )

class Event:

    def __init__(self, t):
        if isinstance(s, StringType):
            t = PPdev.parse(t)[1]
        if len(t) != 4:
            raise ValueError, "Not an Event Record"
        self.tuple = t
        self.time = t[0]
        self.a = t[2]
        self.b = t[3]
        self.prescale = t[1][0]
        self.apeak = t[1][1]
        self.bzero = t[1][2]
        self.trig  = t[1][3]

    def Apeak(self, i):
        return (self.apeak>>i) & 1
        
    def Bzero(self, i):
        return (self.bzero>>i) & 1
        
    def Trig(self, i):
        return (self.trig>>i) & 1
        
        
    def __str__(self):
        return "E %12.7f: ps(%d)  0:(%s%s%s) %7.3f %7.3f  1:(%s%s%s) %7.3f %7.3f" % (
            self.time/3e6, self.prescale,
            " T"[self.Trig(0)], " A"[self.Apeak(0)], " B"[self.Bzero(0)], self.a[0], self.b[0],
            " T"[self.Trig(1)], " A"[self.Apeak(1)], " B"[self.Bzero(1)], self.a[1], self.b[1],
            )

def trig(i, v):
    wREG(0xf80+i, v)

def filt(i,j, v):
    wREG(0x800+16*i+j, v)

def thres(i, thr1, thr2=0x7fffffff, tmin=0x3f, tmax=0x3f):
    thr1 = (i2f(int(thr1))>>5) & 0x3ff
    thr2 = (i2f(int(thr2))>>5) & 0x3ff
    trig(i+1, (long(tmax)<<26) | (tmin<<20) | (thr2<<10) | thr1)

def tmask(m1=0x3ffff, m2=0):
    trig(19, m1)
    trig(20, m2)

def nsamples(n=63):
    trig(30, n)

def pstatus():
    s = PPdev.raddr()
    print >>sys.stderr, "DIRENA STATUS 0x%02x:"%s,
    if s&0x80:
        print >>sys.stderr, " gEN",
    if s&0x40:
        print >>sys.stderr, " tEN",
    if s&0x20:
        print >>sys.stderr, " Ffull",
    if s&0x10:
        print >>sys.stderr, " Fempty",
    if s&0x08:
        print >>sys.stderr, " Falmost",
    if s&0x04:
        print >>sys.stderr, " ???",
    if s&0x02:
        print >>sys.stderr, " Davail",
    if s&0x01:
        print >>sys.stderr, " ERROR",
    print >>sys.stderr
    

def Boot(f="direna.rbf", m=0):
    PPdev.close()
    PPdev.open("/dev/parport0", m)
    PPdev.boot(f)
    reset()
    reset()
    Defaults()
    adcmask(3)
    tmask(3)
    thres(0,10000)
    thres(1,10000)
    if not m:
        pstatus()

Dump = None
DoDump = None
DontDump = False
DumpData = ""

def Start(f):
    import threading
    def func(f):
        global DoDump, DontDump, DumpData
        fn = False
        if not isinstance(f, file):
            if isinstance(f, StringType):
                f = open(f, "a+")
                fn = True
        # else f better has a write() method
        while not DontDump:
            DoDump.wait()
	    if not DontDump:
                s = PPdev.read()
                f.write(s)
		f.flush()
                DumpData = s
	if fn:
            f.close()
    global Dump, DoDump, DontDump
    Dump = threading.Thread(target=func, args=(f,))
    Dump.setDaemon(True)
    DoDump = threading.Event()
    DontDump = False
    Dump.start()
    DoDump.set()

def Stop():
    global DontDump
    DontDump = True
    DoDump.set()
    Dump.join()

def Pause(yes=True):
    if yes:
        DoDump.clear()
    else:
        DoDump.set()

class Datafile:
    def __init__(self, inp, follow=False):
        self.follow = follow
        if isinstance(inp, StringType):
            from bz2 import BZ2File
            try:
                self.inp = BZ2File(inp)
                r = self.inp.read(4096)
                self.follow=False
            except IOError:
                self.inp = open(inp)
                r = self.inp.read(4096)
            self.inps = True
        else:
            self.inp = inp
            r = self.inp.read(4096)
            self.inps = False
        self.r = [r,None]
    def __iter__(self):
        return self
    def next(self):
        self.r = PPdev.parse(self.r[0])
        if self.r[1]:
            return self.r[1]
        self.r = PPdev.parse(self.r[0] + self.inp.read(4096))
        while not self.r[1]:
            if self.follow:
                try:
                    sleep(10)
                except KeyboardInterrupt:
                    raise StopIteration
            else:
                if self.inps:
                    self.inp.close()
                    self.inp = None
                    self.inps = False
                raise StopIteration
            self.r = PPdev.parse(self.r[0] + self.inp.read(4096))
        return self.r[1]
    def preview(self):
        if len(self.r[0]) < 96:
            self.r = [self.r[0]+self.inp.read(4096), None]
        return self.r[0]

def Xstr(r):
    return "X "+repr(r[0])
def Sstr(r):
    return ("S %d " % (r[0]&0xffffffffL))+" ".join([str(i) for i in r[1]])
def Estr(r):
    return ("E %10u %d 0x%05x 0x%05x 0x%05x " % (
        r[0]&0xffffffffL,
        r[1][0], r[1][1], r[1][2], r[1][3],
        ))+" ".join(sum([[str(i) for i in c] for c in r[2:]],[]))
                      
    
def ParseFile(inp, out, E=True, S=True, X=True, follow=False):
    data = Datafile(inp, follow)
    if isinstance(out, StringType):
        out = open(out, "w")
        outs = True
    else:
        outs = False
    for r in data:
        if len(r)==1:
            if X:
                out.write(Xstr(r)+"\n")
        elif len(r)==2:
            if S:
		out.write(Sstr(r)+"\n")
        elif E:
            out.write(Estr(r)+"\n")
    if outs:
        out.close()

def ParseChannel(inp, out, ch, nsa = 16, follow=False):
    data = Datafile(inp, follow)
    if isinstance(out, StringType):
        out = open(out, "w")
        outs = True
    else:
        outs = False
    if ch<0 or ch>=18:
        raise ValueError, "channel index out of range"
    E=[]
    for r in data:
        t = 0
        if len(r)==4:
            t = r[0]
            E.append((t,
                      r[1][0],
                      (r[1][1]>>ch) & 1,
                      (r[1][2]>>ch) & 1,
                      (r[1][3]>>ch) & 1,
                      r[2][ch], r[3][ch],
                      [0]*nsa))
        elif len(r)==2:
            t = r[0]
            for e in E:
                dt = t-e[0]
                if dt < nsa and dt >= 0:
                    e[7][dt] = r[1][ch]
        if t>0:
            while E and (E[0][0] <= t-nsa or E[0][0]>t):
                e = E[0]
                print >>out, e[0]&0xffffffffL, e[1], e[2], e[3], e[4], e[5], e[6],
                for s in e[7]:
                    print >>out, s,
                print >>out
                del E[0:1]
    if outs:
        out.close()

class Event:
    def __init__(self, r, el, ns):
        if len(r)!=4:
            raise ValueError
        self.el = el
        self.ns = ns
        self.E = [r[0],list(r[1]),list(r[2]),list(r[3]),[0]*18]
        self.S = []
        self.T = r[0]
        self.complete = False

    def __iadd__(self, r):
        if len(r)==1:
            raise ValueError
        dT = (r[0]-self.T) & 0xffffffffL
        if len(r)==4:
            if dT>self.el:
                if dT>=self.ns:
                    self.complete = True
                raise ValueError
            apeak = r[1][0]
            for c in range(18):
                b = 1<<c
                if r[2][c] > self.E[2][c]:
                    if apeak & b:
                        self.E[1][0] |= b
                    else:
                        self.E[1][0] &= ~b
                    self.E[2][c] = r[2][c]
                    self.E[3][c] = r[3][c]
                    self.E[4][c] = dT
            return self
        if len(r)==2:
            if dT>=self.ns:
                if dT>self.el:
                    self.complete = True
                raise ValueError
            self.S.append(r)
            return self
        raise ValueError

    def __str__(self):
        return "\n".join([Estr(self.E)]+[Sstr(s) for s in self.S])+"\n"

def make_events(inp, out, el, ns, follow=False):
    events=[]

    data = Datafile(inp, follow)
    for r in data:
        if len(r)==1:
            events=[]
            continue
        used = False
        for e in events:
            try:
                e+=r
                used = True
            except ValueError:
                pass
        if not used:
            try:
                events.append(Event(r, el, ns))
            except ValueError:
                pass
        while events and events[0].complete:
            out.write(str(events[0]))
            events[0:1] = []


try:
    execfile("direnarc.py")
except IOError, e:
    print >>sys.stderr, e
