/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2002 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 <stdlib.h>
#include <sprintf.h>
#include "menu.h"
#include "lcd.h"
#include "button.h"
#include "mas.h"
#include "system.h"

/* The different drawing modes */
#define DRAW_MODE_FILLED  0
#define DRAW_MODE_OUTLINE 1
#define DRAW_MODE_PIXEL   2
#define DRAW_MODE_COUNT   3

#define MAX_PEAK 0x8000

/* number of ticks between two volume samples */
static int  speed = 1;
/* roll == true -> lcd rolls */
static bool roll = true;
/* see DRAW_MODE_XXX constants for valid values */
static int  drawMode = DRAW_MODE_FILLED;

/**
 * Displays a vertically scrolling oscillosgraph using
 * hardware scrolling of the display. The user can change
 * speed
 */
bool oscillograph(void)
{
    /* stores current volume value left */
    int  left;
    /* stores current volume value right */
    int  right;
    /* specifies the current position on the lcd */
    int  y = LCD_WIDTH - 1;

    /* only needed when drawing lines */
    int  lastLeft = 0;
    int  lastRight = 0;
    int  lasty = 0;

    bool exit = false;

    /* the main loop */
    while (!exit) {

        /* read the volume info from MAS */
        left = mas_codec_readreg(0xC) / (MAX_PEAK / (LCD_WIDTH / 2 - 2));
        right = mas_codec_readreg(0xD) / (MAX_PEAK / (LCD_WIDTH / 2 - 2));

        /* delete current line */
        lcd_clearline(0, y, LCD_WIDTH-1, y);

        switch (drawMode) {
            case DRAW_MODE_FILLED:
                lcd_drawline(LCD_WIDTH / 2 + 1        , y, 
                             LCD_WIDTH / 2 + 1 + right, y);
                lcd_drawline(LCD_WIDTH / 2 - 1        , y,
                             LCD_WIDTH / 2 - 1 -left  , y);
                break;

            case DRAW_MODE_OUTLINE:
                /* last position needed for lines */
                lasty = MAX(y-1, 0);

                /* Here real lines were neccessary because 
                   anything else was ugly. */
                lcd_drawline(LCD_WIDTH / 2 + right     , y, 
                             LCD_WIDTH / 2 + lastRight , lasty);
                lcd_drawline(LCD_WIDTH / 2 - left    , y,
                             LCD_WIDTH / 2 - lastLeft, lasty);

                /* have to store the old values for drawing lines
                   the next time */
                lastRight = right;
                lastLeft = left;
                break;

            case DRAW_MODE_PIXEL:
                /* straight and simple */
                lcd_drawpixel(LCD_WIDTH / 2 + right, y);
                lcd_drawpixel(LCD_WIDTH / 2 - left, y);
                break;
        }


        /* increment and adjust the drawing position */
        y++;
        if (y >=  LCD_HEIGHT)
            y = 0;

        /* I roll before update because otherwise the new
           line would appear at the wrong end of the display */
        if (roll)
            lcd_roll(y);

        /* now finally make the new sample visible */
        lcd_update_rect(0, MAX(y-1, 0), LCD_WIDTH, 2);

        /* There are two mechanisms to alter speed:
           1.) slowing down is achieved by increasing
               the time waiting for user input. This
               mechanism uses positive values.
           2.) speeding up is achieved by leaving out
               the user input check for (-speed) volume
               samples. For this mechanism negative values
               are used.
        */
               
        if (speed >= 0 || ((speed < 0) && (y % (-speed) == 0))) {
            bool draw = false;

            /* speed values > 0 slow the oszi down. By user input
               speed might become < 1. If a value < 1 was
               passed user input would be disabled. Thus
               it must be ensured that at least 1 is passed. */

            /* react to user input */
            switch (button_get_w_tmo(MAX(speed, 1))) {
                case BUTTON_UP:
                    speed++;
                    draw = true;
                    break;

                case BUTTON_DOWN:
                    speed--;
                    draw = true;
                    break;

                case BUTTON_PLAY:
                    /* pause the demo */
                    button_get(true);
                    break;

                case BUTTON_F1:
                    /* toggle rolling */
                    roll = !roll;
                    break;

                case BUTTON_F2:
                    /* step through the display modes */
                    drawMode ++;
                    drawMode = drawMode % DRAW_MODE_COUNT;

                    /* lcd buffer might be rolled so that
                       the transition from LCD_HEIGHT to 0 
                       takes place in the middle of the screen.
                       That produces ugly results in DRAW_MODE_OUTLINE
                       mode. If rolling is enabled this change will
                       be reverted before the next update anyway.*/
                    lcd_roll(0);
                    break;

                case BUTTON_F3:
                    speed = 1;
                    draw = true;
                    break;

                case BUTTON_OFF:
                    exit = true;
                    break;
            }

            if (draw) {
                char buf[16];
                snprintf(buf, sizeof buf, "Speed: %d", -speed);
                lcd_putsxy(0, (y + LCD_HEIGHT - 8) % LCD_HEIGHT, buf);
                lcd_update_rect(0, (y + LCD_HEIGHT - 8) % LCD_HEIGHT, 
                                LCD_WIDTH, 8);
            }
        }    
    }

    /* restore to default roll position. 
       Looks funny if you forget to do this... */
    lcd_roll(0);
    lcd_update();

    /* standard return */
    return false;
}
