/**---------------------------------------------------------------------------
* 版权 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 <sys/types.h>
#include <sys/time.h>
#include "roo.h"
#include "shmqueue.h"
#include "spsem.h"


rooProfile g_Profile;
int        g_ProfileInitFlag = 0;

unsigned long CreateMicId(const char *pcchUid)
{
    static unsigned long ulMicId;
    static unsigned int uiChar;
 
    ulMicId = 5381;
    while (uiChar = (unsigned int)*pcchUid++)
    {
        /* ulMicId * 33 + uiChar */
        ulMicId = ((ulMicId << 5) + ulMicId) + uiChar;
    }
 
    return ulMicId;
}
 
unsigned long GenSID()
{
    char buff[64];

    struct timeval today;
 
    gettimeofday(&today, NULL);
    sprintf(buff, "%d_%d_%d", getpid(), today.tv_sec, today.tv_usec);

    return CreateMicId((const char*)buff);
}

void initProfile()
{
    //default profile define
    if (g_ProfileInitFlag == 0){
        g_Profile.RemoveFlag = ROO_FLAG_REMOVE;
        g_Profile.WriteTimeOut = ROO_NOHANG;      //if operation fail, return immediately, when push
        g_Profile.ReadTimeOut = 30;              //if operation fail, try 30 secs when pull, popup, fetch
        g_Profile.SenderID = GenSID();          //generated Sender ID 
        g_ProfileInitFlag = 1;
    }
}
void getProfile(rooProfile *profile)
{
    initProfile();
    memcpy(profile, &g_Profile, sizeof(rooProfile));
}

void setProfile(rooProfile *profile)
{
    initProfile();
    memcpy(&g_Profile, profile, sizeof(rooProfile));
}

unsigned long getMySID()
{
    initProfile();
    return g_Profile.SenderID;
}

int getTimeOut(int FunctionType)
{
    initProfile();
    if (FunctionType == ROO_FUNCTYPE_WRITE)
    	return g_Profile.WriteTimeOut;
    else
    	return g_Profile.ReadTimeOut;
}
void setTimeOut(int FunctionType, int TimeOut)
{
    initProfile();
    if (FunctionType == ROO_FUNCTYPE_WRITE)
    	g_Profile.WriteTimeOut = TimeOut;
    else
    	g_Profile.ReadTimeOut = TimeOut;
}
int getRemoveFlag()
{
    return g_Profile.RemoveFlag;
}
void setRemoveFlag(int RemoveFlag)
{
    if ((RemoveFlag == ROO_FLAG_NOTREMOVE) || (RemoveFlag == ROO_FLAG_REMOVE) )
        g_Profile.RemoveFlag = RemoveFlag;
}


int isleep(float i)
{
    struct timespec interval, remain;
 
    interval.tv_sec = 0;
    interval.tv_nsec = i * 1000000000; 
    nanosleep (&interval, &remain);
}



/*
 * return : 0 if succ or -1 if fail
 */
int rooCreatePool(char *PoolName, int MaxRegion, int TimeOut)
{
    void *shmq;
    char info[128];

    shmq = shmqueue_init(PoolName, MaxRegion, TimeOut);
    if (shmq == NULL) 
        return -1;
    printf("Pool Created:%s\n", get_shmq_info(shmq, info));
    return 0;
}
 
/*
 * return : 0 if succ or -1 if fail
 */ 
int rooDestroyPool(char *PoolName)
{
    void *shmq;

    shmq = shmqueue_init(PoolName, 0, 0);
    if (shmq == NULL) 
        return -1;

    shmqueue_removeall(shmq);
    shmdt(shmq);
    shmqueue_destroy(PoolName);
    return 0;
}

char *rooGetPoolInfo(char *PoolName, char *Buffer)
{
    void *shmq;

    shmq = shmqueue_init(PoolName, 0, 0);
    if (shmq == NULL) 
        return NULL;

    get_shmq_info(shmq, Buffer);
    return Buffer;
}

char *rooGetAllPoolInfo(char *Buffer)
{
    initProfile();
    return get_allshmq_info(Buffer);
}


void rooPoolGC()
{
    initProfile();
    shmqueue_gc_all();
}
    
int _rooWait(int Function, char *PoolName, void **Buffer, int Size, unsigned long *ToWho);
/*
 * return push message size
 */
int rooPush(char *PoolName, void *Buffer, int Size, unsigned long Receiver)
{
    return _rooWait(ROO_FUNC_PUSH, PoolName, &Buffer, Size, &Receiver);
}

/**
 * return pull message size
 */    
int rooPull(char *PoolName , void **Buffer, unsigned long *Sender)
{
    return _rooWait(ROO_FUNC_PULL, PoolName, Buffer, 0, Sender);
}

/**
 * return popup message size
 */    
int rooPopup(char *PoolName , void **Buffer, unsigned long *Sender)
{
    return _rooWait(ROO_FUNC_POPUP, PoolName, Buffer, 0, Sender);
}

/**
 * return fetch message size
 */    
int rooFetch(char *PoolName, void **Buffer, unsigned long *Sender)
{
    return _rooWait(ROO_FUNC_FETCH, PoolName, Buffer, 0, Sender);
}

int _rooWait(int Function, char *PoolName, void **Buffer, int Size, unsigned long *ToWho)
{

    //TODO: semaphore notify mode will be better

    int  retval, Ticker, TimeOut, counter;
    void *shmaddr;

    initProfile();
    shmaddr = shmqueue_init(PoolName, 0, 0);
    if (NULL == shmaddr){
        printf("[D]shmqueue_init error!\n");
        return -1;
    }

    if (Function == ROO_FUNC_PUSH)
        TimeOut = g_Profile.WriteTimeOut;
    else
        TimeOut = g_Profile.ReadTimeOut;


    Ticker = TimeOut * (1 / (ROO_SLEEP_TIME * 200));
    printf ("Ticker=%d, TimeOut=%d, TIME=%f\n", Ticker, TimeOut, ROO_SLEEP_TIME);
    counter = 0;
    for(;;){
        if (Function == ROO_FUNC_PUSH){
            retval = shmqueue_push(shmaddr, *Buffer, Size, getMySID(), *ToWho);
            if ((retval > 0)  || (retval == -1))
                break;
        }
        else if (Function == ROO_FUNC_PULL){
            if ( (retval = shmqueue_pull(shmaddr, Buffer, ToWho)) > 0){
                break;
            }
        }
        else if (Function == ROO_FUNC_POPUP){
            if ( (retval = shmqueue_popup(shmaddr, Buffer, ToWho)) > 0)
                break;
        }
        else if (Function == ROO_FUNC_FETCH){
            if ( (retval = shmqueue_fetch(shmaddr, Buffer, ToWho, getMySID(),
                g_Profile.RemoveFlag, CMP_BY_RECEIVER)) > 0)
                break;
        }
        else
            return -1;

        if (TimeOut < 0)
              isleep(ROO_SLEEP_TIME);
        else {
            if (Ticker <= 0)  //time is up!
                break;
            isleep(ROO_SLEEP_TIME);
            Ticker--;
            //printf("ticket=%d\n", Ticker);
        }
        counter++;
    //    printf("counter = %d\n", counter);

        if (retval >= 0) {
            break;
        }
    }
    return retval;
}

int charPush(char *PoolName, char *Buffer, int Size, unsigned long Receiver)
{
    return rooPush(PoolName, (void *)Buffer, Size, Receiver);
}
int charPull(char *PoolName , char **Buffer, unsigned long *Sender)
{
    return rooPull(PoolName, (void **)Buffer, Sender);
}
int charPopup(char *PoolName , char **Buffer, unsigned long *Sender)
{
    return rooPopup(PoolName, (void **)Buffer, Sender);
}
int charFetch(char *PoolName, char **Buffer, unsigned long *Sender)
{
    return rooFetch(PoolName, (void **)Buffer, Sender);
}


int g_hello = 0;
int hello(char *PoolName, char *Buffer, int Size, unsigned long *Sender)
{
    char s[64];

    printf("HELLO swig:\n");
    *Sender = 9999;
    strcpy(s, "Here is the SWIG world\n");
    memcpy(Buffer, s, strlen(s) + 1);

    g_hello++;
    printf("g_hello=%d\n", g_hello);

    return strlen(s) + 1;
}
