//-----------------------------------------------------------------------------
// Copyright (C) Daniel Karling, 2021
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// main code for Multi Loader
//-----------------------------------------------------------------------------
#include "standalone.h" // standalone definitions
#include "proxmark3_arm.h"
#include "appmain.h"
#include "fpgaloader.h"
#include "ticks.h"
#include "util.h"
#include "dbprint.h"

/*
 * This mode will allow you to access multiple different standalone
 * modes at the same time. The below section defines which modes are
 * available. Modes are added to the mode list according to the
 * following:
 *
 *  1) Include "dankarmulti.h"
 *  2) Define MODE_NAME and MODE_FILE for a mode (MODE_NAME can
 *     of your choosing, but must be a valid C token. I.e. pretend
 *     that you are naming a variable)
 *  3) Include "dankarmulti.h" again
 *  4) Repeat steps 2 and 3 for additional modes.
 *  5) Use the macros START_MODE_LIST, ADD_MODE and END_MODE_LIST
 *     to create the list of modes. You need to use the same names
 *     here as defined earlier.
 *
 *  How this works:
 *  The basic idea is to simply include several c-files with additional
 *  modes into this file. Hopefully the only collision of symbols are
 *  the RunMod and ModInfo, and these are solved by dankarmulti.h.
 *
 *  First, dankarmulti.h is included once by itself to define some macros
 *  used later.
 *
 *  For each mode to be included we define MODE_NAME which is a unique
 *  name to give the mode and MODE_FILE which is the name of the C-file
 *  for the mode. dankarmulti.h will make sure that RunMod and
 *  ModInfo is renamed to RunMod_NAME where name is what we defined. It
 *  will also include the actual mode source code and create a struct
 *  with function pointer to the run and info functions of the mode.
 *  At the end of dankarmulti.h it does #undef on MODE_NAME and MODE_FILE
 *  so that they can be redefined for the next mode to include.
 *
 *  To create a list of the modes we now have available, it is necessary
 *  to use the START_MODE_LIST, ADD_MODE and END_MODE_LIST macros. This
 *  could also have been done with some linker magic and a new section,
 *  or by some other dirty hack. But this works for now.
 *
 *  Usage:
 *  Single press to cycle between the modes.
 *  The LEDs will show the currently selected mode.
 *  Hold button to start the selected mode.
 *
 *  How many modes can you have at the same time? Depends on memory,
 *  but the LEDs will overflow after 15.
 *
 *  I don't know if this works with all the different standalone modes.
 *  I would imagine that it doesn't. If two modes are included with
 *  functions that collide (name-wise) there will be issues.
 *
 *  NOTE: You will have to keep track of if the included modes require
 *  external memory or bluetooth yourself. The mode selection in
 *  the Makefiles is not able to do it.
 */


/*******************
 * Begin mode list *
 *******************/

#include "dankarmulti.h"

#define MODE_NAME mattyrun
#define MODE_FILE "hf_mattyrun.c"
#include "dankarmulti.h"
#define MODE_NAME em4100
#define MODE_FILE "lf_em4100rswb.c"
#include "dankarmulti.h"
#define MODE_NAME tcprst
#define MODE_FILE "hf_tcprst.c"
#include "dankarmulti.h"

START_MODE_LIST
ADD_MODE(mattyrun)
ADD_MODE(em4100)
ADD_MODE(tcprst)
END_MODE_LIST

/*******************
 *  End mode list  *
 *******************/

void ModInfo(void) {
    DbpString("  Multi standalone loader aka dankarmulti (Daniel Karling)");
}

static void update_mode(int selected) {
    if (selected >= NUM_MODES) {
        SpinDown(100);
        Dbprintf("Invalid mode selected");
        LEDsoff();
    } else {
        Dbprintf("Selected mode: '%s'", mode_list[selected]->name);
        LEDsoff();
        LED(selected + 1, 0);
    }
}


void RunMod(void) {
    int selected_mode = 0;

    StandAloneMode();
    Dbprintf("[=] Multi standalone loader aka dankarmulti (Daniel Karling)");
    Dbprintf("[=] Available modes:");

    for (int i = 0; i < NUM_MODES; i++) {
        Dbprintf("[=]   '%s'", mode_list[i]->name);
    }

    update_mode(selected_mode);

    // the main loop for your standalone mode
    for (;;) {
        WDT_HIT();

        // exit from RunMod,   send a usbcommand.
        if (data_available()) break;

        // Was our button held down or pressed?
        int button_pressed = BUTTON_HELD(1000);

        switch (button_pressed) {
            case BUTTON_SINGLE_CLICK:
                selected_mode = (selected_mode + 1) % NUM_MODES;
                update_mode(selected_mode);
                SpinDelay(300);
                break;
            case BUTTON_HOLD:
                Dbprintf("Starting selected mode ('%s')", mode_list[selected_mode]->name);
                mode_list[selected_mode]->run();
                Dbprintf("Exited from selected mode");
                return;
            default:
                break;
        }
    }

    DbpString("[=] exiting");
    LEDsoff();
}
