/*************************************************************************\
* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne
*     National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
*     Operator of Los Alamos National Laboratory.
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbCaTest.c */

/****************************************************************
*
*       Author:         Marty Kraimer
*       Date:           10APR96
*
****************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include "dbDefs.h"
#include "epicsEvent.h"
#include "epicsPrint.h"
#include "epicsStdio.h"

#include "dbStaticLib.h"
/*definitions needed because of old vs new database access*/
#undef DBR_SHORT
#undef DBR_PUT_ACKT
#undef DBR_PUT_ACKS
#undef VALID_DB_REQ
#undef INVALID_DB_REQ
/*end of conflicting definitions*/

#include "cadef.h"

/*define DB_CONVERT_GBLSOURCE because db_access.c does not include db_access.h*/
#define DB_CONVERT_GBLSOURCE

#include "db_access.h"
#include "db_access_routines.h"
#include "dbCa.h"
#include "dbCaPvt.h"
#include "dbCaTest.h"
#include "dbCommon.h"
#include "db_convert.h"
#include "dbLock.h"
#include "link.h"


long dbcar(char *precordname, int level)
{
    DBENTRY             dbentry;
    DBENTRY             *pdbentry=&dbentry;
    long                status;
    dbCommon            *precord;
    dbRecordType        *pdbRecordType;
    dbFldDes            *pdbFldDes;
    DBLINK              *plink;
    int                 ncalinks=0;
    int                 nconnected=0;
    int                 noReadAccess=0;
    int                 noWriteAccess=0;
    unsigned long       nDisconnect=0;
    unsigned long       nNoWrite=0;
    caLink              *pca;
    int                 j;

    if (!precordname || precordname[0] == '\0' || !strcmp(precordname, "*")) {
        precordname = NULL;
        printf("CA links in all records\n\n");
    } else {
        printf("CA links in record named '%s'\n\n", precordname);
    }
    dbInitEntry(pdbbase,pdbentry);
    status = dbFirstRecordType(pdbentry);
    while (!status) {
        status = dbFirstRecord(pdbentry);
        while (!status) {
            if (precordname ?
                !strcmp(precordname, dbGetRecordName(pdbentry)) :
                !dbIsAlias(pdbentry)) {
                pdbRecordType = pdbentry->precordType;
                precord = (dbCommon *)pdbentry->precnode->precord;
                dbScanLock(precord);
                for (j=0; j<pdbRecordType->no_links; j++) {
                    pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[j]];
                    plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
                    if (plink->type == CA_LINK) {
                        ncalinks++;
                        pca = (caLink *)plink->value.pv_link.pvt;
                        if (pca
                        && pca->chid
                        && (ca_field_type(pca->chid) != TYPENOTCONN)) {
                            nconnected++;
                            nDisconnect += pca->nDisconnect;
                            nNoWrite += pca->nNoWrite;
                            if (!ca_read_access(pca->chid)) noReadAccess++;
                            if (!ca_write_access(pca->chid)) noWriteAccess++;
                            if (level>1) {
                                int rw = ca_read_access(pca->chid) |
                                         ca_write_access(pca->chid) << 1;
                                static const char *rights[4] = {
                                    "No Access", "Read Only",
                                    "Write Only", "Read/Write"
                                };
                                int mask = plink->value.pv_link.pvlMask;
                                printf("%28s.%-4s ==> %-28s  (%lu, %lu)\n",
                                    precord->name,
                                    pdbFldDes->name,
                                    plink->value.pv_link.pvname,
                                    pca->nDisconnect,
                                    pca->nNoWrite);
                                printf("%21s [%s%s%s%s] host %s, %s\n", "",
                                    mask & pvlOptInpNative ? "IN" : "  ",
                                    mask & pvlOptInpString ? "IS" : "  ",
                                    mask & pvlOptOutNative ? "ON" : "  ",
                                    mask & pvlOptOutString ? "OS" : "  ",
                                    ca_host_name(pca->chid),
                                    rights[rw]);
                            }
                        } else {
                            if (level>0) {
                                printf("%28s.%-4s --> %-28s  (%lu, %lu)\n",
                                    precord->name,
                                    pdbFldDes->name,
                                    plink->value.pv_link.pvname,
                                    pca ? pca->nDisconnect : 0,
                                    pca ? pca->nNoWrite : 0);
                            }
                        }
                    }
                }
                dbScanUnlock(precord);
                if (precordname) goto done;
            }
            status = dbNextRecord(pdbentry);
        }
        status = dbNextRecordType(pdbentry);
    }
done:
    if ((level > 1 && nconnected > 0) ||
        (level > 0 && ncalinks != nconnected)) printf("\n");
    printf("Total %d CA link%s; ",
           ncalinks, (ncalinks != 1) ? "s" : "");
    printf("%d connected, %d not connected.\n",
           nconnected, (ncalinks - nconnected));
    printf("    %d can't read, %d can't write.",
           noReadAccess, noWriteAccess);
    printf("  (%lu disconnects, %lu writes prohibited)\n\n",
           nDisconnect, nNoWrite);
    dbFinishEntry(pdbentry);

    if ( level > 2  && dbCaClientContext != 0 ) {
        ca_context_status ( dbCaClientContext, level - 2 );
    }

    return(0);
}

void dbcaStats(int *pchans, int *pdiscon)
{
    DBENTRY     dbentry;
    DBENTRY     *pdbentry = &dbentry;
    long        status;
    DBLINK      *plink;
    long        ncalinks = 0;
    long        nconnected = 0;

    dbInitEntry(pdbbase,pdbentry);
    status = dbFirstRecordType(pdbentry);
    while (!status) {
        dbRecordType *pdbRecordType = pdbentry->precordType;

        status = dbFirstRecord(pdbentry);
        while (!status) {
            dbCommon *precord = (dbCommon *)pdbentry->precnode->precord;
            int j;

            if (!dbIsAlias(pdbentry)) {
                for (j=0; j<pdbRecordType->no_links; j++) {
                    int i = pdbRecordType->link_ind[j];

                    dbFldDes *pdbFldDes = pdbRecordType->papFldDes[i];
                    plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
                    if (plink->type == CA_LINK) {
                        ncalinks++;
                        if (dbCaIsLinkConnected(plink)) {
                            nconnected++;
                        }
                    }
                }
            }
            status = dbNextRecord(pdbentry);
        }
        status = dbNextRecordType(pdbentry);
    }
    dbFinishEntry(pdbentry);
    if (pchans)  *pchans  = ncalinks;
    if (pdiscon) *pdiscon = ncalinks - nconnected;
}
