#include "ltrlogapi.h"
#include "ltimer.h"

#include <stdlib.h>
#include <string.h>

#ifdef _WIN32
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
    return 1;
}
#endif

typedef struct {
    TLTRLOG_MSG *msg;
    unsigned cur_msg_buf_size;
    unsigned cur_rcv_size;
} t_ltrlog_internal;






LTRLOGAPI_DllExport(INT) LTRLOG_Init(TLTRLOG *hnd) {
    INT ret = hnd == NULL ? LTR_ERROR_INVALID_MODULE_DESCR : LTR_OK;
    if (ret == LTR_OK) {
        hnd->Size = sizeof(TLTRLOG);
        hnd->Internal = NULL;
        ret = LTR_Init(&hnd->Channel);
    }
    return ret;
}


LTRLOGAPI_DllExport(INT) LTRLOG_Open(TLTRLOG *hnd, DWORD net_addr, WORD net_port) {
    INT ret = hnd == NULL ? LTR_ERROR_INVALID_MODULE_DESCR : LTR_OK;
    if (ret == LTR_OK) {
        LTRLOG_Close(hnd);


        hnd->Channel.saddr = net_addr;
        hnd->Channel.sport = net_port;
        strcpy(hnd->Channel.csn, "#LOGPROTO");
        hnd->Channel.cc = LTR_CC_CHNUM_CONTROL;
        ret = LTR_Open(&hnd->Channel);
        if (ret==LTR_OK) {
            hnd->Internal = calloc(1, sizeof(t_ltrlog_internal));
            if (hnd->Internal == NULL) {
                ret = LTR_ERROR_MEMORY_ALLOC;
            } else {
                t_ltrlog_internal *internal = (t_ltrlog_internal*) hnd->Internal;
                unsigned alloc_size = LTRLOG_MSG_HDR_SIZE + 512;
                internal->msg = malloc(alloc_size);
                if (internal->msg == NULL) {
                    ret = LTR_ERROR_MEMORY_ALLOC;
                } else {
                    internal->cur_msg_buf_size = alloc_size;
                }
            }

        }


        if (ret != LTR_OK)
            LTRLOG_Close(hnd);
    }
    return ret;
}


LTRLOGAPI_DllExport(INT) LTRLOG_Close(TLTRLOG *hnd) {
    INT ret = hnd == NULL ? LTR_ERROR_INVALID_MODULE_DESCR : LTR_OK;
    if (ret == LTR_OK) {
        if (hnd->Internal!=NULL) {
            t_ltrlog_internal *internal = (t_ltrlog_internal*)hnd->Internal;
            free(internal->msg);
            free(hnd->Internal);
            hnd->Internal = NULL;
        }
        ret = LTR_Close(&hnd->Channel);
    }
    return ret;
}

LTRLOGAPI_DllExport(INT) LTRLOG_IsOpened(TLTRLOG *hnd) {
    return (hnd == NULL) ? LTR_ERROR_INVALID_MODULE_DESCR : LTR_IsOpened(&hnd->Channel);
}

LTRLOGAPI_DllExport(INT) LTRLOG_Shutdown(TLTRLOG *hnd) {
    INT ret = hnd == NULL ? LTR_ERROR_INVALID_MODULE_DESCR : LTR_OK;
    if (ret == LTR_OK) {
        ret = LTR_SendShutdown(&hnd->Channel);
    }
    return ret;
}



LTRLOGAPI_DllExport(INT) LTRLOG_GetNextMsg(TLTRLOG *hnd, TLTRLOG_MSG **msg, DWORD tout) {
    INT ret = hnd == NULL ? LTR_ERROR_INVALID_MODULE_DESCR : msg == NULL ?
                                LTR_ERROR_PARAMETERS : LTR_IsOpened(&hnd->Channel);
    if (ret == LTR_OK) {
        t_ltrlog_internal *internal = (t_ltrlog_internal*)hnd->Internal;
        t_ltimer tmr;
        INT rcv_size;
        *msg = NULL;

        ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout));
        /* если не принят весь заголовок, то сперва принимаем его */
        if (internal->cur_rcv_size < LTRLOG_MSG_HDR_SIZE) {
            rcv_size = LTR_RawRecv(&hnd->Channel, &((BYTE*)internal->msg)[internal->cur_rcv_size],
                    LTRLOG_MSG_HDR_SIZE-internal->cur_rcv_size, tout);
            if (rcv_size < 0) {
                ret = rcv_size;
            } else {
                internal->cur_rcv_size += rcv_size;
            }
        }

        if ((ret==LTR_OK) && (internal->cur_rcv_size==LTRLOG_MSG_HDR_SIZE)) {
            /* при необходимости увеличиваем буфер, но не менее чем в 2 раза */
            if (internal->cur_msg_buf_size < internal->msg->Size) {
                TLTRLOG_MSG *prev_hdr = internal->msg;

                internal->cur_msg_buf_size *= 2;
                if (internal->cur_msg_buf_size < internal->msg->Size)
                    internal->cur_msg_buf_size = internal->msg->Size;
                internal->msg = malloc(internal->cur_msg_buf_size);
                if (internal->msg==NULL) {
                    ret = LTR_ERROR_MEMORY_ALLOC;
                } else {
                    /* восстановление заголовока */
                    memcpy(internal->msg, prev_hdr, LTRLOG_MSG_HDR_SIZE);
                }
                free(prev_hdr);
            }

            /* прием тела сообщения */
            if ((ret==LTR_OK) && (internal->cur_rcv_size>=LTRLOG_MSG_HDR_SIZE)) {
                if (internal->cur_rcv_size < internal->msg->Size) {
                    rcv_size = LTR_RawRecv(&hnd->Channel, &((BYTE*)internal->msg)[internal->cur_rcv_size],
                            internal->msg->Size-internal->cur_rcv_size,
                            LTIMER_CLOCK_TICKS_TO_MS(ltimer_expiration(&tmr)));
                    if (rcv_size < 0) {
                        ret = rcv_size;
                    } else {
                        internal->cur_rcv_size += rcv_size;
                    }
                }
            }

            if ((ret==LTR_OK) && (internal->cur_rcv_size == internal->msg->Size)) {
                *msg = malloc(internal->cur_rcv_size) ;
                if (*msg == NULL) {
                    ret = LTR_ERROR_MEMORY_ALLOC;
                } else {
                    memcpy(*msg, internal->msg, internal->cur_rcv_size);
                }
                internal->cur_rcv_size = 0;
            }
        }
    }
    return ret;
}


LTRLOGAPI_DllExport(INT) LTRLOG_FreeMsg(TLTRLOG_MSG *msg) {
    free(msg);
    return LTR_OK;
}
