/**
 * MechBase PLC — Ladder Logic Interpreter v2
 * 
 * IEC 61131-3 compliant ladder logic interpreter for ESP32.
 * 
 * Instructions:
 *   Contacts:  NO, NC, Rising Edge, Falling Edge
 *   Outputs:   OTE (Energize), OTL (Latch), OTU (Unlatch)
 *   Timers:    TON, TOF, TP — with EN, TT, DN status bits
 *   Structure: Parallel branching, series/parallel networks
 */

#ifndef LADDER_INTERPRETER_H
#define LADDER_INTERPRETER_H

#include <Arduino.h>
#include <stdint.h>
#include <stdbool.h>
#include <vector>
#include <string>

// ─────────────────────────────────────────────────────────────────────────────
// Limits
// ─────────────────────────────────────────────────────────────────────────────
#define MAX_DIGITAL_INPUTS    16
#define MAX_DIGITAL_OUTPUTS   16
#define MAX_TIMER_COUNT       16
#define MAX_COUNTER_COUNT     16
#define MAX_INT_REGISTERS     32
#define MAX_BOOL_REGISTERS    64
#define MAX_NETWORKS          64
#define MAX_BRANCHES          16
#define MAX_ELEMENTS          32

// ─────────────────────────────────────────────────────────────────────────────
// Contact types
// ─────────────────────────────────────────────────────────────────────────────
enum ContactType : uint8_t {
    CT_NONE         = 0,
    CT_NO           = 1,  // Normally Open   --| |--
    CT_NC           = 2,  // Normally Closed --|/|--
    CT_RISING       = 3,  // Rising Edge     --|P|--
    CT_FALLING      = 4,  // Falling Edge    --|N|--
    CT_TON_DN       = 5,  // TON Done bit
    CT_TON_TT       = 6,  // TON Timing bit
    CT_TON_EN       = 7,  // TON Enable bit
    CT_TOF_DN       = 8,  // TOF Done bit
    CT_TOF_TT       = 9,  // TOF Timing bit
    CT_TOF_EN       = 10, // TOF Enable bit
    CT_TP_DN        = 11, // TP Done bit
    CT_TP_TT        = 12, // TP Timing bit
    CT_TP_EN        = 13, // TP Enable bit
    CT_INT_GT       = 14, // Integer Greater Than
    CT_INT_LT       = 15, // Integer Less Than
    CT_INT_EQ       = 16, // Integer Equal
    CT_INT_GTE      = 17, // Integer >=
    CT_INT_LTE      = 18, // Integer <=
};

// ─────────────────────────────────────────────────────────────────────────────
// Output types
// ─────────────────────────────────────────────────────────────────────────────
enum OutputType : uint8_t {
    OT_NONE   = 0,
    OT_OTE    = 1,  // Output Energize   --( )--
    OT_OTL    = 2,  // Output Latch      --(L)--  SET
    OT_OTU    = 3,  // Output Unlatch    --(U)--  RESET
    OT_TON    = 4,  // Timer On-Delay
    OT_TOF    = 5,  // Timer Off-Delay
    OT_TP     = 6,  // Timer Pulse
    OT_MVI    = 7,  // Move Integer
    OT_ADD    = 8,  // Add Integer
    OT_SUB    = 9,  // Subtract Integer
};

// ─────────────────────────────────────────────────────────────────────────────
// Timer struct with all status bits
// ─────────────────────────────────────────────────────────────────────────────
struct LadderTimer {
    uint8_t  id;           // Timer index
    uint16_t preset;       // Preset in milliseconds
    uint32_t elapsed;      // Accumulated time
    bool     en;           // Enable — controlling rung has power
    bool     tt;           // Timing — timer is currently running
    bool     dn;           // Done — elapsed >= preset

    void reset() {
        elapsed = 0;
        tt = false;
        dn = false;
    }

    void update(uint32_t current_ms, bool enabled, uint8_t timer_type) {
        en = enabled;

        if (timer_type == OT_TON) {
            // TON: starts timing when enabled, done when elapsed >= preset
            if (enabled) {
                if (elapsed < preset) {
                    elapsed = min(elapsed + 100, preset + 1); // 100ms scan
                    tt = true;
                }
                dn = (elapsed >= preset);
            } else {
                elapsed = 0;
                tt = false;
                dn = false;
            }
        }
        else if (timer_type == OT_TOF) {
            // TOF: starts timing when DISABLED, done when elapsed >= preset
            if (enabled) {
                elapsed = 0;
                tt = false;
                dn = true;
            } else {
                if (elapsed < preset) {
                    elapsed = min(elapsed + 100, preset + 1);
                    tt = true;
                }
                dn = (elapsed >= preset);
            }
        }
        else if (timer_type == OT_TP) {
            // TP: pulses for preset duration on rising edge
            static bool last_en = false;
            bool rising_edge = enabled && !last_en;
            last_en = enabled;

            if (rising_edge || (enabled && elapsed < preset)) {
                if (elapsed < preset) {
                    elapsed = min(elapsed + 100, preset + 1);
                    tt = true;
                    dn = (elapsed >= preset);
                }
            } else {
                elapsed = 0;
                tt = false;
                dn = false;
            }
        }
    }
};

// ─────────────────────────────────────────────────────────────────────────────
// Counter struct
// ─────────────────────────────────────────────────────────────────────────────
struct LadderCounter {
    uint8_t   id;
    int16_t   preset;
    int16_t   count;
    bool      cu;     // Count Up input
    bool      cd;     // Count Down input
    bool      reset;  // Reset input
    bool      ov;     // Overflow
    bool      un;     // Underflow
    bool      dn;     // Done (count >= preset)

    void update() {
        if (reset) {
            count = 0;
            ov = false;
            un = false;
        } else {
            if (cu) count++;
            if (cd) count--;
        }
        ov = (count > 32767);
        un = (count < -32768);
        dn = (count >= preset);
    }
};

// ─────────────────────────────────────────────────────────────────────────────
// Element — a single contact or output in a network
// ─────────────────────────────────────────────────────────────────────────────
struct Element {
    uint8_t type;       // ContactType or OutputType
    char    tag[16];    // Tag name (e.g., "I0", "Q0", "T0", "M10", "INT0")
    int16_t value;      // Preset / compare value
    uint8_t src_reg;    // Source register index (for math/move)
    uint8_t dst_reg;    // Destination register index (for math/move)
    bool    last_state; // For edge detection
    bool    result;     // Evaluation result
};

// ─────────────────────────────────────────────────────────────────────────────
// Branch — parallel group of elements
// ─────────────────────────────────────────────────────────────────────────────
struct Branch {
    std::vector<Element> elements;  // Series elements in this branch
    bool power_flow;                 // Result of this branch

    bool evaluate(LadderInterpreter* interp, bool is_output = false);
};

// ─────────────────────────────────────────────────────────────────────────────
// Network — a ladder rung with optional parallel branches
// ─────────────────────────────────────────────────────────────────────────────
struct Network {
    uint16_t number;
    std::vector<Branch> input_branches;   // Input-side parallel branches
    std::vector<Branch> output_branches;  // Output-side parallel branches
    bool power_flow;

    bool evaluate(LadderInterpreter* interp);
};

// ─────────────────────────────────────────────────────────────────────────────
// Main interpreter
// ─────────────────────────────────────────────────────────────────────────────
class LadderInterpreter {
private:
    bool    di[MAX_DIGITAL_INPUTS];    // Digital inputs
    bool    do_[MAX_DIGITAL_OUTPUTS];  // Digital outputs
    bool    do_prev[MAX_DIGITAL_OUTPUTS];

    bool    m_bool[MAX_BOOL_REGISTERS]; // Boolean memory M0..M63
    int16_t m_int[MAX_INT_REGISTERS];   // Integer memory INT0..INT31

    LadderTimer timers[MAX_TIMER_COUNT];
    LadderCounter counters[MAX_COUNTER_COUNT];

    std::vector<Network> networks;
    bool running;
    bool simulating;
    uint32_t scan_start;

public:
    LadderInterpreter();
    ~LadderInterpreter();

    // ── Lifecycle ────────────────────────────────────────────────────────
    void begin();
    void scan();
    void run();
    void stop();

    // ── I/O ──────────────────────────────────────────────────────────────
    void setDI(uint8_t idx, bool val);
    bool getDI(uint8_t idx);
    bool getDO(uint8_t idx);
    void setDO(uint8_t idx, bool val);

    // ── Memory ───────────────────────────────────────────────────────────
    void setM(bool idx, bool val);
    bool getM(bool idx);
    void setINT(uint8_t idx, int16_t val);
    int16_t getINT(uint8_t idx);

    // ── Timer access ─────────────────────────────────────────────────────
    LadderTimer& getTimer(uint8_t idx);
    void resetTimer(uint8_t idx);

    // ── Counter access ───────────────────────────────────────────────────
    LadderCounter& getCounter(uint8_t idx);
    void resetCounter(uint8_t idx);

    // ── Program loading ──────────────────────────────────────────────────
    Network& addNetwork(uint16_t num);
    void loadFromJSON(const char* json);

    // ── Helpers ──────────────────────────────────────────────────────────
    bool readBool(const char* tag);
    void writeBool(const char* tag, bool val);
    int16_t readInt(const char* tag);
    void writeInt(const char* tag, int16_t val);
    uint8_t parseTimerIndex(const char* tag);
    uint8_t parseCounterIndex(const char* tag);

    // ── Debug ────────────────────────────────────────────────────────────
    void dumpState();
    bool isRunning() { return running; }
    uint32_t getScanTime() { return millis() - scan_start; }

    // ── Simulation ───────────────────────────────────────────────────────
    void enableSimulation();
    void disableSimulation();
    bool getSimulation();
};

#endif // LADDER_INTERPRETER_H