/***************************************************************************
 *             __________               __   ___.
 *   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 <string.h>
#include "key_scheme.h"
#include "button.h"
#include "commands.h"
#include "lcd.h"

struct key_map key_map_store[KEY_MAP_STORE_SIZE ];

struct key_scheme key_scheme_store[KEY_SCHEME_STORE_SIZE ];

int key_scheme_current = SCHEME_DEFAULT;

/**
 * executes the command stored in the map
 */
void execute(struct key_map *map) {
    int value = map->value;
    char *text = map->text;
    struct key_command *command = map->command;

    /* an empty command might have specified in order to only
       change the current key scheme. In that case we don't do
       anything */
    if (command != NULL){
        switch (map->cmd_use) {
            case CMD_ACTION:
                if (command->action != NULL) {
                    command->action();
                }
                break;

            case CMD_VALUE_SET:
                if (command->set_value != NULL) {
                    command->set_value(value);
                }
                break;

            case CMD_VALUE_INC:
                if (command->get_value != NULL && command->set_value != NULL) {
                    value = command->get_value();
                    value ++;
                    command->set_value(value);
                }
                break;

            case CMD_VALUE_DEC:
                if (command->get_value != NULL && command->set_value != NULL) {
                    value = command->get_value();
                    value --;
                    command->set_value(value);
                }
                break;

            case CMD_TEXT_SET:
                if (command->set_text != NULL) {
                    command->set_text(text);
                }
                break;

            case CMD_TEXT_EDIT:
                /* not available yet */
                /* command->set_text(text_editor()); */
                break;
        }
    }
}

/**
 * Use the scheme and execute the command that is mapped 
 * to the given button event.
 * @param int button - A value as received from button_get
 * @param int scheme - A valid index for key_scheme_store
 * @return int - The index of the key scheme that must be
 * used after the execution of the command. If no command
 * was assigned to the button scheme is returned.
 */
int select_and_execute(int button, int scheme){

    int retval = scheme;
    if (scheme != SCHEME_EMPTY && button != BUTTON_NONE) {
        int i;

        /* search the current key scheme for the specified
           button */
        bool button_found = false;
        for (i = key_scheme_store[scheme].start; 
            i < key_scheme_store[scheme].end; i++){
            if (button == key_map_store[i].button_event) {

                retval = key_map_store[i].next_key_scheme;
                execute(&key_map_store[i]);
                button_found = true;

                /* don't return here because servaral commands
                   may have been bound to the same key in order
                   to generate a macro. We only want to consider
                   siblings because otherwise we would execute all
                   inherited assignments instead of overwriting them.
                */
            }
        }

        /* nothing found? try inherited */
        /* This is a recursion that should be transformed to an
           iteration in real life. Maybe we want to consider checking
           for cycles*/
        if (!button_found && key_scheme_store[scheme].parent != SCHEME_EMPTY) {
            select_and_execute(button, key_scheme_store[scheme].parent);
        }
    }
    return retval;
}


static void clear_map(void) {
    int i;

    for (i = 0; i < KEY_MAP_STORE_SIZE; i++) {
        key_map_store[i].button_event = BUTTON_NONE;
        key_map_store[i].cmd_use = -1;
        key_map_store[i].value = -1;
        key_map_store[i].next_key_scheme = SCHEME_DEFAULT;
        key_map_store[i].text = NULL;
        key_map_store[i].command = NULL;
    }

}

/**
 * Here the factory preset is defined
 */
static void default_scheme(void) {
    int scheme;
    int map_idx = 0;

    button_set_release(ALL_BUTTONS);
    button_set_repeat(ALL_BUTTONS);

    init_commands();
    clear_map();
    for (scheme = 0; scheme < KEY_SCHEME_STORE_SIZE; scheme++){
        key_scheme_store[scheme].parent = SCHEME_EMPTY;
        key_scheme_store[scheme].start = map_idx;
        key_scheme_store[scheme].end = map_idx;
    }

    /* The default key scheme */
    scheme = SCHEME_DEFAULT;
    key_scheme_store[scheme].parent = SCHEME_EMPTY;
    key_scheme_store[scheme].start = map_idx;

    /* demonstrate the decrease command */
    key_map_store[map_idx].button_event = BUTTON_LEFT;
    key_map_store[map_idx].cmd_use = CMD_VALUE_DEC;
    key_map_store[map_idx].value = -1;
    key_map_store[map_idx].next_key_scheme = SCHEME_DEFAULT;
    key_map_store[map_idx].text = NULL;
    key_map_store[map_idx].command = &key_command_table[SPLIT_X];
    map_idx++;

    /* demonstrate repeat. In a config file we might want to have
       different entries for the button name and its modifiers. That
       might make the file format more user friendly. 
       We can combine that to one single button event number while
       parsing */
    key_map_store[map_idx].button_event = BUTTON_LEFT | BUTTON_REPEAT;
    key_map_store[map_idx].cmd_use = CMD_VALUE_DEC;
    key_map_store[map_idx].value = -1;
    key_map_store[map_idx].next_key_scheme = SCHEME_DEFAULT;
    key_map_store[map_idx].text = NULL;
    key_map_store[map_idx].command = &key_command_table[SPLIT_X];
    map_idx++;

    /* demonstrate the increase command */
    key_map_store[map_idx].button_event = BUTTON_RIGHT;
    key_map_store[map_idx].cmd_use = CMD_VALUE_INC;
    key_map_store[map_idx].value = -1;
    key_map_store[map_idx].next_key_scheme = SCHEME_DEFAULT;
    key_map_store[map_idx].text = NULL;
    key_map_store[map_idx].command = &key_command_table[SPLIT_X];
    map_idx++;

    key_map_store[map_idx].button_event = BUTTON_RIGHT | BUTTON_REPEAT;
    key_map_store[map_idx].cmd_use = CMD_VALUE_INC;
    key_map_store[map_idx].value = -1;
    key_map_store[map_idx].next_key_scheme = SCHEME_DEFAULT;
    key_map_store[map_idx].text = NULL;
    key_map_store[map_idx].command = &key_command_table[SPLIT_X];
    map_idx++;

    /* demonstrate the action command */
    key_map_store[map_idx].button_event = BUTTON_UP;
    key_map_store[map_idx].cmd_use = CMD_ACTION;
    key_map_store[map_idx].value = -1;
    key_map_store[map_idx].next_key_scheme = SCHEME_DEFAULT;
    key_map_store[map_idx].text = NULL;
    key_map_store[map_idx].command = &key_command_table[SPLIT_ZOOM_IN];
    map_idx++;

    key_map_store[map_idx].button_event = BUTTON_DOWN;
    key_map_store[map_idx].cmd_use = CMD_ACTION;
    key_map_store[map_idx].value = -1;
    key_map_store[map_idx].next_key_scheme = SCHEME_DEFAULT;
    key_map_store[map_idx].text = NULL;
    key_map_store[map_idx].command = &key_command_table[SPLIT_ZOOM_OUT];
    map_idx++;

    key_map_store[map_idx].button_event = BUTTON_F2;
    key_map_store[map_idx].cmd_use = CMD_VALUE_INC;
    key_map_store[map_idx].value = -1;
    key_map_store[map_idx].next_key_scheme = SCHEME_DEFAULT;
    key_map_store[map_idx].text = NULL;
    key_map_store[map_idx].command = &key_command_table[SPLIT_LOOP_MODE];
    map_idx++;

    /* test scheme change 
       The command itself pauses the engine. We want the pause button
       to resume playback. But we cannot use the mpeg_pause command for
       that. Thus we must switch to another key scheme where the pause
       key is assigned to mpeg_resume
    */
    key_map_store[map_idx].button_event = BUTTON_PLAY | BUTTON_REL;
    key_map_store[map_idx].cmd_use = CMD_ACTION;
    key_map_store[map_idx].value = -1;
    key_map_store[map_idx].next_key_scheme = SCHEME_DEFAULT + 1; /* switch scheme */
    key_map_store[map_idx].text = NULL;
    key_map_store[map_idx].command = &key_command_table[MPEG_PAUSE];
    map_idx++;

    /* test button combinations */
    /* test setting a value directly. Note that this feature 
       enables the user to specify the value in the config file 
     */
    key_map_store[map_idx].button_event = BUTTON_ON | BUTTON_LEFT;
    key_map_store[map_idx].cmd_use = CMD_VALUE_SET;
    key_map_store[map_idx].value = 50;
    key_map_store[map_idx].next_key_scheme = SCHEME_DEFAULT;
    key_map_store[map_idx].text = NULL;
    key_map_store[map_idx].command = &key_command_table[MPEG_SET_PITCH];
    map_idx++;
    
    key_map_store[map_idx].button_event = BUTTON_ON | BUTTON_RIGHT;
    key_map_store[map_idx].cmd_use = CMD_VALUE_SET;
    key_map_store[map_idx].value = 150;
    key_map_store[map_idx].next_key_scheme = SCHEME_DEFAULT;
    key_map_store[map_idx].text = NULL;
    key_map_store[map_idx].command = &key_command_table[MPEG_SET_PITCH];
    map_idx++;
    
    /* quit the split editor */
    key_map_store[map_idx].button_event = BUTTON_OFF;
    key_map_store[map_idx].cmd_use = CMD_ACTION;
    key_map_store[map_idx].value = 150;
    key_map_store[map_idx].next_key_scheme = SCHEME_DEFAULT;
    key_map_store[map_idx].text = NULL;
    key_map_store[map_idx].command = &key_command_table[SPLIT_QUIT];
    map_idx++;

    /* demonstrate flexibility: by simply changing the key schemes we
       can have the play button assigned to three different purposes:
       1. pause
       2. resume
       3. reset pitch (when play is pressed and held)
       We need a third key scheme for this purpose because we need a key
       scheme that doesn't pause or resume playback when the play button
       is released.
    */
    key_map_store[map_idx].button_event = BUTTON_PLAY | BUTTON_REPEAT;
    key_map_store[map_idx].cmd_use = CMD_VALUE_SET;
    key_map_store[map_idx].value = 100;
    key_map_store[map_idx].next_key_scheme = SCHEME_DEFAULT + 2; /* key scheme change */
    key_map_store[map_idx].text = NULL;
    key_map_store[map_idx].command = &key_command_table[MPEG_SET_PITCH];
    map_idx++;

    /* the default key scheme ends */
    key_scheme_store[scheme].end = map_idx;

    scheme++;

    /* test inheritance
       This scheme is used when the user has entered pause mode in the split editor.
       It inherits everything from the default scheme but overwrites the functionality
       of the play button. Now the play button is assigned to resume.
     */
    key_scheme_store[scheme].parent = SCHEME_DEFAULT;
    key_scheme_store[scheme].start = map_idx;

    key_map_store[map_idx].button_event = BUTTON_PLAY | BUTTON_REL;
    key_map_store[map_idx].cmd_use = CMD_ACTION;
    key_map_store[map_idx].value = -1;
    key_map_store[map_idx].next_key_scheme = SCHEME_DEFAULT; /* switch back to default */
    key_map_store[map_idx].text = NULL;
    key_map_store[map_idx].command = &key_command_table[MPEG_RESUME];
    map_idx++;

    key_scheme_store[scheme].end = map_idx;
    /* End of key scheme 1. Note that it has only one button assignment */

    scheme++;

    /* test bypass 
       This key scheme doesn't inherit anything. It's only purpose is to
       switch back to the default scheme when the play button is released.
     */
    key_scheme_store[scheme].parent = SCHEME_EMPTY; /* don't inherit */
    key_scheme_store[scheme].start = map_idx;

    key_map_store[map_idx].button_event = BUTTON_PLAY | BUTTON_REL;
    key_map_store[map_idx].cmd_use = CMD_ACTION;
    key_map_store[map_idx].value = -1;
    key_map_store[map_idx].next_key_scheme = SCHEME_DEFAULT; /* change to default */
    key_map_store[map_idx].text = NULL;
    key_map_store[map_idx].command = NULL;//&key_command_table[MPEG_SET_PITCH];
    map_idx++;

    key_scheme_store[scheme].end = map_idx;

    scheme++;

    /* enable the default key scheme */
    key_scheme_current = SCHEME_DEFAULT;
}

void load_scheme(char *filename){
    (void)filename;
    default_scheme();
}

