/**********************************************************************\
 * Kurzbeschreibung: automat.c
 * Stellt Funktionen zur Realisierung eines Automaten zur Verfügung,
 * die über die in der automat.h vorgegebene C-Schnittstelle
 * mit einer grafischen Schnittstelle kommunizieren.
 *
 * Datum:       Autor:        Grund der Aenderung:
 *
 *
\**********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "automat.h"

/*--- #defines -------------------------------------------------------*/
/* Macro zur Ermittlung der Array Groesse */
#define SIZE_OF(a) (sizeof((a))/ sizeof(*(a)))

#define NR_OF_STATES        (I-A + 1)

/*--- Datentypen (typedef) -------------------------------------------*/
/* Definition der Zustaende */
typedef enum {A=0,B,C,D,E,F,G,H,I} state_t;

/* Definition der fsm-spezifischen Variablen */
/* Struktur zur Beschreibung eines Uebergangs mit "Don't Care"-Moeglichkeit */
typedef struct {
    int       input;
    int       mask;
    state_t   nxtstate;
} fsm_state_t;

/* Struktur zur Beschreibung aller Uebergaenge eines Zustands */
typedef struct {
    const fsm_state_t * transitions;
    int                 nrOfTransitions;
    state_t             defaultNxtState;
} fsm_full_state_t;

/*--- Modulglobale static Variablen ----------------------------------*/
/* Definition der Zustandsvariablen */
static state_t s_curstate = A; /* Initialisierung */

/* Definition aller Zustandsuebergaenge fuer jeden Zustand */
static const fsm_state_t s_transitions_A[] =
{ /*    input   mask      nxtstate */
  // Wenn 1?-Münze eingeworfen (muenze=1, muenz_wert=0), egal ob Becher steht oder nicht (weil mask ~0x004, also becher-Bit egal)
  {     0x002,  ~0x004,   B }, // A -> B: 1? eingeworfen
  // Wenn 2?-Münze eingeworfen (muenze=1, muenz_wert=1), egal ob Becher steht oder nicht
  {     0x003,  ~0x004,   C }  // A -> C: 2? eingeworfen
};

static const fsm_state_t s_transitions_B[] =
{ /*    input   mask      nxtstate */
  // Nochmal 1? eingeworfen (muenze=1, muenz_wert=0), becher egal
  {     0x002,  ~0x004,   C }, // B -> C: 1? dazu, jetzt 2?
  // 2? eingeworfen (muenze=1, muenz_wert=1), becher egal
  {     0x003,  ~0x004,   D }  // B -> D: 2? dazu, jetzt 3?
};

static const fsm_state_t s_transitions_C[] =
{ /*    input   mask      nxtstate */
  // Nochmal 1? eingeworfen (muenze=1, muenz_wert=0), becher egal
  {     0x002,  ~0x004,   D }, // C -> D: 1? dazu, jetzt 3?
  // Becher steht (becher=1), egal was mit Münze, weil mask ~0x000 (alle Bits relevant)
  {     0x003,  ~0x000,   F }, // C -> F: Becher untergestellt (becher=1, muenze=1, muenz_wert=1)
  // Becher steht (becher=1), muenze=1, muenz_wert=1 (also 2?), mask ~0x000
  {     0x007,  ~0x000,   H }  // C -> H: Becher steht, 2? eingeworfen
};

static const fsm_state_t s_transitions_D[] =
{ /*    input   mask      nxtstate */
  // Becher steht (becher=1), egal was mit Münze, mask ~0x000
  {     0x002,  ~0x000,   F }, // D -> F: Becher untergestellt
  // 2? eingeworfen (muenze=1, muenz_wert=1), becher egal
  {     0x003,  ~0x004,   E }, // D -> E: 2? dazu, jetzt 5? (Überzahlung)
  // Becher steht (becher=1), muenze=1, muenz_wert=0 (1?), mask ~0x000
  {     0x006,  ~0x000,   H }  // D -> H: Becher steht, 1? eingeworfen
};

static const fsm_state_t s_transitions_E[] =
{
    // Becher steht (becher=1), egal was mit Münze
    {     0x002,  ~0x000,   F }, // E -> F: Becher untergestellt
    // 2? eingeworfen, becher egal
    {     0x000,  ~0x005,   D }, // E bleibt E: weitere Überzahlung
    // Becher steht (becher=1), muenze=1, muenz_wert=0 (1?)
    {     0x006,  ~0x000,   H }  // E -> H: Becher steht, 1? eingeworfen
};

static const fsm_state_t s_transitions_F[] =
{
    // Becher steht, 1? eingeworfen (becher=1, muenze=1, muenz_wert=0), mask ~0x001 (muenz_wert egal)
    {     0x002,  ~0x001,   G }, // F -> G: Becher steht, Münze eingeworfen
    // Becher steht, 2? eingeworfen (becher=1, muenze=0, muenz_wert=1)
    {     0x004,  ~0x001,   H }, // F -> H: Becher steht, 2? eingeworfen
    // Becher steht, 1? eingeworfen (becher=1, muenze=1, muenz_wert=0)
    {     0x006,  ~0x001,   I }  // F -> I: Becher steht, 1? eingeworfen
};

static const fsm_state_t s_transitions_G[] =
{
    // Becher steht, 1? eingeworfen
    {     0x000,  ~0x001,   F }, // G bleibt G: Becher steht, Münze eingeworfen
    // Becher steht, 2? eingeworfen
    {     0x004,  ~0x001,   H }, // G -> H: Becher steht, 2? eingeworfen
    // Becher steht, 1? eingeworfen
    {     0x006,  ~0x001,   I }  // G -> I: Becher steht, 1? eingeworfen
};

static const fsm_state_t s_transitions_H[] =
{
    // Kein Becher, keine Münze, kein Wert (alles 0)
    {     0x000,  ~0x001,   A }, // H -> A: Reset, alles leer
    // Becher steht, keine Münze, kein Wert
    {     0x002,  ~0x000,   B }, // H -> B: Becher steht, 1? eingeworfen
    // Becher steht, 2? eingeworfen
    {     0x003,  ~0x000,   C }, // H -> C: Becher steht, 2? eingeworfen
    // Becher steht, 1? eingeworfen
    {     0x006,  ~0x001,   I }  // H -> I: Becher steht, 1? eingeworfen
};

static const fsm_state_t s_transitions_I[] =
 {   // Kein Becher, keine Münze, kein Wert (alles 0)
    {     0x000,  ~0x001,   A }, // I -> A: Reset, alles leer
    // Becher steht, keine Münze, kein Wert
    {     0x002,  ~0x000,   B }, // I -> B: Becher steht, 1? eingeworfen
    // Becher steht, 2? eingeworfen
    {     0x003,  ~0x000,   C }, // I -> C: Becher steht, 2? eingeworfen
    // Becher steht, 1? eingeworfen
    {     0x004,  ~0x001,    H}  // I bleibt I: Becher steht, 1? eingeworfen
};

/* Definition der Uebergangstabelle */
/* Die Reihenfolge der Zustaende in der enum Definition muss der
 * Reihenfolge der Zustaende in der Uebergangstabelle entsprechen
 *                 [Zeile] [Spalte] */
static const fsm_full_state_t s_state_table[NR_OF_STATES] =
{
    /* transitions     nrOfTransitions           defaultNxtState */
    { s_transitions_A, SIZE_OF(s_transitions_A), A },
    { s_transitions_B, SIZE_OF(s_transitions_B), B },
    { s_transitions_C, SIZE_OF(s_transitions_C), C },
    { s_transitions_D, SIZE_OF(s_transitions_D), D },

    /* TODO   */

    { s_transitions_E, SIZE_OF(s_transitions_E), E },
    { s_transitions_F, SIZE_OF(s_transitions_F), F },
    { s_transitions_G, SIZE_OF(s_transitions_G), G },
    { s_transitions_H, SIZE_OF(s_transitions_H), H },
    { s_transitions_I, SIZE_OF(s_transitions_I), I }
    };

/* Definition der Ausgaenge */
static const fsm_action_t s_out_table[NR_OF_STATES] =
{
    /* display    muenz_rueck   kaffee_los     guthaben,   display_string */
    {   false,       false,       false,          0,          "Warten" },    /* state A */
    {   false,       false,       false,          1,          "1 Euro" },    /* state B */
    {   false,       false,       false,          2,          "2 Euro" },    /* state C */
    {   false,       false,       false,          3,          "3 Euro" },     /* state D */

    /* TODO   */

    {   false,       true,        false,          3,          "3 Euro + M" },                 /* state E */
    {   true,        false,       false,          4,          "4 Euro" },    /* state F */
    {   true,        true,        false,          4,          "4 Euro + M" },    /* state G */
    {   true,        false,       true,           0,          "Kaffe kommt" },            /* state H */
    {   true,        true,        true,           0,          "Kaffe + M" }             /* state I */

};

/*--- Funktionsdefinition --------------------------------------------*/
void automat_reset(void)
{
    printf("---- automat_reset ----\n");

    /* TODO go into IDLE state */

    s_curstate = A;

}

/*--- Funktionsdefinition --------------------------------------------*/
void automat_transition(BOOL becher, BOOL muenze, BOOL muenz_wert)
{
    // Ausgabe der aktuellen Eingabewerte zur Fehlersuche
    printf("---- automat_transition becher(%0d) muenze(%0d) muenz_wert(%0d) ----\n",
           becher, muenze, muenz_wert);

    // Kombiniere die Eingabewerte zu einem einzigen Integer mittels Bitoperationen.
    // Die Eingaben werden wie folgt kombiniert:
    // - 'becher' wird um 2 Bits nach links verschoben (belegt die dritte Bitposition)
    // - 'muenze' wird um 1 Bit nach links verschoben (belegt die zweite Bitposition)
    // - 'muenz_wert' bleibt in der ersten Bitposition
    // Dies ergibt einen 3-Bit-Integer, der den aktuellen Eingabestatus darstellt.
    int in = (becher << 2) | (muenze << 1) | (muenz_wert << 0);

    // Hole die Übergangsinformationen des aktuellen Zustands aus der Zustandstabelle.
    // 's_curstate' enthält den aktuellen Zustand, und 's_state_table' liefert die
    // Übergangsdetails für jeden Zustand.
    const fsm_full_state_t * full_transition = &s_state_table[s_curstate];

    // Hole das Array der Übergänge für den aktuellen Zustand.
    // Jeder Übergang spezifiziert eine Eingabebedingung, eine Maske und einen Folgezustand.
    const fsm_state_t * transition = full_transition->transitions;
	int i;

    // Iteriere über alle definierten Übergänge für den aktuellen Zustand.
    for(i = 0; i < full_transition->nrOfTransitions; i++)
    {
        // Überprüfe, ob die aktuelle Eingabe die Übergangsbedingung erfüllt.
        // Die Bedingung ist erfüllt, wenn die Eingabe, maskiert durch die Übergangsmaske,
        // dem spezifizierten Eingabewert des Übergangs entspricht.
        if(transition[i].input == (in & transition[i].mask))
        {
            // Wenn ein passender Übergang gefunden wird, aktualisiere den aktuellen Zustand
            // auf den im Übergang spezifizierten Folgezustand.
            s_curstate = transition[i].nxtstate;
            // Beende die Schleife, da der Übergang erfolgreich angewendet wurde.
            break;
        }
    }

    // Wenn nach Überprüfung aller Übergänge kein passender Übergang gefunden wurde,
    // setze den aktuellen Zustand auf den Standard-Folgezustand für den aktuellen Zustand.
    // Dies behandelt Fälle, in denen die Eingabe keinem spezifischen Übergang entspricht.
    if(i == full_transition->nrOfTransitions)
    {
        s_curstate = full_transition->defaultNxtState;
    }
}
/*--- Funktionsdefinition --------------------------------------------*/
fsm_action_t automat_output(void)
{
    return s_out_table[s_curstate];
}

//gcc automat.c view.c main.c io.c checker.c -o console_automat