// Thread.h - Arduino API for OpenThread on the nRF52840's native RADIO.
//
// The OpenThread core (vendored, see ARDUINONRF-PATCH markers) runs on a
// register-level 802.15.4 driver in ot_radio_nrf52840.cpp and the platform
// backends in platform_impl.cpp. Everything is polled from process();
// sketches MUST call Thread.process() from loop() (and while waiting).
//
// Typical leader/router node:
//
//   const uint8_t key[16] = {...};
//   Thread.begin();
//   Thread.setNetwork("ArduinoNRF", 11, 0xBEEF, key);
//   Thread.start();
//   void loop() { Thread.process(); }
//
// Peripheral footprint: RADIO (exclusive with NimBLE/Zigbee/NrfRadio),
// RTC2 (ms alarm), TIMER3 (us alarm + timestamps).
#pragma once

#include <stdint.h>
#include <stddef.h>

struct otInstance;

class ThreadClass {
public:
    enum Status : int8_t {
        THREAD_OK          =  0,
        THREAD_NOT_STARTED = -2,
        THREAD_BAD_PARAM   = -3,
        THREAD_INTERNAL    = -4,
    };

    // Mirrors otDeviceRole.
    enum Role : uint8_t {
        ROLE_DISABLED = 0,
        ROLE_DETACHED = 1,
        ROLE_CHILD    = 2,
        ROLE_ROUTER   = 3,
        ROLE_LEADER   = 4,
    };

    // Bring up the platform (alarms, radio, crypto heap) and create the
    // OpenThread instance. Must be called before anything else.
    static Status begin();
    static void   end();
    static bool   isAvailable();

    // Install the operational dataset. Both nodes of a network must use the
    // same name / channel (11..26) / PAN id / 16-byte network key.
    // extendedPanId is optional (8 bytes); defaults to a fixed value.
    static Status setNetwork(const char    *networkName,
                             uint8_t        channel,
                             uint16_t       panId,
                             const uint8_t  networkKey[16],
                             const uint8_t  extendedPanId[8] = nullptr);

    // Interface up + Thread protocol start (attach or form a partition).
    static Status start();
    static Status stop();

    // -- Device link mode -----------------------------------------------------
    //
    // Default is a full router-eligible device. Call before or after start();
    // changes take effect on the next attach cycle.
    enum LinkMode : uint8_t {
        LINK_ROUTER = 0, // full Thread device, may become router/leader (default)
        LINK_MED    = 1, // minimal end device: always-on radio, never routes
        LINK_SED    = 2, // sleepy end device: radio off between data polls
    };

    // pollPeriodMs only applies to LINK_SED (how often the child polls its
    // parent for queued frames).
    static Status setLinkMode(LinkMode mode, uint32_t pollPeriodMs = 1000);

    // -- On-mesh commissioning (MeshCoP) ---------------------------------------
    //
    // Real Thread commissioning: an attached node becomes the Commissioner
    // and admits new devices by a per-device passphrase (PSKd); a factory-
    // fresh device joins with joinNetwork(pskd) - no shared network key in
    // the sketch. The DTLS / EC-JPAKE handshake runs in software and takes a
    // few seconds; keep calling process().

    static Status commissionerStart();   // requires isAttached()
    static bool   commissionerActive();
    // Admit any joiner presenting this PSKd (6..32 chars, uppercase
    // alphanumeric without I/O/Q/Z) for timeoutSec seconds.
    static Status commissionerAddJoiner(const char *pskd, uint32_t timeoutSec = 300);

    enum JoinResult : uint8_t {
        JOIN_IDLE = 0,       // joinNetwork() not called
        JOIN_IN_PROGRESS,    // discovery / DTLS handshake running
        JOIN_OK,             // joined; Thread protocol already started
        JOIN_FAILED,         // see log; call joinNetwork() to retry
    };

    // Join a network whose Commissioner has admitted this PSKd. Asynchronous:
    // poll joinResult() while calling process(). On success the dataset is
    // stored (persists in flash) and Thread is started automatically.
    static Status     joinNetwork(const char *pskd);
    static JoinResult joinResult();

    // Forget the stored network (dataset, keys, counters). Requires the
    // stack to be stopped; takes effect immediately (instance resets state).
    static Status eraseNetwork();

    // Event pump: alarms, radio events, OpenThread tasklets. Call from
    // loop() as often as possible.
    static void process();

    static Role        role();
    static const char *roleString();
    static bool        isAttached();   // child, router or leader

    // RLOC16 and leader info are handy for bring-up diagnostics.
    static uint16_t rloc16();

    // EUI-64 (factory-unique, derived from FICR.DEVICEID).
    static void getEui64(uint8_t eui[8]);

    // -- CoAP (text payloads) ------------------------------------------------
    //
    // A thin, sketch-friendly layer over the official OpenThread CoAP API.
    // Server: register handlers per URI path, then peers reach them at
    // coap://[address]:5683/path. Client: fire-and-forget POST and async GET.
    // Payloads are C strings (up to ~120 bytes). For anything richer use the
    // full otCoap API via instance().

    typedef const char *(*CoapGetHandler)();              // return the resource text
    typedef void (*CoapPostHandler)(const char *payload); // act on received text
    typedef void (*CoapReplyHandler)(const char *payload); // NULL on timeout

    static Status coapStart(uint16_t port = 5683);
    static Status onCoapGet(const char *path, CoapGetHandler handler);
    static Status onCoapPost(const char *path, CoapPostHandler handler);

    // destAddr: any IPv6 string - a node's mesh-local address, or "ff03::1"
    // to reach every node in the mesh (POST only).
    static Status coapPost(const char *destAddr, const char *path, const char *text);
    static Status coapGet(const char *destAddr, const char *path, CoapReplyHandler onReply);

    // Escape hatch: the raw otInstance for direct OpenThread API use.
    static otInstance *instance();
};

extern ThreadClass Thread;
