/* 
 * This software is licensed under terms of the GNU GENERAL PUBLIC LICENSE
 *
 * This software 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 software  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 software.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Source code is available at http://ppcenter.free.fr
 */


#ifndef _INCLUDE_SATCOM_H_
#define _INCLUDE_SATCOM_H_

#include <stdio.h>
#include <windows.h>
#include <time.h>
#include <stdarg.h>

#include "log.h"
#include "misc.h"



/*-----------------------------------------------------------------------------------*/
/* Error codes. */

/* Error codes. */
#define SC_NOERROR            0

/* Unknown parameter in sc_set function. */
#define SC_ERROR_PARAMETER    1

/* Low-level communication error codes. */
#define SC_ERROR_PORT         5

#define SC_ERROR_PNOTOPEN     6

#define SC_ERROR_PINIT0      10
#define SC_ERROR_PINIT1      11
#define SC_ERROR_PINIT2      12
#define SC_ERROR_PINIT3      13

#define SC_ERROR_PSND0       20
#define SC_ERROR_PSND1       21
#define SC_ERROR_PSND2       22
#define SC_ERROR_PSND3       23

#define SC_ERROR_PRCV0       30
#define SC_ERROR_PRCV1       31
#define SC_ERROR_PRCV2       32
#define SC_ERROR_PRCV3       33



/* Packet-level communication error codes. */
// Send-receive error codes
#define SC_ERROR_PKTCHECKSUM  50
#define SC_ERROR_PKTSYNC      51 /* Freewing */
#define SC_ERROR_PKTHANDSHAKE 52 /* Freewing */
#define SC_ERROR_PKTSIZE      53 /* USB DataLink */
#define SC_ERROR_PKTDATALEN   54 /* USB DataLink */
#define SC_ERROR_COMPRESSION  55 /* Freewing */

// Init custom error codes
#define SC_ERROR_IOINIT0      60
#define SC_ERROR_IOINIT1      61
#define SC_ERROR_IOINIT2      62
#define SC_ERROR_IOINIT3      63

// Packet send custom error codes
#define SC_ERROR_PKTSND0      70
#define SC_ERROR_PKTSND1      71
#define SC_ERROR_PKTSND2      72
#define SC_ERROR_PKTSND3      73

// Packet receive custom error codes
#define SC_ERROR_PKTRCV0      80
#define SC_ERROR_PKTRCV1      81
#define SC_ERROR_PKTRCV2      82
#define SC_ERROR_PKTRCV3      83

// "Unimplemented function" error code
#define SC_ERROR_UNIMPLMNTD   90

// "Aborted by user" error code
#define SC_ERROR_ABORTED      100


/*-----------------------------------------------------------------------------------*/
/* Extra stuff. */

/* Send/Execute flag */
#define SC_LOAD   0
#define SC_EXEC   1





/*-----------------------------------------------------------------------------------*/
/* Debugger-related. */
#include "../../satcom_lib/saturn/debugger/scd_common.h"


/* Debugger infos for use by host program. (typically, activity display usage). */
/* See scd_common.h for contents informations. */
typedef struct _scd_info_t
{
    unsigned char cntr;

    /* lblDbgInfos1 */
    unsigned long magic_number;
    unsigned long struct_start_addr;
    unsigned char struct_version[2];

    /* lblDbgInfos2 */
    unsigned long data_start_addr;
    unsigned long data_length;

    /* lblDbgInfos3 */
    unsigned short data_readptr;
    unsigned short data_writeptr;
    unsigned long messages_size;
    unsigned long messages_count;

} scd_info_t;


/* Additional error codes (Debugger-related). */
#define SCD_ERROR_INIT -50




/**
 * SatCom class is an attempt to make easier use of multiple PC<->Saturn communication devices.
 * Feel free to override the sc_* methods with your device-specific functions in order to easily add support to it in SatLink.
 *
 * Also, this class contains stuff common to all devices, such as SatCom debugger support.
**/
class SatCom
{
public:
    char lastErrCode; /* Last error code different from "no error". */
    char errCode;     /* Last error code. */

public:
    //------------------------------------------------------
    // DLL-related stuff.
    SatCom(void)
    {
        /* Reset stacom internal stuff. */
        InternalReset();

        /*
         * Initialize debugger settings :
         *  File I/O folder set to host program current folder.
         *  No debug messages callback.
         */
        memset((void*)_dbg_folder, 0, sizeof(_dbg_folder)); _dbg_folder[0] = '.';

    }
    ~SatCom()
    {
scl_log(SCLT_DLLWRP, 9, "~SatCom->End");
        //End(); // <- Forbidden, because the function is already cleared.

        /* Reset stacom internal stuff (not needed, but ...). */
scl_log(SCLT_DLLWRP, 9, "~SatCom->InternalReset");
        InternalReset();
scl_log(SCLT_DLLWRP, 9, "~SatCom->OK");
    }


private:
    void display_warning(void)
    {
        scl_log(SCLT_DLLPG, 0, "No communication device opened !");
    }
public:
    //------------------------------------------------------
    // Device-specific stuff.
    // Override theses functions for your own device.

    /* Initialize the link interface.                                                                  */
    virtual char sc_start(void)
    {
        display_warning();
        return SC_NOERROR;
    }
    /* Set a single parameter.                                                                         */
    virtual char sc_set(char* parameter, char* value)
    {
        display_warning();
        return SC_NOERROR;
    }
    /* End the link interface.                                                                         */
    virtual char sc_end(void)
    {
        return SC_NOERROR;
    }
    /* Send data to Saturn. (execflag = SC_LOAD -> Send data only, SC_EXEC -> Send and execute)        */
    virtual char sc_send(unsigned long adr, unsigned char* buffer, unsigned long len, char execflag)
    {
        display_warning();
        return SC_NOERROR;
    }
    /* Receive data from Saturn.                                                                       */
    virtual char sc_receive(unsigned long adr, unsigned char* buffer, unsigned long len)
    {
        display_warning();
        return SC_NOERROR;
    }
    /* Get link interface DLL version.                                                                 */
    char _version[1024];
    virtual char* sc_version(void)
    {
        sprintf(_version, (char*)("SatCom void device"));
        return _version;
    }



public:
    ///* Set transfer progress callback.                                                                 */
    //virtual char sc_set_callback(Fct_sc_callback callback)                                           = 0;


    //------------------------------------------------------
    // Link related stuff.
    char Start(void)
    {
scl_log(SCLT_DLLWRP, 9, "[START]");
        errCode = sc_start();
        /* Flush logs to disk. */
        scl_logflush();

        if(errCode != SC_NOERROR) { lastErrCode = errCode; }
        return errCode;
    }
    char Set(char* parameter, char* value)
    {
scl_log(SCLT_DLLWRP, 9, "[SET]");
        errCode = sc_set(parameter, value);
        /* Flush logs to disk. */
        scl_logflush();

        if(errCode != SC_NOERROR) { lastErrCode = errCode; }
        return errCode;
    }

    char End(void)
    {
scl_log(SCLT_DLLWRP, 9, "[END]");
        errCode = sc_end();
        /* Flush logs to disk. */
        scl_logflush();

        if(errCode != SC_NOERROR) { lastErrCode = errCode; }
        return errCode;
    }

    char Send(unsigned long adr, unsigned char* buffer, unsigned long len, char execflag)
    {
scl_log(SCLT_DLLWRP, 9, "[SEND]");
        errCode = sc_send(adr, buffer, len, execflag);
        /* Flush logs to disk. */
        scl_logflush();
scl_log(SCLT_DLLWRP, 9, "[SEND->END]");

        if(errCode != SC_NOERROR) { lastErrCode = errCode; }
        return errCode;
    }

    char Receive(unsigned long adr, unsigned char* buffer, unsigned long len)
    {
scl_log(SCLT_DLLWRP, 9, "[RECEIVE]");
        /* Clear the target buffer before receiving data. */
        memset((void*)buffer, 0, len);
        errCode = sc_receive(adr, buffer, len);
        /* Flush logs to disk. */
        scl_logflush();
scl_log(SCLT_DLLWRP, 9, "[RECEIVE->END]");

        if(errCode != SC_NOERROR) { lastErrCode = errCode; }
        return errCode;
    }


    //------------------------------------------------------
    // UI-related stuff.
    char SetCallback(Fct_sc_callback callback)
    {
scl_log(SCLT_DLLWRP, 9, "[SetCallback]");
        errCode = sc_set_callback(callback);

        if(errCode != SC_NOERROR) { lastErrCode = errCode; }
        return errCode;
    }
    /**
     * Adjust log level and output to a given log type.
    **/
    char AdjustLogLevel(unsigned char type, unsigned char output, unsigned char level)
    {
scl_log(SCLT_DLLWRP, 9, "[AdjustLogLevel]");
        errCode = SC_NOERROR;

        scl_adjust(type, output, level);

        return errCode;
    }


#define _STR(_S_) (char*)(_S_)
    char* Version(void)
    {
scl_log(SCLT_DLLWRP, 9, "[Version]");
        return sc_version();
    }
    char* GetLastErrMsg(void)
    {
        /* No error. */
        if(errCode == SC_NOERROR)
            return _STR("No error");

        /* DLL Errors. */
        if(errCode == SC_ERROR_PARAMETER)
            return _STR("Unknown parameter");

        /* Low-level (port) errors. */
        if(errCode == SC_ERROR_PORT)
            return _STR("Port open error");
        if(errCode == SC_ERROR_PNOTOPEN)
            return _STR("Port is not opened");

        if(errCode == SC_ERROR_PINIT0)
            return _STR("Port init error #0");
        if(errCode == SC_ERROR_PINIT1)
            return _STR("Port init error #1");
        if(errCode == SC_ERROR_PINIT2)
            return _STR("Port init error #2");
        if(errCode == SC_ERROR_PINIT3)
            return _STR("Port init error #3");
        if(errCode == SC_ERROR_PSND0)
            return _STR("Port send error #0");
        if(errCode == SC_ERROR_PSND1)
            return _STR("Port send error #1");
        if(errCode == SC_ERROR_PSND2)
            return _STR("Port send error #2");
        if(errCode == SC_ERROR_PSND3)
            return _STR("Port send error #3");
        if(errCode == SC_ERROR_PRCV0)
            return _STR("Port receive error #0");
        if(errCode == SC_ERROR_PRCV1)
            return _STR("Port receive error #1");
        if(errCode == SC_ERROR_PRCV2)
            return _STR("Port receive error #2");
        if(errCode == SC_ERROR_PRCV3)
            return _STR("Port receive error #3");


        /* Interface-level (packet) errors. */
        if(errCode == SC_ERROR_PKTCHECKSUM)
            return _STR("Packet checksum error");
        if(errCode == SC_ERROR_PKTSYNC)
            return _STR("Packet synchronisation error");
        if(errCode == SC_ERROR_PKTHANDSHAKE)
            return _STR("Packet handshaking error");
        if(errCode == SC_ERROR_PKTSIZE)
            return _STR("Packet size error");
        if(errCode == SC_ERROR_PKTDATALEN)
            return _STR("Packet datalen error");
        if(errCode == SC_ERROR_COMPRESSION)
            return _STR("(De)Compression error");


        if(errCode == SC_ERROR_IOINIT0)
            return _STR("I/O init error #0");
        if(errCode == SC_ERROR_IOINIT1)
            return _STR("I/O init error #1");
        if(errCode == SC_ERROR_IOINIT2)
            return _STR("I/O init error #2");
        if(errCode == SC_ERROR_IOINIT3)
            return _STR("I/O init error #3");

        if(errCode == SC_ERROR_PKTSND0)
            return _STR("Packet send error #0");
        if(errCode == SC_ERROR_PKTSND1)
            return _STR("Packet send error #1");
        if(errCode == SC_ERROR_PKTSND2)
            return _STR("Packet send error #2");
        if(errCode == SC_ERROR_PKTSND3)
            return _STR("Packet send error #3");

        if(errCode == SC_ERROR_PKTRCV0)
            return _STR("Packet receive error #0");
        if(errCode == SC_ERROR_PKTRCV1)
            return _STR("Packet receive error #1");
        if(errCode == SC_ERROR_PKTRCV2)
            return _STR("Packet receive error #2");
        if(errCode == SC_ERROR_PKTRCV3)
            return _STR("Packet receive error #3");

        if(errCode == SC_ERROR_UNIMPLMNTD)
            return _STR("Unimplemented function");

        if(errCode == SC_ERROR_ABORTED)
            return _STR("Aborted by user");


        /* Debugger-related */
        if(errCode == SCD_ERROR_INIT)
            return _STR("Debugger init error");

        return _STR("Unknown error code");
    }

    //------------------------------------------------------
    // Debugger-related stuff.
public:
    /**
     * Reset debugger-related data.
    **/
    char DbgReset(void)
    {
        errCode = SC_NOERROR;

        _dbg_send_data     = 0;
        _dbg_received_data = 0;

        ulDbgStartAddress  = 0;

        memset((void*)&scdData, 0, sizeof(scd_data_t));
        memset((void*)&scdInfo, 0, sizeof(scd_info_t));

        return errCode;
    }


    /**
     * Set log callback functions.
    **/
    char SetLogCallback(int id, Fct_scl_callback callback)
    {
        errCode = SC_NOERROR;

        scl_set_callback(id, callback);

        return errCode;
    }

    /**
     * Set I/O folder.
     * When requesting file data from Saturn, requested file will be read from this folder.
    **/
    char DbgSetIOFolder(char* folder)
    {
        errCode = SC_NOERROR;

        strcpy(_dbg_folder, folder);

        return errCode;
    }

    /**
     * Send answer from a prompt request.
     * Note: you can send the answer whenever you want, however, answer not sent directly
     *       after a prompt request will be ignored.
    **/
    char DbgSendPromptAnswer(char* str)
    {
        errCode = SC_NOERROR;

        /* Set prompt answer data. */
        {
            memset((void*)(scdData.prompt_answer), '\0', SCD_PROMPT_SIZE);
            memcpy((void*)(scdData.prompt_answer), (void*)str, SCD_PROMPT_SIZE);
            scdData.prompt_answer[SCD_PROMPT_SIZE-1] = '\0';
        }

        /* Send prompt answer data. */
        {
            unsigned long offs, len;
            offs = (unsigned long)(&(scdData.prompt_answer)) - (unsigned long)(&scdData);
            len  = strlen((const char*)(scdData.prompt_answer)) + 1;
            SCD_Send(scdData.prompt_answer, len, ulDbgStartAddress + offs, SC_LOAD);
            if(errCode != SC_NOERROR)
            {
                lastErrCode = errCode;
                return errCode;
            }
        }

        /* Set "prompt answer complete" flag, so that Saturn knows that requested data is available. */
        {
            unsigned long offs, len;
            unsigned char tmp = 1; /*proc_complete = 1*/
            offs = (unsigned long)(&(scdData.proc_complete)) - (unsigned long)(&scdData);
            len  = sizeof(unsigned char);
            SCD_Send(&tmp, len, ulDbgStartAddress + offs, SC_LOAD);
            if(errCode != SC_NOERROR)
            {
                lastErrCode = errCode;
                return errCode;
            }
        }

        return errCode;
    }

    /**
     * Debugger informations return function.
     * For use when host program wants to display debugger activity.
    **/
    scd_info_t* DbgGetInfos(void)
    {
        return &scdInfo;
    }

    /**
     * Debugger polling function.
     * If needed, read data from Saturn in order to check if debugger is active or not. If active, init debugger-related data.
     * Then, read data from Saturn in order to check debugger state.
     *
     * Note: When receiving message/file/prompt data, concerned function and/or callback will be called.
    **/
    char DbgPoll(void)
    {
        errCode = SC_NOERROR;

        /* If needed, get address to debugger structure. */
        if(ulDbgStartAddress == 0)
        {
            scl_log(SCLT_DLLWRP, 1, "[DBG]Start debugger init");
            scdInfo.cntr++;

            unsigned char ucScdAbsolute[SCD_ABS_SIZE];
            sc_global_t* sc_global = (sc_global_t*)SC_GLOBAL_DATA_ADR;
            unsigned long scd_absolute_address = (unsigned long)(&(sc_global->scd_ptr));
            SCD_Receive(scd_absolute_address, SCD_ABS_SIZE, ucScdAbsolute);
            if(errCode != SC_NOERROR)
            {
                lastErrCode = errCode;
                return errCode;
            }

            unsigned long  ulMagicNumber = Str2Ulong(ucScdAbsolute + 4);
            scdInfo.magic_number = ulMagicNumber;
            scl_log(SCLT_DLLWRP, 1, "[RBuffer]MagicNumber = 0x%08X (Expected = 0x%08X)", ulMagicNumber, SCD_ABS_MAGIC);
            if(ulMagicNumber == SCD_ABS_MAGIC)
            {
                memset((void*)&scdData, 0, sizeof(scdData));

                ulDbgStartAddress = Str2Ulong(ucScdAbsolute + 0);
                scdInfo.struct_start_addr = ulDbgStartAddress;
                scl_log(SCLT_DLLWRP, 1, "->Start Address = 0x%08X", ulDbgStartAddress);

                /* Get circular buffer parameters. */
                unsigned long offs, len;
                unsigned char tmp[256];
                unsigned char* ptr = (unsigned char*)tmp;
                offs = 0;
                len  = (unsigned long)(&(scdData.rbuffer)) - (unsigned long)(&scdData);
                SCD_Receive(ulDbgStartAddress + offs, len, (unsigned char*)tmp);
                if(errCode != SC_NOERROR)
                {
                    lastErrCode = errCode;
                    return errCode;
                }

                offs = (unsigned long)(&(scdData.version)) - (unsigned long)(&scdData);
                scdData.version[0]    = ptr[offs+0];
                scdData.version[1]    = ptr[offs+1];
                scdInfo.struct_version[0] = scdData.version[0];
                scdInfo.struct_version[1] = scdData.version[1];
                scl_log(SCLT_DLLWRP, 1, "[RBuffer] ->[O%2d]scdData.version       = %d.%d", offs, scdData.version[0], scdData.version[1]);

                offs = (unsigned long)(&(scdData.structsz)) - (unsigned long)(&scdData);
                scdData.structsz      = Str2Ushort(ptr+offs);
                scl_log(SCLT_DLLWRP, 1, "[RBuffer] ->[O%2d]scdData.structsz      = %d", offs, scdData.structsz);

                /* Version check. */
                if((scdData.version[0] != SCD_VERSION_MAJOR)
                || (scdData.structsz != sizeof(scd_data_t)))
                {
                    scl_log(SCLT_DLLWRP, 0, "[DBG]Version check failed ! PC(version = %d.%d, struct sz=%d)", SCD_VERSION_MAJOR, SCD_VERSION_MINOR, sizeof(scd_data_t));
                    ulDbgStartAddress = 0;
                }
                else
                {
                    scl_log(SCLT_DLLWRP, 1, "[DBG]Version check OK.");

                    offs = (unsigned long)(&(scdData.readptr)) - (unsigned long)(&scdData);
                    scdData.readptr       = Str2Ushort(ptr+offs);
                    scdInfo.data_readptr = scdData.readptr;
                    scl_log(SCLT_DLLWRP, 1, "[RBuffer] ->[O%2d]scdData.readptr       = 0x%04X", offs, scdData.readptr);

                    offs = (unsigned long)(&(scdData.writeptr)) - (unsigned long)(&scdData);
                    scdData.writeptr      = Str2Ushort(ptr+offs);
                    scdInfo.data_writeptr = scdData.writeptr;
                    scl_log(SCLT_DLLWRP, 1, "[RBuffer] ->[O%2d]scdData.writeptr      = 0x%04X", offs, scdData.writeptr);

                    offs = (unsigned long)(&(scdData.structsz)) - (unsigned long)(&scdData);
                    scdData.structsz      = Str2Ushort(ptr+offs);
                    scl_log(SCLT_DLLWRP, 1, "[RBuffer] ->[O%2d]scdData.structsz      = %d", offs, scdData.structsz);

                    offs = (unsigned long)(&(scdData.dbg_on)) - (unsigned long)(&scdData);
                    scdData.dbg_on        = Str2Ushort(ptr+offs);
                    scl_log(SCLT_DLLWRP, 1, "[RBuffer] ->[O%2d]scdData.dbg_on        = %d", offs, scdData.dbg_on);

                    offs = (unsigned long)(&(scdData.buffer_size)) - (unsigned long)(&scdData);
                    scdData.buffer_size   = Str2Ulong(ptr+offs);
                    scdInfo.data_length = scdData.buffer_size;
                    scl_log(SCLT_DLLWRP, 1, "[RBuffer] ->[O%2d]scdData.buffer_size   = %d", offs, scdData.buffer_size);

                    offs = (unsigned long)(&(scdData.start_address)) - (unsigned long)(&scdData);
                    scdData.start_address = Str2Ulong(ptr+offs);
                    scdInfo.data_start_addr = scdData.start_address;
                    scl_log(SCLT_DLLWRP, 1, "[RBuffer] ->[O%2d]scdData.start_address = 0x%08X", offs, scdData.start_address);

                    offs = (unsigned long)(&(scdData.end_address)) - (unsigned long)(&scdData);
                    scdData.end_address   = Str2Ulong(ptr+offs);
                    scl_log(SCLT_DLLWRP, 1, "[RBuffer] ->[O%2d]scdData.end_address   = 0x%08X", offs, scdData.end_address);

                    { /** Set debug flag to ON, and update it. **/
                        scl_log(SCLT_DLLWRP, 1, "[DBG]Set debug flag to ON");
                        scdData.dbg_on = 1;

                        unsigned long offs, len;
                        unsigned char tmp = scdData.dbg_on;
                        offs = (unsigned long)(&(scdData.dbg_on)) - (unsigned long)(&scdData);
                        len  = sizeof(unsigned char);
                        SCD_Send(&tmp, len, ulDbgStartAddress + offs, SC_LOAD);
                        if(errCode != SC_NOERROR)
                        {
                            lastErrCode = errCode;
                            return errCode;
                        }
                    }
                }
            }
        }


        /* Debugger init OK ? */
        if(ulDbgStartAddress == 0)
        {
            scl_log(SCLT_DLLWRP, 0, "Error: could not init debugger !");
            errCode = SCD_ERROR_INIT;

            lastErrCode = errCode;
            return errCode;
        }

        unsigned long offs, len;
        unsigned char tmp[256];
        unsigned char* ptr = (unsigned char*)tmp;


        /* Get circular buffer pointers. */
        offs = (unsigned long)(&(scdData.readptr)) - (unsigned long)(&scdData);
        len  = 2*sizeof(unsigned short);
        SCD_Receive(ulDbgStartAddress + offs, len, tmp);
        if(errCode != SC_NOERROR)
        {
            lastErrCode = errCode;
            return errCode;
        }
        scdData.readptr  = Str2Ushort(ptr+0);
        scdData.writeptr = Str2Ushort(ptr+2);
        scdInfo.data_readptr = scdData.readptr;
        scdInfo.data_writeptr = scdData.writeptr;
        scdInfo.messages_size  = 0;
        scdInfo.messages_count = 0;

        /* If needed, get and display elements from circular buffer. */
        if(scdData.readptr != scdData.writeptr)
        {
            unsigned short rptr = scdData.readptr;
            unsigned short wptr = scdData.writeptr;

            /* Read all the data in the circular buffer. */
            unsigned char data[SCD_CIRCBUFF_MAXSIZE];
            memset((void*)data, 1, SCD_CIRCBUFF_MAXSIZE);

            unsigned long len = ReadCircBufferData(&scdData, data);

            /* All the messages are concatenated in the received
             * data, so extract them individually.
             */
            unsigned long data_offs = 0;
            do
            {
                unsigned char msg_len  = data[data_offs + 0];
                unsigned char msg_type = data[data_offs + 1];
                /* Avoid irregular lengths  */
                if((msg_len <= 2) || ((data_offs + msg_len) > SCD_CIRCBUFF_SIZE))
                {
                    LogDebuggerError(rptr, wptr, data, len, data_offs, "[DBG]Error: invalid length (%d)", msg_len);
                    break;
                }

                unsigned char msg[255 + 1];
                memset((void*)msg, 0, sizeof(msg));
                memcpy((void*)msg, (void*)(data+data_offs+2), msg_len-2);
                /* For debug message (0: console, 1~: string grid) */
                unsigned char msg_id    = 0;
                unsigned char prompt_on = 0;

                /* Decide what to do according to the message type. */
                switch(msg_type)
                {
                /* Debug message. */
                case(SCD_TYPE_PROMPT):
                    scl_log(SCLT_DLLWRP, 0, "[PROMPT] Waiting for answer ...");
                    prompt_on = 1;
                    // No break here.
                case(SCD_TYPE_MSG):
                    msg_id = msg[0];
                    scl_log(SCLT_DLLWRP, 2, "ID%03d>\"%s\"", msg_id, msg+1);

                    /* Send the debug message to the host program. */
                    /* Parameters are: log type (msg/console), grid line + prompt_on, message. */
                    scl_log((msg_id == 0 ? SCLT_SATMSG : SCLT_SATGRD) | (prompt_on == 1 ? SCLT_PROMPT_MSK : 0), 
                            msg_id, 
                            (char*)(msg+1));

                    break;

                case(SCD_TYPE_RFILE):
                case(SCD_TYPE_WFILE):
                case(SCD_TYPE_AFILE):
                case(SCD_TYPE_SFILE):
                    SCD_FileIO(
                        /*type       */msg_type, 
                        /*foffset    */Str2Ulong(msg + 0), 
                        /*frw_address*/Str2Ulong(msg + 4), 
                        /*frw_len    */Str2Ulong(msg + 8), 
                        /*fname      */msg+12);
                    break;

                /* Unknown type. */
                default:
                    LogDebuggerError(rptr, wptr, data, len, data_offs, "[DBG]Warning: unknown type (%d)", msg_type);
                }

                /* Update offset to the next nessage. */
                data_offs += msg_len;
                scdInfo.messages_size += msg_len;
                scdInfo.messages_count++;
            } while(data_offs < len); /* Last message ? */
        } /* Data in Circular Buffer. */

        return errCode;
    }





private:
    //------------------------------------------------------
    // Debugger-related stuff.
    scd_data_t          scdData;
    unsigned long       ulDbgStartAddress;
    char                _dbg_folder[4096];
    unsigned long       _dbg_send_data;
    unsigned long       _dbg_received_data;

public:
    //------------------------------------------------------
    // Debugger-related stuff (for use by host program).
    scd_info_t          scdInfo;


    //------------------------------------------------------
    // Functions for wrappper internal use.
private:

    /* Reset SatCom internal stuff. */
    void InternalReset(void)
    {
        scl_log(SCLT_DLLWRP, 9, "[InternalReset]");
        lastErrCode         = SC_NOERROR;
        errCode             = SC_NOERROR;

        /* Reset debugger stuff. */
        DbgReset();
    }



    void LogDebuggerError(unsigned short rptr, unsigned short wptr, 
                          unsigned char* data, unsigned long len, unsigned long data_offs, 
                          const char* str, int i)
    {
        scl_log(SCLT_DLLWRP, 0, "");
        SYSTEMTIME sTime; GetLocalTime(&sTime);
        scl_log(SCLT_DLLWRP, 0, "[%02d:%02d:%02d:%03d]Error happened !", sTime.wHour, sTime.wMinute, sTime.wSecond, sTime.wMilliseconds);
        scl_log(SCLT_DLLWRP, 0, str, i);
        scl_log(SCLT_DLLWRP, 0, "pSc->errCode=%d, pSc->lastErrCode=%d", errCode, lastErrCode);
        scl_log(SCLT_DLLWRP, 0, "RD/WR PTR = 0x%08X / 0x%08X, data len=%d(0X%X) bytes", rptr, wptr, len, len);

        /* Display data */
        for(unsigned long ii=0; ii<len; ii++)
        {
            scl_log(SCLT_DLLWRP, 0, " data[%3d]=0x%02X", ii, data[ii]);
        }
    }

    unsigned short Str2Ushort(unsigned char* str)
    {
        unsigned short usRet;
        usRet  = (str[0] <<  8) & 0x0000FF00;
        usRet |= (str[1]      ) & 0x000000FF;
        return usRet;
    }
    void Ushort2Str(unsigned short usData, unsigned char* str)
    {
        str[0] = (usData >>  8) & 0xFF;
        str[1] = (usData >>  0) & 0xFF;
    }
    unsigned long Str2Ulong(unsigned char* str)
    {
        unsigned long  ulRet;
        ulRet  = (str[0] << 24) & 0xFF000000;
        ulRet |= (str[1] << 16) & 0x00FF0000;
        ulRet |= (str[2] <<  8) & 0x0000FF00;
        ulRet |= (str[3]      ) & 0x000000FF;
        return ulRet;
    }
    void Ulong2Str(unsigned long ulData, unsigned char* str)
    {
        str[0] = (ulData >> 24) & 0xFF;
        str[1] = (ulData >> 16) & 0xFF;
        str[2] = (ulData >>  8) & 0xFF;
        str[3] = (ulData >>  0) & 0xFF;
    }


    void SCD_Receive(unsigned long address, unsigned long size, unsigned char* buffer)
    {
        scl_log(SCLT_DLLWRP, 3, "[DBG]Receive %d bytes from address 0x%08X ...", size, address);
        if(Receive(address, buffer, size) != SC_NOERROR)
        {
            scl_log(SCLT_DLLWRP, 0, " *** Receive error ! (Code = %d, Message = \"%s\")", errCode, GetLastErrMsg());
        }
        else
        {
            _dbg_received_data += size;
            scl_log(SCLT_DLLWRP, 3, "[DBG]Receive OK, Total received data = %d bytes", _dbg_received_data);
        }
    }
    void SCD_Send(unsigned char* buffer, unsigned long size, unsigned long address, bool exec_flag)
    {
        scl_log(SCLT_DLLWRP, 3, "[DBG]Send to %d bytes to address 0x%08X ...", size, address);
        if(Send(address, buffer, size, (exec_flag ? SC_EXEC : SC_LOAD)) != SC_NOERROR)
        {
            scl_log(SCLT_DLLWRP, 0, " *** Send error ! (Code = %d, Message = \"%s\")", errCode, GetLastErrMsg());
        }
        else
        {
            _dbg_send_data += size;
            scl_log(SCLT_DLLWRP, 3, "[DBG]Send OK, Total send data = %d bytes", _dbg_received_data);
        }
    }
    /**
     * Read all the data in the circular buffer.
     * Note: need at most SCD_CIRCBUFF_SIZE bytes in output buffer.
     * It modifies circular buffer's read pointer.
    **/
    //#include "../satcom_lib/scd_circbuffer.inc.c"
    //unsigned long scd_cbread(scd_data_t* d, unsigned char* data)
    unsigned long ReadCircBufferData(scd_data_t* d, unsigned char* data)
    {
        unsigned long read_size=0;

        /* Circular buffer is empty. */
        if(d->writeptr == d->readptr) return read_size;

        if(d->writeptr > d->readptr)
        {
            read_size = d->writeptr - d->readptr;
            //memcpy((void*)(data), 
            //       (void*)(d->start_address+d->readptr), 
            //       read_size);
            {/** Read data from Saturn. **/
                SCD_Receive(d->start_address+d->readptr, read_size, data);
                scl_log(SCLT_DLLWRP, 5, "[CircBuffer]Read all = %d bytes [%d]", read_size, data[0]);
            }
        }
        else
        {
            read_size = d->buffer_size - d->readptr;
            //memcpy((void*)(data), 
            //       (void*)(d->start_address+d->readptr), 
            //       read_size);
            {/** Read first half data from Saturn. **/
                SCD_Receive(d->start_address+d->readptr, read_size, data);
                scl_log(SCLT_DLLWRP, 5, "[CircBuffer]Read First = %d bytes [%d]", read_size, data[0]);
            }

            //memcpy((void*)(data+read_size), 
            //       (void*)(d->start_address), 
            //       d->writeptr);
            {/** Read second half data from Saturn. **/
                SCD_Receive(d->start_address, d->writeptr, data+read_size);
                scl_log(SCLT_DLLWRP, 5, "[CircBuffer]Read second = %d bytes [%d]", d->writeptr, data[read_size]);
            }

            read_size += d->writeptr;
        }

        /* Update read pointer. */
        d->readptr = d->writeptr;
        {/** Write read pointer to Saturn. **/
            unsigned long offs, len;
            unsigned char tmp[2];
            Ushort2Str(d->readptr, tmp);
            offs = (unsigned long)(&(scdData.readptr)) - (unsigned long)(&scdData);
            len  = sizeof(unsigned short);
            SCD_Send(tmp, len, ulDbgStartAddress + offs, SC_LOAD);
        }
        return read_size;
    }


    /*
     * Send/Receive file data with debugger.
     */
#define SCD_FILEIO_MAXLEN (4*1024*1024)
    void SCD_FileIO(unsigned char type, unsigned long foffset, unsigned long  frw_address, unsigned long  frw_len, unsigned char* fname)
    {
        /* Limit transfer size to 4MB. */
        if(frw_len > SCD_FILEIO_MAXLEN) frw_len = SCD_FILEIO_MAXLEN;

        scl_log(SCLT_DLLWRP, 1, "[FILE] Type=%c, offs=%d, rwadr=0x%08X, rwlen=%d, fname=\"%s\"", type, foffset, frw_address, frw_len, fname);

        FILE* fp;
        char file[sizeof(_dbg_folder)];

        /* Set path to requested file. */
        sprintf(file, "%s\\%s", _dbg_folder, fname);
        scl_log(SCLT_DLLWRP, 1, "[FILE] Type=%c, path=\"%s\"", type, file);


        /* Send/Receive data. */
        unsigned char* buff = (unsigned char*)malloc(frw_len);
        /* request return data. */
        unsigned long frw_len_ret = 0; /* Return value when the file couldn't be opened. */
        if(buff)
        {
            if(type == SCD_TYPE_RFILE)
            { /* File read request from Saturn. */
                fp=fopen(file, "rb");
                if(fp)
                {
                    fseek(fp, foffset, SEEK_SET);
                    frw_len_ret = fread(buff, 1, frw_len, fp);
                    fclose(fp); //fp=NULL;
                }
                SCD_Send(buff, frw_len_ret, frw_address, SC_LOAD);
            }
            else if(type == SCD_TYPE_WFILE)
            { /* File write request from Saturn. */
                SCD_Receive(frw_address, frw_len, buff);
                fp=fopen(file, "wb");
                if(fp)
                {
                    fseek(fp, foffset, SEEK_SET);
                    frw_len_ret = fwrite(buff, 1, frw_len, fp);
                    fclose(fp); //fp=NULL;
                }
            }
            else if(type == SCD_TYPE_AFILE)
            { /* Append to file write request from Saturn. */
                SCD_Receive(frw_address, frw_len, buff);
                fp=fopen(file, "ab");
                if(fp)
                {
                    frw_len_ret = fwrite(buff, 1, frw_len, fp);
                    fclose(fp); //fp=NULL;
                }
            }
            else //if(type == SCD_TYPE_SFILE)
            { /* File size request from Saturn. */
                unsigned long fsize=0;
                fp=fopen(file, "rb");
                if(fp)
                {
                    fseek(fp, 0, SEEK_END);
                    fsize = ftell(fp);
                    frw_len_ret = fsize;
                    fclose(fp); //fp=NULL;
                }
                Ulong2Str(fsize, buff);
                SCD_Send(buff, frw_len, frw_address, SC_LOAD);
            }
            free(buff);// buff = NULL;
        }
        else
        {
            scl_log(SCLT_DLLWRP, 0, "[FILE] Error in buffer malloc !");
        }


        /* Send size actually read/written. */
        {
            unsigned long offs, len;
            unsigned char tmp[4];
            Ulong2Str(frw_len_ret, tmp); /* Set return data. */
            offs = (unsigned long)(&(scdData.fileio_size)) - (unsigned long)(&scdData);
            len  = sizeof(unsigned long);
            SCD_Send(tmp, len, ulDbgStartAddress + offs, SC_LOAD);
        }

        /* Set "transfer complete" flag, so that Saturn knows that requested data is available. */
        {
            unsigned long offs, len;
            unsigned char tmp = 1; /*proc_complete = 1*/
            offs = (unsigned long)(&(scdData.proc_complete)) - (unsigned long)(&scdData);
            len  = sizeof(unsigned char);
            SCD_Send(&tmp, len, ulDbgStartAddress + offs, SC_LOAD);
        }
    }

};




#endif // _INCLUDE_SATCOM_H_
