/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id: $
 *
 * Copyright (C) 2002 by Philipp Pertermann
 *
 * All files in this archive are subject to the GNU General Public License.
 * See the file COPYING in the source tree root for full license agreement.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include "mas.h"
#include "thread.h"
#include "kernel.h"
#include "settings.h"
#include "lcd.h"
#include "wps-display.h"
#include "menu.h"
#include "sprintf.h"
#include "button.h"
#include "system.h"
#include "font.h"
#include "icons.h"
#include "lang.h"

static char peak_meter_name[] = { "Peak Meter" } ;
static char peak_meter_stack[DEFAULT_STACK_SIZE / 2];

#ifdef HAVE_LCD_BITMAP
static int peak_meter_max_l;
static int peak_meter_max_r;
static long peak_meter_timeout_l;
static long peak_meter_timeout_r;
static long peak_meter_overflow_timeout_l;
static long peak_meter_overflow_timeout_r;
#endif

bool peak_meter_enabled = false;
static int peak_meter_l;
static int peak_meter_r;
static bool peak_meter_l_overflow = false;
static bool peak_meter_r_overflow = false;

/**
 * This is the main loop of the peak meter thread. It reads volume levels 
 * from the mas and finds maxima. These maxima are stored for later display.
 * To reduce cpu / i²c bus load (e.g. when no peak meter is displayed) 
 * this loop can be turned off by setting the variable peak_meter_enabled. 
 */
static void peak_meter_thread(void)
{
    /* somehow rockbox freezes while booting if
       we don't wait for a while */
    sleep(500);
    while (1) {
       /* if the peak meter is disabled we don't want
          to communicate with the mas in order to save
          energy */
       if (peak_meter_enabled)
       {
           /* read the peak values */
            int left = mas_codec_readreg(0xC);
            int right = mas_codec_readreg(0xD);

            /* check for overflows
               An overflow is assumed when two consecutive readouts
               of the volume are at full scale. This is proven
               to be inaccurate in both ways: it may detect overflows
               when no overflow occurred and it may fail to detect
               a real overflow. */

            if ((left  == peak_meter_l) && 
                (left  == MAX_PEAK - 1)) {
                peak_meter_l_overflow = true;
                peak_meter_overflow_timeout_l = 
                    current_tick + global_settings.peak_meter_overflow * HZ;
            }

            if ((right == peak_meter_r) && 
                (right == MAX_PEAK - 1)) {
                peak_meter_r_overflow = true;
                peak_meter_overflow_timeout_r = 
                    current_tick + global_settings.peak_meter_overflow * HZ;
            }

            /* peaks are searched -> we have to find the maximum */
            peak_meter_l = MAX(peak_meter_l, left);
            peak_meter_r = MAX(peak_meter_r, right);
       }
        yield();
    }
}

/**
 * Installs the peak meter thread 
 */
void peak_meter_init(void) 
{
    create_thread(peak_meter_thread, peak_meter_stack,
                  sizeof(peak_meter_stack), peak_meter_name);
}

/**
 * Reads out the peak volume of the left channel.
 * @return int - The maximum value that has been detected
 * since the last call of peak_meter_read_l. The value
 * is in the range 0 <= value < MAX_PEAK.
 */
static int peak_meter_read_l (void) {
    int retval = peak_meter_l;
    peak_meter_l = mas_codec_readreg(0xC);
    return retval;
}

/**
 * Reads out the peak volume of the right channel.
 * @return int - The maximum value that has been detected
 * since the last call of peak_meter_read_l. The value
 * is in the range 0 <= value < MAX_PEAK.
 */
static int peak_meter_read_r (void) {
    int retval = peak_meter_r;
    peak_meter_r = mas_codec_readreg(0xD);
    return retval;
}

/**
 * Reset the detected overflows. This method is for
 * use by the user interface.
 * @param int unused - This parameter was added to
 * make the function compatible with set_int
 */
void peak_meter_reset_overflow(int unused) {
    peak_meter_l_overflow = false;
    peak_meter_r_overflow = false;
}

#ifdef HAVE_LCD_BITMAP
/**
 * Draws a peak meter in the specified size at the specified position.
 * @param int x - The x coordinate. 
 *                Make sure that 0 <= x and x + width < LCD_WIDTH
 * @param int y - The y coordinate. 
 *                Make sure that 0 <= y and y + height < LCD_HEIGHT
 * @param int width - The width of the peak meter. Note that for display
 *                    of overflows a 3 pixel wide area is used ->
 *                    width > 3
 * @param int height - The height of the peak meter. height > 3
 */
void peak_meter_draw(int x, int y, int width, int height) {
    int left, right;
    int meterwidth = width - 3;
    int i;

    /* read the volume info from MAS */
    left  = peak_meter_read_l() / (MAX_PEAK / meterwidth);
    right = peak_meter_read_r() / (MAX_PEAK / meterwidth);

    /* reset max values after timeout */
    if (TIME_AFTER(current_tick, peak_meter_timeout_l)){
        peak_meter_max_l = 0;
    }

    if (TIME_AFTER(current_tick, peak_meter_timeout_r)){
        peak_meter_max_r = 0;
    }

    if (peak_meter_l_overflow && TIME_AFTER(current_tick, peak_meter_overflow_timeout_l)){
        peak_meter_l_overflow = false;
    }

    if (peak_meter_r_overflow && TIME_AFTER(current_tick, peak_meter_overflow_timeout_r)){
        peak_meter_r_overflow = false;
    }

    /* check for new max values */
    if (left > peak_meter_max_l) {
        peak_meter_max_l = left - 1;
        peak_meter_timeout_l = 
            current_tick + global_settings.peak_meter_release;
    }

    if (right > peak_meter_max_r) {
        peak_meter_max_r = right - 1;
        peak_meter_timeout_r = 
            current_tick + global_settings.peak_meter_release;
    }

    /* draw the peak meter */
    lcd_clearrect(x, y, width, height);

    /* draw left */
    lcd_fillrect (x, y, left, height / 2 -1 );
    if (peak_meter_max_l > 0) {
        lcd_drawline(x + peak_meter_max_l, y, 
                     x + peak_meter_max_l, y + height / 2 -2 );
    }
    if (peak_meter_l_overflow) {
        lcd_fillrect(x + meterwidth, y, 3, height / 2 - 1);
    }

    /* draw right */
    lcd_fillrect(x, y + height / 2, right, height / 2- 1);
    if (peak_meter_max_r > 0) {
        lcd_drawline( x + peak_meter_max_r, y + height / 2, 
                      x + peak_meter_max_r, y + height);
    }
    if (peak_meter_r_overflow) {
        lcd_fillrect(x + meterwidth, y + height / 2, 3, height / 2 - 1);
    }

    /* draw scale */
    lcd_drawline(x, y + height / 2 - 1, 
                 x + width - 1, y + height / 2 - 1);
    lcd_drawline(x + meterwidth, y,
                 x + meterwidth, y + height);
    for (i = 0; i < 10; i++) {
        lcd_invertpixel(x + meterwidth * i / 10, y + height / 2 - 1);
    }
}
#endif

