import logging
import math
from gi.repository import Gst, Gtk


class AudioLevelDisplay(object):
    """Displays a Level-Meter of another VideoDisplay into a GtkWidget"""

    def __init__(self, drawing_area):
        self.log = logging.getLogger(
            'AudioLevelDisplay[{}]'.format(drawing_area.get_name())
        )

        self.drawing_area = drawing_area

        self.levelrms = []
        self.levelpeak = []
        self.leveldecay = []

        # register on_draw handler
        self.drawing_area.connect('draw', self.on_draw)

    def on_draw(self, widget, cr):
        # number of audio-channels
        channels = len(self.levelrms)

        if channels == 0:
            return

        width = self.drawing_area.get_allocated_width()
        height = self.drawing_area.get_allocated_height()

        # space between the channels in px
        margin = 2

        # 1 channel -> 0 margins, 2 channels -> 1 margin, 3 channels…
        channel_width = int((width - (margin * (channels - 1))) / channels)

        # self.log.debug(
        #     'width: %upx filled with %u channels of each %upx '
        #     'and %ux margin of %upx',
        #     width, channels, channel_width, channels - 1, margin
        # )

        # normalize db-value to 0…1 and multiply with the height
        rms_px = [self.normalize_db(db) * height for db in self.levelrms]
        peak_px = [self.normalize_db(db) * height for db in self.levelpeak]
        decay_px = [self.normalize_db(db) * height for db in self.leveldecay]

        # set the line-width >1, to get a nice overlap
        cr.set_line_width(2)

        # iterate over all pixels
        for y in range(0, height):

            # calculate our place in the color-gradient, clamp to 0…1
            # 0 -> green, 0.5 -> yellow, 1 -> red
            color = self.clamp(((y / height) - 0.6) / 0.42)

            for channel in range(0, channels):
                # start-coordinate for this channel
                x = (channel * channel_width) + (channel * margin)

                # calculate the brightness based on whether this line is in the
                # active region

                # default to 0.25, dark
                bright = 0.25
                if int(y - decay_px[channel]) in range(0, 2):
                    # decay marker, 2px wide, extra bright
                    bright = 1.5
                elif y < rms_px[channel]:
                    # rms bar, full bright
                    bright = 1
                elif y < peak_px[channel]:
                    # peak bar, a little darker
                    bright = 0.75

                # set the color with a little reduced green
                cr.set_source_rgb(
                    color * bright,
                    (1 - color) * bright * 0.75,
                    0
                )

                # draw the marker
                cr.move_to(x, height - y)
                cr.line_to(x + channel_width, height - y)
                cr.stroke()

                # draw a black line for the margin
                cr.set_source_rgb(0, 0, 0)
                cr.move_to(x + channel_width, height - y)
                cr.line_to(x + channel_width + margin, height - y)
                cr.stroke()

        # draw db text-markers
        cr.set_source_rgb(1, 1, 1)
        for db in [-40, -20, -10, -5, -4, -3, -2, -1]:
            text = str(db)
            (xbearing, ybearing,
             textwidth, textheight,
             xadvance, yadvance) = cr.text_extents(text)

            y = self.normalize_db(db) * height
            cr.move_to((width - textwidth) / 2, height - y - textheight)
            cr.show_text(text)

        return True

    def normalize_db(self, db):
        # -60db -> 1.00 (very quiet)
        # -30db -> 0.75
        # -15db -> 0.50
        #  -5db -> 0.25
        #  -0db -> 0.00 (very loud)
        logscale = 1 - math.log10(-0.15 * db + 1)
        return self.clamp(logscale)

    def clamp(self, value, min_value=0, max_value=1):
        return max(min(value, max_value), min_value)

    def level_callback(self, rms, peak, decay):
        self.levelrms = rms
        self.levelpeak = peak
        self.leveldecay = decay
        self.drawing_area.queue_draw()
