/* 
  Copyright 2023 Thorsten Alteholz <libcontra@alteholz.eu>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program (see the file COPYING included with this
  distribution); if not, write to the Free Software Foundation, Inc.,
  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*!
*************************************************************************
*
* \file test_client.c
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \brief this is the client for testing
*
* This client contains all functions that are within the wrapper 
* library. It shall produce the output for all client functions.
*
*
* \addtogroup libcontratest
* The group of the test stuff
* @{
************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <pthread.h>
#include <stdbool.h>
#include <libgen.h>
#include <netinet/in.h>
#include <strings.h>
#include <errno.h>
#include <libcontra_log.h>
#include <libcontra.h>
#include "test-common.h"

pthread_mutex_t libcontra_lock; /*!< mutex for output locks in different functions */
bool	libcontra_logStdio=true; /*!< during the tests we want to capture output on stdio */
int	libcontra_loglevel=LOGLEVEL_MAX; /*!< during the tests we want to log everything */
FILE*	libcontra_file=NULL; /*!< variable needed to be able to use libcontra */

/* global variables for client tests */
#define PORT 54321 /*!< default port that the test server listens on */
#define SA struct sockaddr /*!< shortcut */
struct sockaddr_in servaddr; /*!< global variable for local test server address */
int globalSockfd; /*!< global variable of socket to be used in several tests */

static const struct option long_options[] =
    {
        { "testnumber", required_argument,       0, 't' },
//        { "BBB", no_argument,       0, 'b' },
//        { "CCC", required_argument, 0, 'c' },
//        { "DDD", no_argument,       0, 'd' },
//        { "EEE", no_argument,       0, 0   },
//        { "FFF", required_argument, 0, 0   },
        0
    };

/*!
*************************************************************************
*
* \fn bool do_test_0_1(int testcase)
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \brief test feature 1 of testcase 0
*
* \return rc value of test -> always true
************************************************************************/
bool do_test_0_1(int testcase)
{
  bool result=true;

  libcontra_log(LOGLEVEL_INFO, "I: ...... doing test %02i 1: %5s (%5s)",testcase, bool2str(result), bool2str(true));
  return result;
}

/*!
*************************************************************************
*
* \fn bool do_test_0_2(int testcase)
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \brief test feature 2 of testcase 0
*
* \return rc value of test = always false
************************************************************************/
bool do_test_0_2(int testcase)
{
  bool result=false;

  libcontra_log(LOGLEVEL_INFO, "I: ...... doing test %02i 2: %5s (%5s)",testcase, bool2str(result), bool2str(false));
  return result;
}


/*!
*************************************************************************
*
* \fn bool do_test_0(int testcase)
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \brief testcase 0, client internal test
*
* \return rc value of all tests from testcase 0
************************************************************************/
bool do_test_0(int testcase)
{
  bool result=true;

  result=result & do_test_0_1(testcase); /* result of test: true */
  result=result & !do_test_0_2(testcase); /* result of test: false */
  libcontra_log(LOGLEVEL_INFO, "I: ... test %02i: %5s (%5s)",testcase, bool2str(result), bool2str(true));

  return result;
}

/*!
*************************************************************************
*
* \fn bool do_test_1_2(int testcase)
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \brief test feature 1 of testcase 2; socket(AF_INET6)
*
* \return rc value of test
************************************************************************/
int do_test_1_2(int testcase)
{
  int result=0;
  bool resultBool;
  
  result=socket(AF_INET6, SOCK_STREAM, 0);
  resultBool=socket>0;
  libcontra_log(LOGLEVEL_INFO, "I: ...... doing test %02i 2: v(%i) %5s (%5s) (%s:v(%d))",testcase, result, bool2str(resultBool), bool2str(true), basename(__FILE__), __LINE__);
  return result;
}

/*!
*************************************************************************
*
* \fn bool do_test_1_1(int testcase)
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \brief test feature 1 of testcase 1; socket(AF_INET)
*
* \return rc value of test
************************************************************************/
int do_test_1_1(int testcase)
{
  int result=0;
  bool resultBool;
  
  result=socket(AF_INET, SOCK_STREAM, 0);
  resultBool=socket>0;
  libcontra_log(LOGLEVEL_INFO, "I: ...... doing test %02i 1: v(%i) %5s (%5s) (%s:v(%d))",testcase, result, bool2str(resultBool), bool2str(true), basename(__FILE__), __LINE__);
  return result;
}


/*!
*************************************************************************
*
* \fn bool do_test_1(int testcase)
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \brief testcase 1, socket test
*
* \return rc value of all tests from testcase 0
************************************************************************/
bool do_test_1(int testcase)
{
  bool result=true;

  result=result & (do_test_1_1(testcase)>0); /* result of test: true */
  result=result & (do_test_1_2(testcase)>0); /* result of test: true */
  libcontra_log(LOGLEVEL_INFO, "I: ... test %02i: %5s (%5s)",testcase, bool2str(result), bool2str(true));

  return result;
}

/*!
*************************************************************************
*
* \fn bool do_test_4_1(int testcase)
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \brief test feature 1 of testcase 4; connect
*
* \return rc value of test
************************************************************************/
bool do_test_4_1(int testcase, int sockfd)
{
  int result=0;
  bool resultBool;
    
  // assign IP, PORT
  bzero(&servaddr, sizeof(servaddr));   
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  servaddr.sin_port = htons(PORT+testcase);

  result=connect(sockfd, (SA*)&servaddr, sizeof(servaddr));
  
  resultBool=result==0;
  
  libcontra_log(LOGLEVEL_INFO, "I: ...... doing test %02i 1: v(%i) %5s (%5s) (%s:v(%d))",testcase, result, bool2str(resultBool), bool2str(true), basename(__FILE__), __LINE__);
  
  return resultBool;
}

/*!
*************************************************************************
*
* \fn bool do_test_4(int testcase)
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \brief testcase 1, connect test <- accept test on server
*
* \return rc value of all tests from testcase 0
************************************************************************/
bool do_test_4(int testcase)
{
  bool result=true;
  int sockfd;
  
  /* get AF_INET socket with function from previous test */
  sockfd=do_test_1_1(testcase);
  if (sockfd == -1) {
    libcontra_log(LOGLEVEL_ERROR, "E: ... test %02i: could not create socket %i -> %s (%s:v(%d))", testcase, errno, strerror(errno), basename(__FILE__), __LINE__);
    return false;
  } else {
    libcontra_log(LOGLEVEL_INFO, "I: ... test %02i: socket created (%s:v(%d))", testcase, basename(__FILE__), __LINE__);
  }
  
  result=do_test_4_1(testcase, sockfd);
  
  close(sockfd);
  
  libcontra_log(LOGLEVEL_INFO, "I: ... test %02i: %5s (%5s)",testcase, bool2str(result), bool2str(true));

  
  return result;
}

/*!
*************************************************************************
*
* \fn bool do_test_5_1(int testcase)
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \brief test feature 1 of testcase 5; send
*
* \return rc value of test
************************************************************************/
bool do_test_5_1(int testcase, int sockfd)
{
  int result=0;
  bool resultBool;
  ssize_t bytesSent;
  
  bytesSent=send(sockfd, TEST_SEND_MESSAGE, TEST_SEND_MESSAGE_LEN,0);
  
  resultBool=bytesSent==TEST_SEND_MESSAGE_LEN;
  
  libcontra_log(LOGLEVEL_INFO, "I: ...... doing test %02i 1: v(%li) %5s (%5s) (%s:v(%d))",testcase, bytesSent, bool2str(resultBool), bool2str(true), basename(__FILE__), __LINE__);
  
  return resultBool;
}

/*!
*************************************************************************
*
* \fn bool do_test_5(int testcase)
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \brief testcase 1, send)() test <- recv test on server
*
* \return rc value of all tests from testcase 0
************************************************************************/
bool do_test_5(int testcase)
{
  bool result=true;
  int sockfd;
  
  /* get AF_INET socket with function from previous test */
  sockfd=do_test_1_1(testcase);
  if (sockfd == -1) {
    libcontra_log(LOGLEVEL_ERROR, "E: ... test %02i: could not create socket %i -> %s (%s:v(%d))", testcase, errno, strerror(errno), basename(__FILE__), __LINE__);
    return false;
  } else {
    libcontra_log(LOGLEVEL_INFO, "I: ... test %02i: socket created (%s:v(%d))", testcase, basename(__FILE__), __LINE__);
  }
  
  result=do_test_4_1(testcase, sockfd);
  result=result & do_test_5_1(testcase, sockfd);
  
  close(sockfd);
  
  libcontra_log(LOGLEVEL_INFO, "I: ... test %02i: %5s (%5s)",testcase, bool2str(result), bool2str(true));

  return result;
}



/*!
*************************************************************************
*
* \fn int main(int argc, char **argv)
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \brief main function of client
*
* @param[in] argc number of parameters
* @param[in] argv value of parameters
* 
* \return rc value of all tests
************************************************************************/
int main(int argc, char **argv)
{
 int result, index, testnumber=0;
 bool testresult=false;
 
 libcontra_log(LOGLEVEL_INFO, "I: start test_client");
 libcontra_log(LOGLEVEL_INFO, "I: ... parsing parameter");
 while(1) {
  result = getopt_long(argc, argv, "t:", long_options, &index);
  if (result == -1) break;
  switch (result) {
    case 't':
      testnumber=atoi(optarg);
      break;
    default: /* unknown */
      break;;
  }
 }
 libcontra_log(LOGLEVEL_INFO, "I: ... doing test %02i",testnumber);

 switch (testnumber) {
   case 0:
     testresult=do_test_0(0);
     break;
   case 1:
     testresult=do_test_1(1);
     break;
   case 4:
     testresult=do_test_4(4);
     break;
   case 5:
     testresult=do_test_5(5);
     break;
   default:
     libcontra_log(LOGLEVEL_INFO, "I: wrong testcase %i", testnumber);
     break;
 }
 libcontra_log(LOGLEVEL_INFO, "I: end test_client: %s", bool2str(testresult));

 if (testresult) return 0;
 else return 1;
}




/*! @} */

