#include "ngspice/cm.h"
extern void cm_astate(Mif_Private_t *);
/*.......1.........2.........3.........4.........5.........6.........7.........8
================================================================================

FILE astate/cfunc.mod

3-clause BSD

Copyright 2025
The ngspice team

AUTHORS

    20 September 2025     Holger Vogt


MODIFICATIONS



SUMMARY

    This file contains the functional description of the analog
    state code model.
    It takes an input node, stores its value (voltage or current)
    during the last three time steps and returns a value at the output,
    delayed by 0 to 3 steps, depending on the model parameter astate_no.


INTERFACES

    FILE                 ROUTINE CALLED



REFERENCED FILES

    Inputs from and outputs to ARGS structure.


NON-STANDARD FEATURES

    NONE

===============================================================================*/

/*=== INCLUDE FILES ====================*/

#include <stdlib.h>


/*=== CONSTANTS ========================*/




/*=== MACROS ===========================*/




/*=== LOCAL VARIABLES & TYPEDEFS =======*/

typedef struct {
    double    state1;       /* first state value */
    double    state2;       /* second state value */
    double    state3;       /* third state value */
    double    outval;       /* output value */
    double    xval1;        /* first x value */
    double    xval2;        /* second x value */
    double    xval3;        /* third x value */
} stLocal_Data_t;





/*=== FUNCTION PROTOTYPE DEFINITIONS ===*/

static void cm_astate_callback(Mif_Private_t *mif_private, Mif_Callback_Reason_t reason);

/*=== CM_STATE ROUTINE ===*/

void cm_astate(Mif_Private_t *mif_private)
{
    int state_number = 0;
    double outval = 0.0;

    stLocal_Data_t *loc;        /* Pointer to local static data, not to be included
                                       in the state vector */


    if (mif_private->circuit.anal_type != MIF_AC) {     /**** only Transient Analysis and dc ****/

        /** INIT: allocate storage **/

        if (mif_private->circuit.init==1) {

            *(mif_private->callback) = cm_astate_callback;

            /*** allocate static storage for *loc ***/
            if ((loc = (stLocal_Data_t *) (mif_private->inst_var[0]->element[0].pvalue = calloc(1,
                    sizeof(stLocal_Data_t)))) == (stLocal_Data_t *) NULL) {
                cm_message_send("Unable to allocate Local_Data_t "
                    "in cm_astate()");
                return;
            }
            loc->state1 = 0;
            loc->state2 = 0;
            loc->state3 = 0;
            loc->outval = 0;
            loc->xval1 = 0;
            loc->xval2 = 0;
            loc->xval3 = 0;
        }

        /* retrieve previous values */

        loc =  mif_private->inst_var[0]->element[0].pvalue;

        state_number = mif_private->param[0]->element[0].ivalue;

        if (state_number == 0) {
            mif_private->conn[1]->port[0]->output.rvalue = mif_private->conn[0]->port[0]->input.rvalue;
            return;
        }

        if (mif_private->circuit.time == loc->xval1) {
            switch(state_number) {
                case 1:
                    loc->outval = loc->state1;
                    break;
                case 2:
                    loc->outval = loc->state2;
                    break;
                case 3:
                    loc->outval = loc->state3;
                    break;
                default: 
                    loc->outval = mif_private->conn[0]->port[0]->input.rvalue;
                    break;
            }
        }
        else if (mif_private->circuit.time > loc->xval1) {
            loc->state3 = loc->state2;
            loc->state2 = loc->state1;
            loc->state1 = mif_private->conn[0]->port[0]->input.rvalue;

            loc->xval3 = loc->xval2;
            loc->xval2 = loc->xval1;
            loc->xval1 = mif_private->circuit.time;
        }
        /* initial time iteration */
        else if (mif_private->circuit.time == 0.0) {
            loc->state1 = loc->outval = mif_private->conn[0]->port[0]->input.rvalue;
            loc->xval1 = 0.0;
        }
        /* time step rejected ? */
        else if (mif_private->circuit.time < loc->xval1){
            loc->state1 = mif_private->conn[0]->port[0]->input.rvalue;
            loc->xval1 = mif_private->circuit.time;
        }
        /* output */
        if (mif_private->circuit.anal_type == MIF_TRAN) {
            mif_private->conn[1]->port[0]->output.rvalue = loc->outval;
        }
        else { /* dc */
            mif_private->conn[1]->port[0]->output.rvalue = mif_private->conn[0]->port[0]->input.rvalue;
        }
    }
    else {
        mif_private->conn[1]->port[0]->output.rvalue = mif_private->conn[0]->port[0]->input.rvalue;
    }
}

/* free the memory created locally */
static void cm_astate_callback(Mif_Private_t *mif_private, Mif_Callback_Reason_t reason)
{
    switch (reason) {
        case MIF_CB_DESTROY: {
            stLocal_Data_t *loc = (stLocal_Data_t *) mif_private->inst_var[0]->element[0].pvalue;
            if (loc == (stLocal_Data_t *) NULL) {
                break;
            }

            free(loc);

            mif_private->inst_var[0]->element[0].pvalue = NULL;
            break;
        }
    }
} /* end of function cm_astate_callback */
