/**
 * TestLab main driver: simulation implementation.
 *
 * @copyright 2008, Research In Motion Ltd.
 *
 * @version $Id: //depot/main/dsp/ecall/c/testlab/testlab_sock.c#12 $
 */

#include <stdlib.h>

#include "rimtypes.h"
#include "ipc.h"
#include "error.h"
#include "option.h"
#include "testlab.h"

static char *options[] = {
    "--ul:%s@%s",
    "--ul@%s",
    "--ul",
    "--dl:%s@%s",
    "--dl@%s",
    "--dl",
    "--control:%s:%s:%s:%s@%s",
    "--control@%s",
    "--control",
    "--logpcm:%s",
    "--logpcm",
    "--log:%s",
    "--startframe:%s",
    "--totalframes:%s",
    "--campaign:%s",
    "-h",
    "--help",
    NULL
};

#define ARG_MAX_LENGTH  (200)
#define PORT_MAX_LENGTH (200)
#define HOST_MAX_LENGTH (200)
#define FILE_MAX_LENGTH (200)

static char p1[ARG_MAX_LENGTH];
static char p2[ARG_MAX_LENGTH];
static char p3[ARG_MAX_LENGTH];
static char p4[ARG_MAX_LENGTH];
static char p5[ARG_MAX_LENGTH];

static char ulPort[PORT_MAX_LENGTH + 1]        = "";
static char dlPort[PORT_MAX_LENGTH + 1]        = "";
static char ivsPort[PORT_MAX_LENGTH + 1]       = "";
static char psapPort[PORT_MAX_LENGTH + 1]      = "";
static char host[HOST_MAX_LENGTH + 1]          = "";
static int  mode                               = 0;
static char* pcm                               = NULL;
static char pcmFile[FILE_MAX_LENGTH + 1]       = "";
static char logFile[FILE_MAX_LENGTH + 1]       = "";
static char campaignFile[FILE_MAX_LENGTH + 1]  = "";
static int  startFrame                         = 0;
static int  totalFrames                        = 0;

static void Usage(char *msg, ...) {
    va_list args;
    va_start(args, msg);

    if (msg != NULL) {
        fprintf(stderr, "Error: ");
        vfprintf(stderr, msg, args);
        fprintf(stderr, "\n");
    }

    fprintf(stderr, "Usage is one of the following forms:\n");
    fprintf(stderr, " testlab --ul[[:port]@host]\n");
    fprintf(stderr, "    Runs testlab as the UL channel processing task\n");
    fprintf(stderr, "    Default port@host is 10000@localhost\n");
    fprintf(stderr, "\n");
    fprintf(stderr, " testlab --dl[[:port]@host]\n");
    fprintf(stderr, "    Runs testlab as the DL channel processing task\n");
    fprintf(stderr, "    Default port@host is 10001@localhost\n");
    fprintf(stderr, "\n");
    fprintf(stderr, " testlab <options> --control[[:ul:dl:ivs:psap]@host]\n");
    fprintf(stderr, "    Runs testlab as the control processing task\n");
    fprintf(stderr, "    Default ul:dl:ivs:psap@host is\n");
    fprintf(stderr, "    10000:10001:10002:10003@localhost\n");
    fprintf(stderr, "\n");
    fprintf(stderr, "   with <options> in:\n");
    fprintf(stderr, "   --logpcm[:fileBase]\n");
    fprintf(stderr, "     Logs UL in/out and DL in/out PCM samples\n");
    fprintf(stderr, "     Default fileBase is pcm.\n");
    fprintf(stderr, "     PCM file name template: fileBase<src><index>.pcm\n");
    fprintf(stderr, "   --log:file\n");
    fprintf(stderr, "     Sets output log file to file.\n");
    fprintf(stderr, "     Default is results.txt.\n");
    fprintf(stderr, "     [stdout] and [stderr] can be used to send output\n");
    fprintf(stderr, "     to stdout and stderr streams.\n");
    fprintf(stderr, "   --startframe:n\n");
    fprintf(stderr, "     The number of frames to process before\n");
    fprintf(stderr, "     sending the MsgPsapTx signal to the PSAP.\n");
    fprintf(stderr, "     Default is 10 frames (200 ms).\n");
    fprintf(stderr, "   --totalframes:n\n");
    fprintf(stderr, "     The number of frames to wait for the MsgPsapRx\n");
    fprintf(stderr, "     signal before declaring a tomeout for a given\n");
    fprintf(stderr, "     transmission.  Default is 200*50 frames (200 s).\n");
    fprintf(stderr, "   --campaign:file\n");
    fprintf(stderr, "     Sets input campaign file to file.\n");
    fprintf(stderr, "     Default is campaign.txt\n");

    va_end(args);
}

static int ProcessArgs(int argc, char *argv[]) {
    int ulCount = 0;
    int dlCount = 0;
    int controlCount = 0;

    while (argc-- > 0) {
        int n;

        if (strlen(*argv) >= ARG_MAX_LENGTH) {
            Usage("Arguement too long <%s>", *argv);
            return FALSE;
        }
       
        n = option(*argv, options, p1, p2, p3, p4, p5);

        switch (n) {
            case 0: strcpy(ulPort, p1); strcpy(host, p2); ulCount += 1; break;
            case 1: strcpy(host, p1); ulCount += 1; break;
            case 2: ulCount += 1; break;
            case 3: strcpy(dlPort, p1); strcpy(host, p2); dlCount += 1; break;
            case 4: strcpy(host, p1); dlCount += 1; break;
            case 5: dlCount += 1; break;
            case 6: strcpy(ulPort, p1);  strcpy(dlPort, p2);
                    strcpy(ivsPort, p3); strcpy(psapPort, p4);
                    strcpy(host, p5); controlCount += 1; break;
            case 7: strcpy(host, p1); controlCount += 1; break;
            case 8: controlCount += 1; break;
            case 9: strcpy(pcmFile, p1); pcm = pcmFile; break;
            case 10: pcm = pcmFile; break;
            case 11: strcpy(logFile, p1); break;
            case 12: if (sscanf(p1, "%d", &startFrame) != 1) {
                         Usage("Invalid startFrame count %s", p1);
                         return FALSE;
                     }
                     break;
            case 13: if (sscanf(p1, "%d", &totalFrames) != 1) {
                         Usage("Invalid totalFrames count <%s>", p1);
                         return FALSE;
                     }
                     break;
            case 14: strcpy(campaignFile, p1); break;
            case 15:
            case 16: Usage(NULL); return FALSE;
            default: Usage("Invalid option %s", *argv); return FALSE;
        }
        argv++;
    }
    if ((ulCount + dlCount + controlCount) < 1) {
        Usage("At least one of --ul --dl or --control must appear");
        return FALSE;
    }
    if ((ulCount + dlCount + controlCount) > 1) {
        Usage("At most one of --ul --dl or --control can appear");
        return FALSE;
    }
    if (ulCount      > 0) mode = 1;
    if (dlCount      > 0) mode = 2;
    if (controlCount > 0) mode = 3;

    if (strlen(ulPort)       <= 0) strcpy(ulPort,        "10000");
    if (strlen(dlPort)       <= 0) strcpy(dlPort,        "10001");
    if (strlen(ivsPort)      <= 0) strcpy(ivsPort,       "10002");
    if (strlen(psapPort)     <= 0) strcpy(psapPort,      "10003");
    if (strlen(host)         <= 0) strcpy(host,          "localhost");
    if (strlen(pcmFile)      <= 0) strcpy(pcmFile,       "pcm");
    if (strlen(logFile)      <= 0) strcpy(logFile,       "results.txt");
    if (strlen(campaignFile) <= 0) strcpy(campaignFile,  "campaign.txt");
    if (startFrame == 0)           startFrame = 10;
    if (totalFrames == 0)          totalFrames = 200 * 50;

    return TRUE;
}

static void Run(BOOL (*run)(void))
{
    IpcStart();
    while((*run)());
    IpcStop();
}

int main(int argc, char *argv[]) {
    if (ProcessArgs(argc-1, &argv[1])) {
        LogInit(stderr);
        IpcInit();
        switch (mode) {
            case 1:
                UlChannelInit(host, ulPort);
                Run(UlChannelRun);
                UlChannelFinal();
                break;
            case 2:
                DlChannelInit(host, dlPort);
                Run(DlChannelRun);
                DlChannelFinal();
                break;
            case 3:
                ControlInit(host, ulPort, dlPort, ivsPort, psapPort,
                            pcm, campaignFile, logFile,
                            totalFrames + startFrame, startFrame);
                LogInfo("Waiting for UL/DL/IVS/PSAP to connect");
                Run(ControlRun);
                break;
            default:
                return 0;
        }
        IpcFinal();
        return 0;
    }

    return -1;
}
