/*************************************************************************\
* Copyright (c) 2015 Michael Davidsaver
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/

#include <string.h>

#include "dbAccess.h"
#include "devSup.h"
#include "alarm.h"
#include "dbUnitTest.h"
#include "errlog.h"
#include "epicsThread.h"

#include "longinRecord.h"

#include "testMain.h"

void recTestIoc_registerRecordDeviceDriver(struct dbBase *);

static void startTestIoc(const char *dbfile)
{
    testdbPrepare();
    testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
    recTestIoc_registerRecordDeviceDriver(pdbbase);
    testdbReadDatabase(dbfile, NULL, NULL);

    eltc(0);
    testIocInitOk();
    eltc(1);
}

/* testing here instead of ioc/db/test as xRecord has no INP/OUT */
static void testdbGetDevLink(void)
{
    longinRecord *rec = (longinRecord*)testdbRecordPtr("li1");
    testOk1(dbGetDevLink((dbCommon*)rec) == &rec->inp);
}

static void testLongStringInit()
{
    testDiag("testLongStringInit");

    startTestIoc("linkInitTest.db");

    testdbGetDevLink();

    {
        const char buf[] = "!----------------------------------------------!";
        testdbGetArrFieldEqual("longstr1.VAL$", DBF_CHAR, NELEMENTS(buf)+2, NELEMENTS(buf), buf);
        testdbGetFieldEqual("longstr1.VAL", DBR_STRING, "!--------------------------------------");
    }

    {
        const char buf[] = "!----------------------------------------------!";
        testdbGetArrFieldEqual("longstr2.VAL$", DBF_CHAR, NELEMENTS(buf)+2, NELEMENTS(buf), buf);
        testdbGetFieldEqual("longstr2.VAL", DBR_STRING, "!--------------------------------------");
    }

    {
        const char buf[] = "!----------------------------------------------!";
        testdbGetArrFieldEqual("longstr3.VAL$", DBF_CHAR, NELEMENTS(buf)+2, NELEMENTS(buf), buf);
        testdbGetFieldEqual("longstr3.VAL", DBR_STRING, "!--------------------------------------");
    }

    testdbGetFieldEqual("longstr4.VAL", DBR_STRING, "One");

    testIocShutdownOk();

    testdbCleanup();
}

static void testCalcInit()
{
    testDiag("testCalcInit");

    startTestIoc("linkInitTest.db");

    testdbGetFieldEqual("emptylink.VAL", DBR_DOUBLE, 0.0);
    testdbGetFieldEqual("emptylink.SEVR", DBR_LONG, INVALID_ALARM);

    testdbPutFieldOk("emptylink.PROC", DBF_LONG, 1);

    testdbGetFieldEqual("emptylink.VAL", DBR_DOUBLE, 0.0);
    testdbGetFieldEqual("emptylink.SEVR", DBR_LONG, 0);

    testdbGetFieldEqual("emptylink1.VAL", DBR_DOUBLE, 0.0);
    testdbGetFieldEqual("emptylink1.SEVR", DBR_LONG, INVALID_ALARM);

    testdbPutFieldOk("emptylink1.PROC", DBF_LONG, 1);

    testdbGetFieldEqual("emptylink1.VAL", DBR_DOUBLE, 1.0);
    testdbGetFieldEqual("emptylink1.SEVR", DBR_LONG, 0);

    testIocShutdownOk();

    testdbCleanup();
}

static void testPrintfStrings()
{
    testDiag("testPrintfStrings");

    startTestIoc("linkInitTest.db");

    {
        const char buf1[] = "Test string, exactly 40 characters long";
        const char buf2[] = "Longer test string, more that 40 characters long";
        const char buf2t[] = "Longer test string, more that 40 charac";

        /* The FMT field is pp(TRUE), so this put triggers processing */
        testdbPutFieldOk("printf1.FMT", DBF_STRING, "%s");
        testdbGetArrFieldEqual("printf1.VAL$", DBF_CHAR, NELEMENTS(buf1)+2,
            NELEMENTS(buf1), buf1);
        testdbGetFieldEqual("printf1.VAL", DBR_STRING, buf1);

        testdbPutFieldOk("printf1.FMT", DBF_STRING, "%ls");
        testdbGetArrFieldEqual("printf1.VAL$", DBF_CHAR, NELEMENTS(buf1)+2,
            NELEMENTS(buf1), buf1);
        testdbGetFieldEqual("printf1.VAL", DBR_STRING, buf1);

        testdbPutFieldOk("printf2.FMT", DBF_STRING, "%s");
        testdbGetArrFieldEqual("printf2.VAL$", DBF_CHAR, NELEMENTS(buf2)+2,
            NELEMENTS(buf2t), buf2t);
        testdbGetFieldEqual("printf2.VAL", DBR_STRING, buf2t);

        testdbPutFieldOk("printf2.FMT", DBF_STRING, "%ls");
        testdbGetArrFieldEqual("printf2.VAL$", DBF_CHAR, NELEMENTS(buf2)+2,
            NELEMENTS(buf2), buf2);
        testdbGetFieldEqual("printf2.VAL", DBR_STRING, buf2t);

        testdbPutFieldOk("printf2.FMT", DBF_STRING, "%.39ls");
        testdbGetArrFieldEqual("printf2.VAL$", DBF_CHAR, NELEMENTS(buf2)+2,
            NELEMENTS(buf2t), buf2t);
    }

    testIocShutdownOk();

    testdbCleanup();
}

static void testArrayInputs()
{
    epicsInt32 oneToTwelve[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
    epicsOldString reindeer[10] = {"Dasher", "Dancer", "Prancer", "Vixen",
                    "Comet", "Cupid", "Donner", "Blitzen", "Rudolph"};

    testDiag("testArrayInputs");

    startTestIoc("linkInitTest.db");

    testdbGetFieldEqual("aai1.NORD", DBR_LONG, 10);
    testdbGetFieldEqual("aai1.UDF", DBR_UCHAR, 0);
    testdbGetFieldEqual("aai2.NORD", DBR_LONG, 10);
    testdbGetFieldEqual("aai2.UDF", DBR_UCHAR, 0);
    testdbGetFieldEqual("sa1.NORD", DBR_LONG, 10);
    testdbGetFieldEqual("sa1.UDF", DBR_UCHAR, 0);
    testdbGetFieldEqual("sa2.NORD", DBR_LONG, 0);
    testdbGetFieldEqual("sa2.UDF", DBR_UCHAR, 1);
    testdbGetFieldEqual("wf1.NORD", DBR_LONG, 10);
    testdbGetFieldEqual("wf1.UDF", DBR_UCHAR, 0);
    testdbGetFieldEqual("wf2.NORD", DBR_LONG, 10);
    testdbGetFieldEqual("wf2.UDF", DBR_UCHAR, 0);

    testdbGetArrFieldEqual("aai1.VAL", DBF_LONG, 12, 10, &oneToTwelve[0]);
    testdbGetArrFieldEqual("aai2.VAL", DBF_LONG, 12, 10, &oneToTwelve[0]);
    testdbGetArrFieldEqual("sa1.VAL", DBF_LONG, 12, 10, &oneToTwelve[2]);
    testdbGetArrFieldEqual("sa2.VAL", DBF_LONG, 10, 0, NULL);
    testdbGetArrFieldEqual("wf1.VAL", DBF_LONG, 12, 10, &oneToTwelve[0]);
    testdbGetArrFieldEqual("wf2.VAL", DBF_LONG, 12, 10, &oneToTwelve[0]);
    testdbGetArrFieldEqual("reindeer.VAL", DBF_STRING, 10, 9, &reindeer[0]);

    testdbPutFieldOk("sa1.INDX", DBF_LONG, 3);
    testdbGetArrFieldEqual("sa1.VAL", DBF_LONG, 12, 9, &oneToTwelve[3]);

    testdbPutFieldOk("sa1.NELM", DBF_LONG, 3);
    testdbGetArrFieldEqual("sa1.VAL", DBF_LONG, 12, 3, &oneToTwelve[3]);

    testdbPutFieldOk("sa2.VAL", DBF_LONG, 1);
    testdbGetArrFieldEqual("sa2.VAL", DBF_LONG, 10, 1, &oneToTwelve[0]);

    testDiag("testScalarInputs");

    testdbGetFieldEqual("li1", DBR_LONG, 1);
    testdbGetFieldEqual("i64i1", DBR_INT64, 1LL);
    testdbGetFieldEqual("li2", DBR_LONG, 1);
    testdbGetFieldEqual("i64i2", DBR_INT64, 1LL);

    testIocShutdownOk();
    testdbCleanup();
}

static void testEventRecord()
{
    testMonitor *countmon;

    testDiag("testEventRecord");

    startTestIoc("linkInitTest.db");
    countmon = testMonitorCreate("count1.VAL", DBR_LONG, 0);

    testdbGetFieldEqual("ev1.VAL", DBR_STRING, "soft event 1");
    testdbGetFieldEqual("ev1.UDF", DBR_UCHAR, 0);
    testdbGetFieldEqual("ev2.VAL", DBR_STRING, "");
    testdbGetFieldEqual("ev2.UDF", DBR_UCHAR, 1);
    testdbGetFieldEqual("count1.VAL", DBR_LONG, 0);

    testdbPutFieldOk("ev1.PROC", DBF_UCHAR, 1);
    testMonitorWait(countmon);
    testdbGetFieldEqual("count1.VAL", DBR_LONG, 1);

    testdbPutFieldOk("ev2.PROC", DBF_UCHAR, 1);
    testMonitorWait(countmon);
    testdbGetFieldEqual("ev2.UDF", DBR_UCHAR, 0);
    testdbGetFieldEqual("count1.VAL", DBR_LONG, 2);

    testdbPutFieldOk("count1.EVNT", DBF_STRING, "Tock");
    testdbPutFieldOk("ev2.PROC", DBF_UCHAR, 1);
    testMonitorWait(countmon);
    testdbGetFieldEqual("count1.VAL", DBR_LONG, 3);

    testMonitorDestroy(countmon);
    testIocShutdownOk();
    testdbCleanup();
}

void testInt64Inputs(void)
{
    testDiag("testInt64Inputs");

    startTestIoc("linkInitTest.db");

    testdbGetFieldEqual("i1.VAL", DBR_INT64, 1234567890123456789LL);
    testdbGetFieldEqual("i2.VAL", DBR_INT64, 1234567890123456789LL);
    testdbGetFieldEqual("i3.VAL", DBR_INT64, 1234567890123456789LL);

    testdbGetFieldEqual("i4.NORD", DBR_LONG, 1);
    testdbGetFieldEqual("i4.VAL", DBR_INT64, 1234567890123456789LL);

    testIocShutdownOk();
    testdbCleanup();
}


MAIN(linkInitTest)
{
    testPlan(79);

    testLongStringInit();
    testCalcInit();
    testPrintfStrings();
    testArrayInputs();
    testEventRecord();
    testInt64Inputs();

    return testDone();
}
