/*============================================================================
FILE    EVTsetup.c

MEMBER OF process XSPICE

Copyright 1991
Georgia Tech Research Corporation
Atlanta, Georgia 30332
All Rights Reserved

PROJECT A-8503

AUTHORS

    9/12/91  Bill Kuhn

MODIFICATIONS

    <date> <person name> <nature of modifications>

SUMMARY

    This file contains function EVTsetup which clears/allocates the
    event-driven queues and data structures immediately prior to a new
    analysis.  In addition, it places entries in the job list so that
    results data from multiple analysis can be retrieved similar to
    SPICE3C1 saving multiple 'plots'.

INTERFACES

    int EVTsetup(CKTcircuit *ckt)

REFERENCED FILES

    None.

NON-STANDARD FEATURES

    None.

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

#include "ngspice/ngspice.h"
//#include "misc.h"

#include "ngspice/cktdefs.h"
#include "ngspice/sperror.h"
//#include "util.h"

#include "ngspice/mif.h"
#include "ngspice/evt.h"
#include "ngspice/evtudn.h"
#include "ngspice/mifproto.h"
#include "ngspice/evtproto.h"




static int EVTsetup_queues(CKTcircuit *ckt);
static int EVTsetup_data(CKTcircuit *ckt);
static int EVTsetup_jobs(CKTcircuit *ckt);
static int EVTsetup_load_ptrs(CKTcircuit *ckt);




/* Allocation macros with built-in check for out-of-memory */
/* Adapted from SPICE 3C1 code in CKTsetup.c */

#define CKALLOC(var,size,type) \
    if(size) { \
        if((var = TMALLOC(type, size)) == NULL) \
            return(E_NOMEM); \
    }

#define CKREALLOC(var,size,type) \
    if((size) == 1) { \
        if((var = TMALLOC(type, size)) == NULL) \
            return(E_NOMEM); \
    } else if((size) > 1) { \
        if((var = TREALLOC(type, (var), size)) == NULL) \
            return(E_NOMEM); \
    }


/*
EVTsetup

This function clears/allocates the event-driven queues and data structures
immediately prior to a new analysis.  In addition, it places entries
in the job list so that results data from multiple analysis can be
retrieved similar to SPICE3C1 saving multiple 'plots'.
*/


int EVTsetup(
    CKTcircuit *ckt)   /* The circuit structure */
{

    int err;


    /* Exit immediately if no event-driven instances in circuit */
    if(ckt->evt->counts.num_insts == 0)
        return(OK);

    /* Clear the inst, node, and output queues, and initialize the to_call */
    /* elements in the instance queue to call all event-driven instances */
    err = EVTsetup_queues(ckt);
    if(err)
        return(err);

    /* Allocate and initialize the node, state, message, and statistics data */
    err = EVTsetup_data(ckt);
    if(err)
        return(err);

    /* Set the job pointers to the allocated results, states, messages, */
    /* and statistics so that data will be accessable after run */
    err = EVTsetup_jobs(ckt);
    if(err)
        return(err);

    /* Setup the pointers in the MIFinstance structure for inputs, outputs, */
    /* and total loads */
    err = EVTsetup_load_ptrs(ckt);
    if(err)
        return(err);

    /* Initialize additional event data */
    g_mif_info.circuit.evt_step = 0.0;

    /* Return OK */
    return(OK);
}




/*
EVTsetup_queues

This function clears the event-driven queues in preparation for
a new simulation.
*/


static int EVTsetup_queues(
    CKTcircuit *ckt)         /* The circuit structure */
{

    int i;
    int num_insts;
    int num_nodes;
    int num_outputs;

    Evt_Inst_Queue_t    *inst_queue;
    Evt_Node_Queue_t    *node_queue;
    Evt_Output_Queue_t  *output_queue;

    Evt_Inst_Event_t    *inst_event;
    Evt_Output_Event_t  *output_event;

    void                *ptr;

    /* ************************ */
    /* Clear the instance queue */
    /* ************************ */

    num_insts = ckt->evt->counts.num_insts;
    inst_queue = &(ckt->evt->queue.inst);

    for(i = 0; i < num_insts; i++) {
        inst_event = inst_queue->head[i];
        while(inst_event) {
            ptr = inst_event;
            inst_event = inst_event->next;
            FREE(ptr);
        }
        inst_event = inst_queue->free[i];
        while(inst_event) {
            ptr = inst_event;
            inst_event = inst_event->next;
            FREE(ptr);
        }
        inst_queue->head[i] = NULL;
        inst_queue->current[i] = &(inst_queue->head[i]);
        inst_queue->last_step[i] = &(inst_queue->head[i]);
        inst_queue->free[i] = NULL;
    }

    inst_queue->next_time = 0.0;
    inst_queue->last_time = 0.0;

    inst_queue->num_modified = 0;
    inst_queue->num_pending = 0;
    inst_queue->num_to_call = 0;

    for(i = 0; i < num_insts; i++) {
        inst_queue->modified[i] = MIF_FALSE;
        inst_queue->pending[i] = MIF_FALSE;
        inst_queue->to_call[i] = MIF_FALSE;
    }


    /* ******************** */
    /* Clear the node queue */
    /* ******************** */

    num_nodes = ckt->evt->counts.num_nodes;
    node_queue = &(ckt->evt->queue.node);

    node_queue->num_changed = 0;
    node_queue->num_to_eval = 0;

    for(i = 0; i < num_nodes; i++) {
        node_queue->changed[i] = MIF_FALSE;
        node_queue->to_eval[i] = MIF_FALSE;
    }

    /* ********************** */
    /* Clear the output queue */
    /* ********************** */

    num_outputs = ckt->evt->counts.num_outputs;
    output_queue = &(ckt->evt->queue.output);

    for(i = 0; i < num_outputs; i++) {
        output_event = output_queue->head[i];
        while(output_event) {
            ptr = output_event;
            output_event = output_event->next;
            FREE(ptr);
        }
        output_event = output_queue->free[i];
        while(output_event) {
            ptr = output_event;
            output_event = output_event->next;
            FREE(ptr);
        }
        output_queue->head[i] = NULL;
        output_queue->current[i] = &(output_queue->head[i]);
        output_queue->last_step[i] = &(output_queue->head[i]);
        output_queue->free[i] = NULL;
    }

    output_queue->next_time = 0.0;
    output_queue->last_time = 0.0;

    output_queue->num_modified = 0;
    output_queue->num_pending = 0;
    output_queue->num_changed = 0;

    for(i = 0; i < num_outputs; i++) {
        output_queue->modified[i] = MIF_FALSE;
        output_queue->pending[i] = MIF_FALSE;
        output_queue->changed[i] = MIF_FALSE;
    }

    return(OK);
}



/*
EVTsetup_data

This function sets up the event-driven node, state, and
message data runtime structures in preparation for
a new simulation.
*/


static int EVTsetup_data(
    CKTcircuit *ckt)       /* The circuit structure */
{

    Evt_Data_t  *data;

    int         i;
    int         j;

    int         num_insts;
    int         num_ports;
    int         num_nodes;

    int         udn_index;
    int         num_outputs;

    Mif_Boolean_t  invert;

    Evt_Node_Data_t     *node_data;
    Evt_State_Data_t    *state_data;
    Evt_Msg_Data_t      *msg_data;
    /*    Evt_Statistic_t     *statistics_data;*/

    Evt_Node_t          *rhs;
    Evt_Node_t          *rhsold;

    Evt_Node_Info_t     *node_info;


    /* Allocate main substructures of data */
    /* Note that we don't free any old structures */
    /* since they are pointed to by jobs and need */
    /* to be maintained so that results from multiple */
    /* jobs are kept around like SPICE does */

    data = &(ckt->evt->data);
    CKALLOC(data->node, 1, Evt_Node_Data_t)
    CKALLOC(data->state, 1, Evt_State_Data_t)
    CKALLOC(data->msg, 1, Evt_Msg_Data_t)
    CKALLOC(data->statistics, 1, Evt_Statistic_t)

    /* Allocate node data */

    num_nodes = ckt->evt->counts.num_nodes;
    node_data = data->node;

    CKALLOC(node_data->head, num_nodes, Evt_Node_t *)
    CKALLOC(node_data->tail, num_nodes, Evt_Node_t **)
    CKALLOC(node_data->last_step, num_nodes, Evt_Node_t **)
    CKALLOC(node_data->free, num_nodes, Evt_Node_t *)
    CKALLOC(node_data->modified_index, num_nodes, int)
    CKALLOC(node_data->modified, num_nodes, Mif_Boolean_t)
    CKALLOC(node_data->rhs, num_nodes, Evt_Node_t)
    CKALLOC(node_data->rhsold, num_nodes, Evt_Node_t)
    CKALLOC(node_data->total_load, num_nodes, double)

    /* Initialize the node data */

    for(i = 0; i < num_nodes; i++) {
        node_data->tail[i] = &(node_data->head[i]);
        node_data->last_step[i] = &(node_data->head[i]);
    }

    for(i = 0; i < num_nodes; i++) {

        /* Get pointers to rhs & rhsold, the user-defined node type index, */
        /* the number of outputs on the node and the invert flag */
        rhs = &(node_data->rhs[i]);
        rhsold = &(node_data->rhsold[i]);
        node_info = ckt->evt->info.node_table[i];
        udn_index = node_info->udn_index;
        num_outputs = node_info->num_outputs;
        invert = node_info->invert;

        /* Initialize the elements within rhs and rhsold */
        rhs->step = 0.0;
        rhsold->step = 0.0;
        if(num_outputs > 1) {
            CKALLOC(rhs->output_value, num_outputs, void *)
            CKALLOC(rhsold->output_value, num_outputs, void *)
            for(j = 0; j < num_outputs; j++) {
                g_evt_udn_info[udn_index]->create (&(rhs->output_value[j]));
                g_evt_udn_info[udn_index]->initialize (rhs->output_value[j]);
                g_evt_udn_info[udn_index]->create (&(rhsold->output_value[j]));
                g_evt_udn_info[udn_index]->initialize (rhsold->output_value[j]);
            }
        }
        g_evt_udn_info[udn_index]->create (&(rhs->node_value));
        g_evt_udn_info[udn_index]->initialize (rhs->node_value);
        g_evt_udn_info[udn_index]->create (&(rhsold->node_value));
        g_evt_udn_info[udn_index]->initialize (rhsold->node_value);
        if(invert) {
            g_evt_udn_info[udn_index]->create (&(rhs->inverted_value));
            g_evt_udn_info[udn_index]->initialize (rhs->inverted_value);
            g_evt_udn_info[udn_index]->create (&(rhsold->inverted_value));
            g_evt_udn_info[udn_index]->initialize (rhsold->inverted_value);
        }

        /* Initialize the total load value to zero */
        node_data->total_load[i] = 0.0;
    }


    /* Allocate and initialize state data */

    num_insts = ckt->evt->counts.num_insts;
    state_data = data->state;

    CKALLOC(state_data->head, num_insts, Evt_State_t *)
    CKALLOC(state_data->tail, num_insts, Evt_State_t **)
    CKALLOC(state_data->last_step, num_insts, Evt_State_t **)
    CKALLOC(state_data->free, num_insts, Evt_State_t *)
    CKALLOC(state_data->modified_index, num_insts, int)
    CKALLOC(state_data->modified, num_insts, Mif_Boolean_t)
    CKALLOC(state_data->total_size, num_insts, int)
    CKALLOC(state_data->desc, num_insts, Evt_State_Desc_t *)

    for(i = 0; i < num_insts; i++) {
        state_data->tail[i] = &(state_data->head[i]);
        state_data->last_step[i] = &(state_data->head[i]);
    }


    /* Allocate and initialize msg data */

    num_ports = ckt->evt->counts.num_ports;
    msg_data = data->msg;

    CKALLOC(msg_data->head, num_ports, Evt_Msg_t *)
    CKALLOC(msg_data->tail, num_ports, Evt_Msg_t **)
    CKALLOC(msg_data->last_step, num_ports, Evt_Msg_t **)
    CKALLOC(msg_data->free, num_ports, Evt_Msg_t *)
    CKALLOC(msg_data->modified_index, num_ports, int)
    CKALLOC(msg_data->modified, num_ports, Mif_Boolean_t)

    for(i = 0; i < num_ports; i++) {
        msg_data->tail[i] = &(msg_data->head[i]);
        msg_data->last_step[i] = &(msg_data->head[i]);
    }

    /* Don't need to initialize statistics since they were */
    /* calloc'ed above */

    return(OK);
}




/*
EVTsetup_jobs

This function prepares the jobs data for a new simulation.
*/


static int EVTsetup_jobs(
    CKTcircuit *ckt)       /* The circuit structure */
{

    int  i;
    int  num_jobs;

    Evt_Job_t  *jobs;
    Evt_Data_t *data;


    jobs = &(ckt->evt->jobs);
    data = &(ckt->evt->data);

    /* Increment the number of jobs */
    num_jobs = ++(jobs->num_jobs);

    /* Allocate/reallocate necessary pointers */
    CKREALLOC(jobs->job_name, num_jobs, char *)
    CKREALLOC(jobs->node_data, num_jobs, Evt_Node_Data_t *)
    CKREALLOC(jobs->state_data, num_jobs, Evt_State_Data_t *)
    CKREALLOC(jobs->msg_data, num_jobs, Evt_Msg_Data_t *)
    CKREALLOC(jobs->statistics, num_jobs, Evt_Statistic_t *)

    /* Fill in the pointers, etc. for this new job */
    i = num_jobs - 1;
    jobs->job_name[i] = MIFcopy(ckt->CKTcurJob->JOBname);
    jobs->node_data[i] = data->node;
    jobs->state_data[i] = data->state;
    jobs->msg_data[i] = data->msg;
    jobs->statistics[i] = data->statistics;

    return(OK);
}



/*
EVTsetup_load_ptrs

This function setups up the required data in the MIFinstance
structure of event-driven and hybrid instances.
*/


static int EVTsetup_load_ptrs(
    CKTcircuit *ckt)            /* The circuit structure */
{

    int         i;
    int         j;
    int         k;

    int         num_insts;
    int         num_conn;
    int         num_port;
    int         num_outputs;

    int         node_index;
    int         output_subindex;

    MIFinstance         *fast;

    Mif_Conn_Data_t     *conn;

    Mif_Port_Type_t     type;
    Mif_Port_Data_t     *port;

    Evt_Node_Data_t     *node_data;


    /* This function setups up the required data in the MIFinstance */
    /* structure of event-driven and hybrid instances */

    /* Loop through all event-driven and hybrid instances */
    num_insts = ckt->evt->counts.num_insts;
    for(i = 0; i < num_insts; i++) {

        /* Get the MIFinstance pointer */
        fast = ckt->evt->info.inst_table[i]->inst_ptr;

        /* Loop through all connections */
        num_conn = fast->num_conn;
        for(j = 0; j < num_conn; j++) {

            /* Skip if connection is null */
            if(fast->conn[j]->is_null)
                continue;

            conn = fast->conn[j];

            /* Loop through all ports */
            num_port = conn->size;
            for(k = 0; k < num_port; k++) {

                /* Get port data pointer for quick access */
                port = conn->port[k];

                if(port->is_null)
                    continue;

                /* Skip if port is not digital or user-defined type */
                type = port->type;
                if((type != MIF_DIGITAL) && (type != MIF_USER_DEFINED))
                    continue;

                /* Set input.pvalue to point to rhsold.node_value or to */
                /* rhsold.inverted_value as appropriate */
                node_index = port->evt_data.node_index;
                node_data  = ckt->evt->data.node;
                if(conn->is_input) {
                    if(port->invert) {
                        port->input.pvalue = node_data->rhsold[node_index].
                                             inverted_value;
                    }
                    else {
                        port->input.pvalue = node_data->rhsold[node_index].
                                             node_value;
                    }
                }

                /* Set output.pvalue to point to rhs.node_value or rhs.output_value[i] */
                /* where i is given by the output_subindex in output info */
                /* depending on whether more than one output is connected to the node. */
                /* Note that this is only for the DCOP analysis.  During a transient */
                /* analysis, new structures will be created and the pointers will */
                /* be set by EVTload */
                if(conn->is_output) {
                    num_outputs = ckt->evt->info.node_table[node_index]->num_outputs;
                    if(num_outputs <= 1) {
                        port->output.pvalue = node_data->rhs[node_index].
                                              node_value;
                    }
                    else {
                        output_subindex = port->evt_data.output_subindex;
                        port->output.pvalue = node_data->rhs[node_index].
                                              output_value[output_subindex];
                    }
                }

            } /* end for number of ports */
        } /* end for number of connections */
    } /* end for number of insts */

    return(OK);
}
