#!/usr/bin/python3
# \file hpimeters.py
#
# Example use of python hpi bindings - HPI meters

import sys
try:
    from tkinter import Frame, Label, Scale, Tk, Canvas
except ImportError:
    from tkinter import Frame, Label, Scale, Tk, Canvas

import audioscience.hpi as hpi

poll_ms = 125
sstate=('?','Stopped','Playing','Recording','Xrun')

hpi.NODE = {100:'none',101:'Stream',102:'Line',103:'Aesesbu',104:'Tuner',
    105:'Rf',106:'Clock',107:'Bitstream',108:'Mic',109:'Cobranet', 110: 'Analog',
    111:'Adapter', 112:'Rtp', 113:'GPI', 114:'Internal',
    200:'none',201:'Stream',202:'Line',203:'Aesebu',204:'Rf',
    205:'Speaker',206:'Cobranet', 207:'Analog', 208:'Rtp', 209:'GPO'}

def AdapterInfoString(adapter):
  e,nos,nis,ver,serial,atype= hpi.Adapter_GetInfo(adapter)
  return "%d: ASI%x IS=%d OS=%d" % (adapter,atype, nis, nos)

class MeterBar(Frame):
    '''A single meter bar, with peak indicator
    '''
    def __init__(self, master=None, colour='pale green'):
        Frame.__init__(self, master)
        self.pack(side='left')
        self.height = 120
        self.width = 15
        self.h = -1
        self.g = -1
        self.canvas = Canvas(self, width=self.width, height=self.height)
        self.canvas.pack()
        self.r = self.canvas.create_rectangle(0, self.height - 5,
                    self.width, self.height, fill=colour, outline=colour)
        self.p = self.canvas.create_rectangle(0, self.height - 5,
                    self.width, self.height,
                    fill='orange red', outline='orange red')

    def set(self, h, g=0):
        if h < 0:
            h = 0
        if h != self.h:
            self.canvas.coords(self.r,
                    (0, self.height - h * self.height,
                    self.width, self.height))
            self.h = h
        if g < 0:
            g = 0
        if g != self.g:
            self.canvas.coords(self.p,
                    (0, self.height - g * self.height,
                    self.width, self.height - g * self.height + 3))
            self.h = h

class MonoMeter(Frame):
    '''A meter bar, with a scale
    '''
    def __init__(self, master=None, label=None, colour='red'):
        Frame.__init__(self, master)
        self.pack(side='left')
        if label is not None:
            self.label =Label(self,text=label)
            self.label.pack()

        self.ml = MeterBar(self)
        self.ml.pack(side='left')

        self.scale = Canvas(self, width=24, height=121)
        for l in range(1,122,20):
            self.scale.create_line(1, l, 5, l)
            self.scale.create_line(20, l, 24, l)
            self.scale.create_text(12, l,
                    text= '%d' % (-(l - 1) / 2),
                    anchor='n',
                    font=('Helvetica', 8))
        self.scale.pack(side='left')

        spacer = Canvas(self, width=5, height=121)
        spacer.create_line(3,0,3,121)
        spacer.pack(side='left')

    def set(self, h, g):
        self.ml.set(h[0], g[0])

class StereoMeter(Frame):
    '''A pair of meter bars, with a scale between
    '''
    def __init__(self, master=None, label=None, colour='red'):
        Frame.__init__(self, master)
        self.pack(side='left')
        if label is not None:
            self.label =Label(self,text=label)
            self.label.pack()

        self.ml = MeterBar(self)
        self.ml.pack(side='left')

        self.scale = Canvas(self, width=24, height=121)
        for l in range(1,122,20):
            self.scale.create_line(1, l, 5, l)
            self.scale.create_line(20, l, 24, l)
            self.scale.create_text(12, l,
                    text= '%d' % (-(l - 1) / 2),
                    anchor='n',
                    font=('Helvetica', 8))
        self.scale.pack(side='left')

        self.mr = MeterBar(self)
        self.mr.pack(side='left')
        spacer = Canvas(self, width=5, height=121)
        spacer.create_line(3,0,3,121)
        spacer.pack(side='left')

    def set(self, h, g):
        self.ml.set(h[0], g[0])
        self.mr.set(h[1], g[1])

class HpiMeter(Frame):

    def __init__(self, master, hmixer, node, index, label=None):
        self.hx = hmixer

        if (node < 200):
            e,self.hm = hpi.Mixer_GetControl(self.hx, node, index, 0, 0, hpi.CONTROL.METER)
            if e:
                raise LookupError('Meter on %s %d not found' % (hpi.SourceDict[node], index))
        else:
            e,self.hm = hpi.Mixer_GetControl(self.hx, 0, 0, node, index, hpi.CONTROL.METER)
            if e:
                raise LookupError('Meter on %s %d not found' % (hpi.DestDict[node], index))

        e, self.channels = hpi.Meter_QueryChannels(self.hm)
        if e:
            self.channels = 2

        Frame.__init__(self, master)
        if label is not None:
            label=hpi.NODE[node]+str(index)
        if self.channels == 1:
            self.meter = MonoMeter(self, label)
        else:
            self.meter = StereoMeter(self, label)
        self.update()

    def update(self):
        e,peak = hpi.Meter_GetPeak(self.hm)
        e2,rms = hpi.Meter_GetRms(self.hm)
        pm = (1.0 + peak[0]/6000.0, 1.0 + peak[1]/6000.0)
        if not e2:
            rm = (1.0 + rms[0]/6000.0, 1.0 + rms[1]/6000.0)
        else:
            rm = (0, 0)

        self.meter.set(rm, pm)
        self.listenID = self.after(poll_ms, self.update)


class ObjMeters(Frame):
  def __init__(self, adapter, obj, master=None):
    Frame.__init__(self, master)
    self.pack(side='top', anchor='w')
    for i in range(32):
        try:
            HpiMeter(self, adapter, obj, i, label=True).pack(side='left')
        except LookupError(e):
            break


if __name__ == '__main__':

    from optparse import OptionParser

    parser = OptionParser()
    parser.add_option('-a','--adapter',type='int',
                      help='Adapter index. Default=%default', default=0)
    parser.add_option('-I','--interface-ip-address',type='string',dest='ipaddr',
                          help='Interface IP address.', default=None)
    parser.add_option('-n','--node',type='int',
                      help='Node type=%default', default=0)
    parser.add_option('-N','--node-count',type='int',
                      help='Node type=%default', default=0)

    opts,args = parser.parse_args()
    hpi.setup(opts.adapter, opts.ipaddr)

    e, nos, nis, ver, serial, atype = hpi.Adapter_GetInfo(opts.adapter)

    app = Tk()
    app.title("HPI Meters" + AdapterInfoString(opts.adapter))

    #adap=Label(text=AdapterInfoString(opts.adapter))
    #adap.pack()

    e, hx = hpi.Mixer_Open(opts.adapter)
    if e:
        sys.exit('HPI Mixer open error ' + hpi.GetErrorText(e))

    Label(text='SOURCES').pack()
    ObjMeters(hx, hpi.SOURCENODE.OSTREAM)
    ObjMeters(hx, hpi.SOURCENODE.LINEIN)
    ObjMeters(hx, hpi.SOURCENODE.ANALOG)
    ObjMeters(hx, hpi.SOURCENODE.MICROPHONE)
    ObjMeters(hx, hpi.SOURCENODE.AESEBU_IN)
    ObjMeters(hx, hpi.SOURCENODE.COBRANET)
    Label(text='DESTINATIONS').pack()
    ObjMeters(hx, hpi.DESTNODE.LINEOUT)
    ObjMeters(hx, hpi.DESTNODE.ANALOG)
    ObjMeters(hx, hpi.DESTNODE.AESEBU_OUT)
    ObjMeters(hx, hpi.DESTNODE.COBRANET)
    ObjMeters(hx, hpi.DESTNODE.ISTREAM)

    app.mainloop()
