/* Create a new SSL/TLS connection data object.
 *
 * numConns   The number of concurrent connections.
 * bufferLen  The number of data bytes to read from server.
 * replyLen   The number of data bytes to send to servers.
 * maxConns   The number of connections to process this run.
 *            -1 indicates no maximum.
 * maxBytes   The number of bytes to send this run.
 *            -1 indicates no maximum.
 * returns an allocated and initialized connection data object or NULL on error.
 */
static SSLConn_CTX* SSLConn_New(int numConns, int bufferLen, int replyLen,
                                int maxConns, int maxBytes, int resume)
{
    SSLConn_CTX* ctx;
    int          i;

    ctx = (SSLConn_CTX*)malloc(sizeof(*ctx));
    if (ctx == NULL)
        return NULL;
    memset(ctx, 0, sizeof(*ctx));

    ctx->resume = resume;
    ctx->numConns = numConns;
    ctx->bufferLen = bufferLen;
    ctx->replyLen = replyLen;
    ctx->maxConnections = maxConns;
    ctx->maxBytes = maxBytes;

    /* Create an entry for each concurrent connection. */
    ctx->sslConn = (SSLConn*)malloc(ctx->numConns * sizeof(*ctx->sslConn));
    if (ctx->sslConn == NULL) {
        SSLConn_Free(ctx);
        return NULL;
    }
    for (i = 0; i < ctx->numConns; i++) {
        ctx->sslConn[i].sockfd = -1;
        ctx->sslConn[i].ssl = NULL;
        ctx->sslConn[i].session = NULL;
        ctx->sslConn[i].state = INIT;
        ctx->sslConn[i].err = 0;
    }

    /* Create a buffer for server data. */
    ctx->buffer = (char*)malloc(bufferLen);
    if (ctx->buffer == NULL) {
        SSLConn_Free(ctx);
        return NULL;
    }

    /* Create a reply that contains rand data. */
    ctx->reply = (char*)malloc(replyLen);
    if (ctx->reply == NULL) {
        SSLConn_Free(ctx);
        return NULL;
    }
    RandomReply(ctx->reply, replyLen);

    return ctx;
}
/* Main entry point for the program.
 *
 * argc  The count of command line arguments.
 * argv  The command line arguments.
 * returns 0 on success and 1 otherwise.
 */
int main(int argc, char* argv[])
{
    int                 i;
    int                 ch;

    /* Parse the command line arguments. */
    while ((ch = mygetopt(argc, argv, OPTIONS)) != -1) {
        switch (ch) {
            /* Help with command line options. */
            case '?':
                Usage();
                exit(EXIT_SUCCESS);

            /* Port number to listen on. */
            case 'p':
                port = (word16)atoi(myoptarg);
                break;

            /* Version of SSL/TLS to use. */
            case 'v':
                version = atoi(myoptarg);
                if (version < 0 || version > 3) {
                    Usage();
                    exit(MY_EX_USAGE);
                }
                break;

            /* List of cipher suites to use. */
            case 'l':
                cipherList = myoptarg;
                break;

            /* File name of server certificate for authentication. */
            case 'c':
                ourCert = myoptarg;
                break;

            /* File name of server private key for authentication. */
            case 'k':
                ourKey = myoptarg;
                break;

            /* File name of client certificate/CA for peer verification. */
            case 'A':
                verifyCert = myoptarg;
                break;

            /* Number of connections to make. */
            case 't':
                numThreads  = atoi(myoptarg);
                if (numThreads < 0 || numThreads > 100) {
                    Usage();
                    exit(MY_EX_USAGE);
                }
                break;

            /* Number of connections to make. */
            case 'n':
                maxConns  = atoi(myoptarg);
                if (maxConns < 0 || maxConns > 1000000) {
                    Usage();
                    exit(MY_EX_USAGE);
                }
                maxBytes = 0;
                break;

            /* Number of conncurrent connections to use. */
            case 'N':
                numConns  = atoi(myoptarg);
                if (numConns < 0 || numConns > 100000) {
                    Usage();
                    exit(MY_EX_USAGE);
                }
                break;

            /* Number of bytes to read each call. */
            case 'R':
                numBytesRead = atoi(myoptarg);
                if (numBytesRead <= 0) {
                    Usage();
                    exit(MY_EX_USAGE);
                }
                break;

            /* Number of bytes to write each call. */
            case 'W':
                numBytesWrite = atoi(myoptarg);
                if (numBytesWrite <= 0) {
                    Usage();
                    exit(MY_EX_USAGE);
                }
                break;

            /* Maximum number of read and write bytes (separate counts). */
            case 'B':
                maxBytes = atoi(myoptarg);
                if (maxBytes <= 0) {
                    Usage();
                    exit(MY_EX_USAGE);
                }
                maxConns = 0;
                break;

            /* Unrecognized command line argument. */
            default:
                Usage();
                exit(MY_EX_USAGE);
        }
    }

#ifdef DEBUG_WOLFSSL
    wolfSSL_Debugging_ON();
#endif

    /* Initialize wolfSSL */
    wolfSSL_Init();

    RandomReply(reply, sizeof(reply));

    /* Create SSL/TLS connection data object. */
    sslConnCtx = SSLConn_New(numThreads, numConns, numBytesRead, numBytesWrite,
                             maxConns, maxBytes);
    if (sslConnCtx == NULL)
        exit(EXIT_FAILURE);

    for (i = 0; i < numThreads; i++) {
        if (pthread_create(&sslConnCtx->threadData[i].thread_id, NULL,
                           ThreadHandler, &sslConnCtx->threadData[i]) < 0) {
            perror("ERRROR: could not create thread");
        }
    }

    /* Start all the threads. */
    for (i = 0; i < numThreads; i++)
        pthread_join(sslConnCtx->threadData[i].thread_id, NULL) ;

    sslConnCtx->totalTime = current_time(0) - sslConnCtx->totalTime;

    SSLConn_PrintStats(sslConnCtx);
    SSLConn_Free(sslConnCtx);

    wolfSSL_Cleanup();

    exit(EXIT_SUCCESS);
}