/**
 * @file    hardware_access.c
 * @brief   Three ways a plugin reaches the hardware: ExecSync, SubmitJob with
 *          on_complete, and GetRegSnapshot. A reading aid for the tutorial
 *          page "hardware-access", not a complete plugin: the three functions
 *          below would be wired into an IcPlugin by name (probe, poll_fn).
 *
 * @note    Kept ASCII-only on purpose (the PDK samples must survive VC6 and
 *          CP950), and C89 so it also passes the syntax-only guard.
 */

#include <string.h>

#include "nexus_plugin_api.h"

/** @brief The HostAPI table, saved by init() (not shown here). */
static const NxHostApi *s_host = (const NxHostApi *)0;

/** @brief Last bytes read by the background poll (written on the Worker). */
static NxByte s_last[4];

/**
 * @brief  Way 1 - ExecSync: blocking read, worker thread only.
 * @param[in] ctx This mount's context.
 * @return ERR_OK, or the error the hardware access returned.
 * @note   Call this from probe(), poll_fn(), a custom command handler or
 *         on_widget_event(). From init() (UI Thread) it returns
 *         ERR_WRONG_THREAD and touches nothing.
 */
NxLong hwa_read_id(NxPluginCtx ctx)
{
    NxJobRequest req;
    NxByte       id;

    memset(&req, 0, sizeof(req));            /* zeros = this mount's address, bank and backend */
    req.size_of_struct = (NxDword)sizeof(req);
    req.op     = NX_JOB_READ;
    req.offset = 0x00;
    req.length = 1;
    return s_host->ExecSync(ctx, &req, &id, (NxWord)sizeof(id));
}

/**
 * @brief  Way 2 (completion): runs on the Worker after the read finished.
 * @param[in] ctx       This mount's context.
 * @param[in] rc        ERR_OK or the error of the job.
 * @param[in] data      Bytes read; valid only during this call.
 * @param[in] len       Number of bytes in data.
 * @param[in] user_data Whatever the request carried (unused here).
 */
static void NX_CALL hwa_on_done(NxPluginCtx ctx, NxLong rc, const NxByte *data,
                                NxWord len, void *user_data)
{
    NX_UNUSED_ARG(user_data);
    if (rc != ERR_OK || data == (const NxByte *)0)
    {
        return;
    }
    if (len > (NxWord)sizeof(s_last))
    {
        len = (NxWord)sizeof(s_last);
    }
    memcpy(s_last, data, (size_t)len);       /* copy: data is gone after we return */
    s_host->NotifyUiRefresh(ctx);            /* the only way a Worker asks the UI to redraw */
}

/**
 * @brief  Way 2 - SubmitJob: queue a read and return at once.
 * @param[in] ctx This mount's context.
 * @return ERR_OK when queued; ERR_POLL_DISABLED when the user typed
 *         "poll off" (a low priority job is refused, which is correct).
 * @note   Legal on the UI Thread as well as the Worker: it never blocks.
 */
NxLong NX_CALL hwa_poll_fn(NxPluginCtx ctx)
{
    NxJobRequest req;

    memset(&req, 0, sizeof(req));
    req.size_of_struct = (NxDword)sizeof(req);
    req.op          = NX_JOB_READ;
    req.priority    = NX_JOB_PRI_LOW;        /* background work: obeys "poll off" */
    req.offset      = 0x00;
    req.length      = (NxWord)sizeof(s_last);
    req.on_complete = hwa_on_done;
    return s_host->SubmitJob(ctx, &req);
}

/**
 * @brief  Way 3 - GetRegSnapshot: no hardware access, any thread.
 * @param[in]  ctx   This mount's context.
 * @param[out] out   Receives 256 register bytes.
 * @param[out] valid Receives the 256-bit mask: bit set = that byte was really read.
 * @return ERR_OK, or an error code with out/valid left alone.
 * @note   Never treat a byte whose valid bit is clear as 0x00: it was
 *         simply never read.
 */
NxLong hwa_snapshot(NxPluginCtx ctx, NxByte out[256], NxByte valid[32])
{
    return s_host->GetRegSnapshot(ctx, 0, 0, out, 256, valid, 32);
}
