#!/usr/bin/env python
# -*- coding: utf-8 -*-

ALSA_STATE_PATH = '/usr/share/openmoko/scenarios/gsmhandset.state'

# define here controls you want to manage
# only controls of type INTEGER are allowed
CONTROLS = [
    5, 12, 48, # mic volume & buzz problem
    4, 6,      # speaker volume & echo problem
]

################################################################################
# Anti-Buzz -- an application to kill your GSM buzz
#
# Copyright (C) 2009 Valéry Febvre <vfebvre@easter-eggs.com>
# http://???.projects.openmoko.org/
#
# This file is part of Anti-Buzz.
#
# Neon is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# Neon is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""
Anti-Buzz is an application to kill your GSM buzz.
"""

import os, re, commands
import elementary

RE_CONTROL = re.compile(r'^\t\control.(\d+) {$')

RE_TYPE    = re.compile(r'^\t\tcomment.type ([A-Z]+).*')
RE_COUNT   = re.compile(r"^\t\tcomment.count (\d+)")
RE_RANGE   = re.compile(r"^\t\tcomment.range '(\d+) - (\d+)")
RE_NAME    = re.compile(r"^\t\tname '([A-Za-z0-9- ]+)'")
RE_VALUE   = re.compile(r'^\t\tvalue (\d+)')
RE_VALUE0  = re.compile(r'^\t\tvalue.0 (\d+)')
RE_VALUE1  = re.compile(r'^\t\tvalue.1 (\d+)')

controls_data = {}
blocks        = {}
sliders       = {}

def destroy(obj, event, data):
    print "Bye bye"
    elementary.exit()

def restore_default(obj, event, data):
    for id in sliders.keys():
        control = controls_data[id]
        sliders[id].value = float(control['value'])
    alsactl_restore(ALSA_STATE_PATH)

def apply_changes(obj, event, data):
    new_alsa_state_path = '/tmp/%s' % os.path.basename(ALSA_STATE_PATH)
    f = open(new_alsa_state_path, 'w+')
    f.write("state.neo1973gta02 {\n")
    for id in blocks.keys():
        for line in blocks[id]:
            if id in CONTROLS and \
                (RE_VALUE.match(line) or RE_VALUE0.match(line) or RE_VALUE1.match(line)):
                old_value = controls_data[id]['value']
                new_value = str(int(round(sliders[id].value)))
                #print 'control %d: %s => %s' % (id, old_value, new_value)
                line = line.replace(old_value, new_value)
            f.write(line)
    f.close()
    alsactl_restore(new_alsa_state_path)
    msg = "New Alsa scenario has been applied and saved in %s" % new_alsa_state_path
    label_info.label_set(msg)

def alsactl_restore(state_file):
    commands.getoutput('alsactl -f %s restore' % state_file)

if __name__ == "__main__":
    f = open(ALSA_STATE_PATH, 'r')
    lines = f.readlines()

    num = None

    # store lines of all controls
    for line in lines:
        if RE_CONTROL.match(line):
            num = int(RE_CONTROL.findall(line)[0])
            blocks[num] = []
        if num:
            blocks[num].append(line)

    # get data of selected controls
    for id in CONTROLS[:]:
        is_valid = True
        count = None
        type_ = None
        for line in blocks[id]:
            # type
            if RE_TYPE.match(line):
                type_ = RE_TYPE.findall(line)[0]
            if type_ and type_ != 'INTEGER':
                print 'WARNING: ignore control %d of type %s' % (id, type_)
                del CONTROLS[CONTROLS.index(id)]
                is_valid = False
                break

            # name
            if RE_NAME.match(line):
                name = RE_NAME.findall(line)[0]
            # count
            if RE_COUNT.match(line):
                count = int(RE_COUNT.findall(line)[0])
            # range
            if RE_RANGE.match(line):
                range = RE_RANGE.findall(line)[0]
            # value or value0
            if count == 1:
                if RE_VALUE.match(line):
                    value = RE_VALUE.findall(line)[0]
            elif count == 2:
                if RE_VALUE0.match(line):
                    value = RE_VALUE0.findall(line)[0]

        if is_valid:
            controls_data[id] = {
                'name':  name,
                'type':  type_,
                'count': count,
                'range': range,
                'value': value,
                }

    # build GUI
    elementary.init()
    win = elementary.Window("test", elementary.ELM_WIN_BASIC)
    win.title_set("anti-buzz application")
    win.destroy = destroy
    
    bg = elementary.Background(win)
    win.resize_object_add(bg)
    bg.size_hint_weight_set(1.0, 1.0)
    bg.show()
    
    box0 = elementary.Box(win)
    box0.size_hint_weight_set(1.0, 1.0)
    win.resize_object_add(box0)
    box0.show()

    sc = elementary.Scroller(win)
    sc.size_hint_weight_set(1.0, 1.0)
    sc.size_hint_align_set(-1.0, -1.0)
    box0.pack_end(sc)
    sc.show()

    box1 = elementary.Box(win)
    box1.size_hint_weight_set(1.0, 1.0)
    sc.content_set(box1)
    box1.show()

    for id in CONTROLS:
        if id not in controls_data.keys():
            continue

        control = controls_data[id]

        label = elementary.Label(win)
        label.label_set(' <b>%s</b> (%d)' % (control['name'], id))
        label.size_hint_align_set(0, 0.5)
        label.scale_set(1.5)
        box1.pack_end(label)
        label.show()

        slider = elementary.Slider(win)
        slider.label_set('')
        slider.span_size_set(160)
        slider.size_hint_align_set(0, 0.5)
        slider.size_hint_weight_set(0, 1)
        slider.unit_format_set("   %3.0f" + ' / %d' % float(control['range'][1]))
        slider.indicator_format_set("%3.0f")
        slider.min_max_set(float(control['range'][0]), float(control['range'][1]))
        slider.value = float(control['value'])
        slider.scale_set(2)
        box1.pack_end(slider)
        slider.show()
        sliders[id] = slider

    label_info = elementary.Label(win)
    label_info.label_set(' ')
    label_info.size_hint_align_set(0.5, 0.5)
    box0.pack_end(label_info)
    label_info.show()

    table = elementary.Table(win)
    table.size_hint_weight_set(1, -1)
    box0.pack_end(table)
    table.show()

    btn_default = elementary.Button(win)
    btn_default.label_set('Restore Default')
    table.pack(btn_default, 0, 0, 1, 1)
    btn_default.show()
    btn_default.clicked = restore_default

    btn_apply = elementary.Button(win)
    btn_apply.label_set('     Apply     ')
    table.pack(btn_apply, 1, 0, 1, 1)
    btn_apply.show()
    btn_apply.clicked = apply_changes

    btn_quit = elementary.Button(win)
    btn_quit.label_set('      Quit      ')
    table.pack(btn_quit, 2, 0, 1, 1)
    btn_quit.show()
    btn_quit.clicked = destroy

    win.resize(480, 640)
    win.show()
    elementary.run()
    elementary.shutdown()
