/**---------------------------------------------------------------------------
* 版权 2004 啄木鸟基金会
* 保留一切权利。
* 
* 重新分发或者使用源代码格式和二进制格式，无论是否修改，必须在符合
* 以下许可条件的情况下进行：
* 1. 重新分发的源代码必须保留上述版权信息、本许可条件列表以及下列
*    否认声明。
* 2. 重新分发的二进制格式程序必须再次产生上述版权信息，本许可条件列
*    表以及在本分发中的文档或者其他材料中提供的否认声明。
*
* 本软件是由作者和参与者“照此”提供，并且任何显式或隐式的保证，只
* 包含在内但不被其限制，否认任何商业性和特殊目的适切性的隐式担保
* 之效力。本软件的作者或参与者不对由于本软件造成的任何直接地、
* 间接地、连带地、特殊地、惩戒性地，或者由此而产生地损害（还包括
* 以下的不被其限制的内容在内：替代品或服务地取得；使用过程、数据、
* 或由此产生的收益之损失；或商业行为的中断）发生在即使被告知可能
* 出现上述损害后，仍设法摆脱使用本软件的过程中，无论其起因和任何
* 责任地推测，其是否属于合同范畴、限定的义务、或者民事侵权行为
*（包括疏忽及其他）都否认其责任。
* 
* 
*
* Copyright (c) 2004 The Woodpecker Foundation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
**---------------------------------------------------------------------------
*/

#include <stdio.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
#include "roo.h"
#include "utils.h"

int g_counter = 0;
int createPools(const char *FileName){
    int  i, size = 0;
    char **vals, tmp_item[64], tmp_str[128];
    char DefaultMaxsize[64], DefaultDurability[64];
    char name[164];
    int  maxsize, durability;

    
    strcpy(DefaultMaxsize, getinival(FileName, "COMMON", "DEFAULT_MAXSIZE", tmp_str, "500"));
    strcpy(DefaultDurability, getinival(FileName, "COMMON", "DEFAULT_DURABILITY", tmp_str, "90"));

    if ( (size = getinivals(FileName, "POOLS", "POOL", &vals)) > 0){
//        printf("size= %d\n", size);
        for (i = 0; i < size; i++){
//            printf("pool=%s\n", vals[i]);
              if (strcmp(get_item(vals[i], "name", name, "NONAME"), "NONAME") == 0)
                  err_quit("bad INI file, check POOLS/POOL in this INI file.\n");
//              printf("xname=%s\n", name);
              maxsize = atoi(get_item(vals[i], "maxsize", tmp_item, DefaultMaxsize));
              durability = atoi(get_item(vals[i], "durability", tmp_item, DefaultDurability));
              if (rooCreatePool(name, maxsize, durability) < 0)
                  err_quit("fail to creat pool:%s\n", vals[i]);
 //           printf("free...\n");
              free(vals[i]);
 //           printf("freed\n");
        }
        free(vals);
    }
    return size;
}
int getQueuePair(const char *FileName, char *req, char *resp)
{
    int i, size;
    char **vals, name[128];

    if ( (size = getinivals(FileName, "POOLS", "POOL", &vals)) > 0){
        strcpy(req, get_item(vals[0], "name", name, "NONAME"));
        strcpy(resp, get_item(vals[1], "name", name, "NONAME"));
        for (i = 0; i < size; i++)
            free(vals[i]);
        free(vals);
    }
}
 
int consume(char *reqname, char *respname)
{
    int size;
    char *pmsg;
    unsigned long sender;


//    if ( (size = charPull("/tmp/shmq_reqt_192.168.212.10", &pmsg, &sender )) == -1){
    if ( (size = charPull(reqname, &pmsg, &sender )) == -1){
       printf("rooPull error!\n");
       return -1;
    }


//    printf("GET:%s\n", pmsg);
    strcpy(pmsg + size - 20, "Changed!!!");
//    printf("PUT:%s\n", pmsg);

//    if ( charPush("/tmp/shmq_resp_192.168.212.10", pmsg, size, sender) < 0 ){
    if ( charPush(respname, pmsg, size, sender) < 0 ){
       printf("rooPush error!\n");
       return -1;
    }

    free(pmsg);
    return 0;
}

int produce(int buffsize, char *reqname, char *respname)
{
    char s[512 * 1024];
    char *s2;
    char mypid[16];
    char buff[1024];
    char buff2[1024];
    unsigned long  sender;

    memset(s, 'X', buffsize);
    s[0] = '[';
    sprintf(mypid, "%08d", getpid());
    memcpy(s + 1, mypid, 8);
    s[buffsize - 1] = '\n';
    s[buffsize - 2] = ']';

    // put a message which anyone can access
//    charPush("/tmp/shmq_reqt_192.168.212.10", s, buffsize, 0);
    charPush(reqname, s, buffsize, 0);

    // get a message which snd to me
//    charFetch("/tmp/shmq_resp_192.168.212.10", &s2, &sender);
    charFetch(respname, &s2, &sender);

   
    memcpy(buff, s, 1 + 8 + 10); buff[1 + 8 + 10] = '\n';
    memcpy(buff2, s2, 1 + 8 + 10); buff2[1 + 8 + 10] = '\n';

/*
    if (memcmp(s, s2, 9) != 0){
        printf("something wrong\n");
        exit(0);
    }
    if (memcmp(s2 + 9, "Changed!!!", 10) != 0){
        printf("something wrong: not changed\n");
        exit(0);
    }
    if (memcmp(s + 19, s2 + 19, buffsize - 19) != 0){
        printf("something wrong\n");
        exit(0);
    }
*/
/*
    printf("send=%s\n", buff);
    printf("recv=%s\n", buff2);
*/
    free(s2);
    return 0;
}

int do_nothing(int buffsize)
{
    char s[512 * 1024];

    memset(s, 'X', buffsize);
    s[0] = '[';
    s[buffsize - 1] = '\n';
    s[buffsize - 2] = ']';

    printf("do nothing ...\n");
    sleep(1);

    memset(s, 'M', buffsize);
}

int monitor(char *fname)
{
    int timeout = 100;
    int size = 2000;
    int timeval = 3;
    char buff[100 * 1024];

    createPools(fname);


    timeval = atoi(getinival(fname, "COMMON", "TIMEVAL", buff, "1"));

/*
    if (rooCreatePool("/tmp/shmq_reqt_192.168.212.10", size, timeout) < 0){
        printf("rooCreatePool error!\n");
        return -1;
    };
    if (rooCreatePool("/tmp/shmq_resp_192.168.212.10", size, timeout) < 0){;
        printf("rooCreatePoolerror!\n");
        return -1;
    };
*/
    for (;;){

//        printf("REQT info :%s\n", rooGetPoolInfo("/tmp/shmq_reqt_192.168.212.10", buff));
//        printf("RESP info :%s\n", rooGetPoolInfo("/tmp/shmq_resp_192.168.212.10", buff));

        printf("------------------------ current queue status ------------------------------\n");
        printf("%s\n", rooGetAllPoolInfo(buff));
        rooPoolGC();
        sleep(timeval);
    }
}
int print_usage()
{
    printf("Usage:   ctest m|p|c [pnum] [times] [kbytes] [inifile] \n");
    printf("                                             defalut INI file:shmq.ini\n" );
    printf("Example: ctest m myshmq.ini   -  start the manager configured with file myshmq.ini\n" );
    printf("         ctest p 200 500 1  -  start the producer[200 process, 500 times, 1kbytes]\n" );
    printf("         ctest c 3          -  start the consumer[3 process] with file shmq.ini\n" );
}
int main(int argc, char *args[])
{
    int i, j;
    int pid, stat;
    char buff[128], reqname[64], respname[64], inifname[64];

    struct timeval tpstart,tpend; 
    float timeuse; 

    int  producer_num = 1;
    int  consumer_num = 1;
    int  transaction_num = 1;
    int  produce_size = 1; //kbytes
    int  times;
    int  kbytes;

 
//    printf ("ID=%u\n", getMySID()); exit(0);

/*
    for (times = 0; times < 5 * (1 / 0.0001); times++ ){
        isleep(0.0001);
        printf("%d\n", times);
    }
    exit(0);
*/
    if (argc < 2){
        print_usage();
        return -1;
    } 
    if (strcmp(args[1], "p") == 0){ 
        if (argc < 5){
            print_usage();
            return -1;
        }
        if (argc > 5)
            strcpy(inifname, args[5]);
        else
            strcpy(inifname, "shmq.ini");
        getQueuePair(inifname, reqname, respname);
        printf("shmqueue REQT:%s|\n", reqname);
        printf("shmqueue RESP:%s|\n", respname);


        producer_num = atoi(args[2]);
        transaction_num = atoi(args[3]);
        produce_size = atoi(args[4]);
        gettimeofday(&tpstart,NULL); 
        for (i = 0 ; i < producer_num; i++){
            if (fork() == 0) {
                for(j = 0 ; j < transaction_num; j ++){
                    produce(produce_size * 1024, reqname, respname);
                    //sleep(1);
                }
                exit(0);
            }
        }
        printf("waiting for child quit...\n");
        while ( (pid = waitpid(-1, &stat, WNOHANG)) >= 0)
            ;
        gettimeofday(&tpend,NULL); 
        timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+ tpend.tv_usec-tpstart.tv_usec; 
        timeuse/=1000000; 
        times = producer_num * transaction_num;
        kbytes = times * produce_size;
        printf("process:        %d + %d\n", producer_num, consumer_num);
        printf("transaction:       %d\n", producer_num * transaction_num);
        printf("write:          %dkbytes\n", kbytes  * 2);
        printf("read:           %dkbytes\n", kbytes  * 2);
        printf("used time:      %fsec\n", timeuse);
        printf("transaction/sec:   %f\n", times/timeuse);
        printf("kbytes/sec:     %f\n", 4 * kbytes / timeuse);
        printf("sleep time:     %f\n", ROO_SLEEP_TIME);
    }
    else  if (strcmp(args[1], "c") == 0){ 
        if (argc < 3){
            print_usage();
            return -1;
        }
        if (argc > 3)
            strcpy(inifname, args[3]);
        else
            strcpy(inifname, "shmq.ini");
        getQueuePair(inifname, reqname, respname);
        printf("shmqueue REQT:%s|\n", reqname);
        printf("shmqueue RESP:%s|\n", respname);

        consumer_num = atoi(args[2]);
        for ( i = 0; i < consumer_num ; i++){
            if (fork() == 0){
                gettimeofday(&tpstart,NULL); 
                for(;;){
                    consume(reqname, respname); 
                    //sleep(1);
                }
                gettimeofday(&tpend,NULL); 
                timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+ tpend.tv_usec-tpstart.tv_usec; 
                timeuse/=1000000; 
                printf("[%d]Recv&Send %d, Size:%lbytes, Used Time:%fnsec\n", getpid(), 
                    transaction_num, transaction_num * produce_size, timeuse); 
            }
        }
        sleep(10000);
    }
    else  if (strcmp(args[1], "m") == 0){ 
        if (argc < 2){
            print_usage();
            return -1;
        }
        if (argc > 2)
            strcpy(inifname, args[2]);
        else
            strcpy(inifname, "shmq.ini");
        monitor(inifname);
    }
    else {
        print_usage();
        return -1;
    }
}

