#!/usr/bin/env python

from __future__ import division
import sys, tag_wrapper, struct, math
import mutagen.mp3

class SoundCheck(object):
    def __init__(self, tag):
        self.tag = tag
        if type(self.tag._tag) == mutagen.mp3.MP3:
            parts = self.get_parts(tag['iTunNORM'][0])
        else:
            parts = self.get_parts(tag['----:com.apple.iTunes:iTunNORM'][0])
        self.dBChange = (parts[0], parts[1])
        self.dBChange_2500 = (parts[2], parts[3])
        self.statsOne = (parts[4], parts[5])
        self.peak = (parts[6], parts[7])
        self.statsTwo = (parts[8], parts[9])

    def get_parts(self, value):
        return [int(x,16) for x in value.split()]

    def build_parts(self):
        str = ""
        l = list(self.dBChange + self.dBChange_2500 + self.statsOne + self.peak + self.statsTwo)
        for x in l:
            s = hex(x)[2:].upper()
            s = ("0" * (8 - len(s))) + s
            str = " ".join((str, s))
        return str

    def write(self):
        if type(self.tag._tag) == mutagen.mp3.MP3:
            self.tag['iTunNORM'] = self.build_parts()
        else:
            self.tag['----:com.apple.iTunes:iTunNORM'] = self.build_parts()
        self.tag.save()

def soundcheck2db(s, g):
    return -10 * math.log10(s/g)

def db2soundcheck(d, g):
    return int(g * math.pow(10, -d/10))

def build_channels(items, piece, gain, weighted=False):
    left_channel = []
    right_channel = []

    for k,v in items.items():
        left_channel.append(soundcheck2db(getattr(v,piece)[0], gain))
        right_channel.append(soundcheck2db(getattr(v,piece)[1], gain))

    left_channel.sort()
    right_channel.sort()
    if len(left_channel) > 3 and weighted:
        left_channel = left_channel[1:-1]
        right_channel = right_channel[1:-1]

    left_average = sum(left_channel)/len(left_channel)
    right_average = sum(right_channel)/len(right_channel)

    print "Average: (%f dB, %f dB) @ %d" % (left_average, right_average, gain)

    return (left_average, right_average)

def main(args=sys.argv):
    pretend = False
    weighted = False
    if args[1] == '-h':
        print """album_soundcheck.py: album_soundcheck.py [-h|-p] [-w] <files>
Determines the average soundcheck level and overwrites the existing value with
that.
-h - print this help.
-p - pretend, print the resulting average.
-w - remove the smallest and largest values before computing average.
Values are (Left Channel, Right Channel)"""
        return 0
    if args[1] == '-p':
        args = args[1:]
        pretend = True
    if args[1] == '-w':
        args = args[1:]
        weighted = True

    files = {}
    for name in args[1:]:
        files[name] = SoundCheck(tag_wrapper.tag(name))

    if pretend:
        print "Original dB adjustments:"
        keys = files.keys()
        keys.sort()
        for k in keys:
            print "%s: (%f dB, %f dB) @ 1000" % (k,
                    soundcheck2db(files[k].dBChange[0], 1000),
                    soundcheck2db(files[k].dBChange[1], 1000))

    dBChange = build_channels(files, 'dBChange', 1000, weighted)
    dBChange_2500 = build_channels(files, 'dBChange_2500', 2500, weighted)

    if pretend:
        return 0

    for v in files.values():
        v.dBChange = tuple([db2soundcheck(x, 1000) for x in dBChange])
        v.dBChange_2500 = tuple([db2soundcheck(x, 2500) for x in dBChange_2500])
        v.write()

    return 0

if __name__ == '__main__':
    sys.exit(main())
