/*
 * oc_ema_modem_at.c
 *
 * Created: 6/24/2019 11:47:33 AM
 * 
 *  ######################################################################################################
 *  DISCLAIMER:
 *  OptConnect Management, LLC provides this documentation in support of its products for the internal use
 *  of its current and prospective customers. The publication of this document does not create any other
 *  right or license in any party to use any content contained in or referred to in this document and any
 *  modification or redistribution of this document is not permitted. While efforts are made to ensure
 *  accuracy, typographical and other errors may exist in this document. OptConnect Management, LLC reserves
 *  the right to modify or discontinue its products and to modify this and any other product documentation
 *  at any time.
 * 
 *  All OptConnect products are sold subject to its published Terms and Conditions and subject to any
 *  separate terms agreed with its customers. No warranty of any type is extended by publication of this
 *  documentation, including, but not limited to, implied warranties of merchantability, fitness for a
 *  particular purpose and non-infringement.
 * 
 *  OptConnect is a trademark, and OptConnect ema is a trademark, of OptConnect Management, LLC. All
 *  trademarks, service marks and similar designations referenced in this document are the property of
 *  their respective owners
 *  #######################################################################################################
 * 
 *  version: 1.0
 * 
 *  History:
 *  Version     Description                                     Date
 *  1.0         Initial Release                                 5/19/2020
 */ 

 #include "oc_ema_modem_at.h"

/*########## Telit LE910V2 responses/urcs/patterns ##########*/
static const char atOKPattern[]             = "\r\nOK\r\n";
static const char atERRPattern[]            = "\r\nERROR\r\n";
static const char atSSENDPattern[]          = "> ";
static const char atHTTPSNDPattern[]        = ">>>";
static const char atHTTPRING[]              = "\r\n#HTTPRING: ";
static const char atSRING[]                 = "\r\nSRING: ";
static const char atSRECV[]                 = "\r\n#SRECV: ";
/*###########################################*/

/*########## Telit LE910V2 AT cmds/definitions ##########*/
static const char atCmdAT[]                 = "at\r"; 
static const char atCmdATE0[]               = "ate0\r";
static const char atCmdCMEE[]               = "at+cmee=";
static const char atCmdCESQ[]               = "at+cesq\r";
static const char atCmdCREG[]               = "at+creg?\r";
static const char atCmdCEREG[]              = "at+cereg?\r";
static const char atCmdMONI[]               = "at#moni\r";
static const char atCmdCOPS[]               = "at+cops?\r";
static const char atCmdSCFG[]               = "at#scfg=";
static const char atCmdSD[]                 = "at#sd=";
static const char atCmdSH[]                 = "at#sh=";
static const char atCmdSSEND[]              = "at#ssend=";
static const char atCmdSRECV[]              = "at#srecv=";
static const char atCmdSS[]                 = "at#ss\r";
static const char atCmdSGACT[]              = "at#sgact=";
static const char atCmdCGPADDR[]            = "at+cgpaddr=";
static const char atCmdHTTPCFG[]            = "at#httpcfg=";
static const char atCmdHTTPSND[]            = "at#httpsnd=";
static const char atCmdHTTPRCV[]            = "at#httprcv=";
/*###########################################*/

/*############ Helper Functions ############*/
/* 
emaModemAt_build_cmd: 
Copies a cmd(*cmd) to the modem at tx buffer(context->at_cmd). 
*/
static void emaModemAt_BuildCmd( struct modem_at_context *context, const char *cmd )
{
    sprintf(context->at_cmd, "%s", cmd);
}

/* 
emaModemAT_CheckOK: 
Checks an AT cmd response(*buff) for the OK pattern. The length of the data(*len) excluding the OK pattern is updated. 
*/
int emaModemAT_CheckOK( char *buff, unsigned short *len )
{
    int st = MOD_AT_ST_SUCCESS;
    char *pLen;

    // check the input buff for the "\r\nOK\r\n" pattern
    pLen = strstr(buff, atOKPattern); 
    if ( pLen == 0 ){
        st = MOD_AT_ST_NO_OK;
    }else{
        *len = pLen - buff;
    }

    return st;
}

/* 
emaModemAT_CheckError: 
Checks an AT cmd response(*buff) for the ERROR pattern.  
*/
int emaModemAT_CheckError( char *buff )
{
    int st = MOD_AT_ST_UNKNOWN;
    char *p;

    // check the input buff for the "\r\nERROR\r\n" pattern
    p = strstr(buff, atERRPattern); 
    if ( p ){
        st = MOD_AT_ST_ERROR;
    }

    return st;
}

/* 
emaModemAT_CheckSSEND: 
Checks an AT cmd response(*buff) for the SEND pattern.  
*/
int emaModemAT_CheckSSEND( char *buff )
{
    int st = MOD_AT_ST_UNKNOWN;
    char *pLen;

    // check the input buff for the pattern
    pLen = strstr(buff, atSSENDPattern); 
    if ( pLen ){
        st = MOD_AT_ST_SUCCESS;
    }

    return st;
}

/* 
emaModemAT_CheckSRECV: 
Checks an AT cmd response(*buff) for the RECV pattern.  
*/
int emaModemAT_CheckSRECV( char *buff )
{
    int st = MOD_AT_ST_UNKNOWN;
    char *pLen;

    // check the input buff for the pattern
    pLen = strstr(buff, atSRECV); 
    if ( pLen ){
        st = MOD_AT_ST_SUCCESS;
    }

    return st;
}

/* 
emaModemAT_CheckHTTPSND: 
Checks an AT cmd response(*buff) for the HTTP SEND pattern.  
*/
int emaModemAT_CheckHTTPSND( char *buff )
{
    int st = MOD_AT_ST_UNKNOWN;
    char *pLen;

    // check the input buff for the pattern
    pLen = strstr(buff, atHTTPSNDPattern); 
    if ( pLen ){
        st = MOD_AT_ST_SUCCESS;
    }

    return st;
}
/*###########################################*/

/*############ Modem AT cmd APIs ############*/
/* 
emaModemAT_init: 
This API is provided for the users application to pass in a function pointer(*at_query_ptr) for a specific AT cmd 
send/recieve function. Subsequent APIs in this file will use this function pointer for AT transactions. 
*/
void emaModemAT_Init( struct modem_at_context *at_handle, 
                      int (*at_query_ptr)(struct modem_at_context *, unsigned short tx_timeout, unsigned short res_timeout, const char *res_pattern )) 
{
    // set the user provided function for an at query
    at_comm_handle = at_query_ptr;

    // reset vars
    at_handle->at_cmd_flags = 0;
    at_handle->response_flags = 0;
    at_handle->status = 0;
}

/* 
emaModemAT_check_comm: 
Sends a simple AT cmd(AT) to the module for communication validation. The response is checked accordingly. 
*/
int emaModemAT_CheckComm( struct modem_at_context *context )
{
    int st;
    unsigned short len;

    emaModemAt_BuildCmd( context, atCmdAT );

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, MODEM_AT_RES_WAITTIME, atOKPattern );
    if ( st > 0 ) {
        // check the response
        st = emaModemAT_CheckOK( context->response, &len );
    }

    return st;
}

/* 
emaModemAT_EnableErrorReporting: 
Enables error reporting by the input verbosity level(level(0-2)) that is passed in. 
*/
int emaModemAT_EnableErrorReporting( struct modem_at_context *context, unsigned char level )
{
    int st, l;
    unsigned short len;

    // build the cmd
    l = sprintf( context->at_cmd, "%s", atCmdCMEE );
    l += sprintf( context->at_cmd+l, "%d", level );
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, MODEM_AT_RES_WAITTIME, atOKPattern );
    if ( st > 0 ) {
        // check the response
        st = emaModemAT_CheckOK( context->response, &len );
    }

    return st;
}

/* 
emaLinkAT_DisableEcho: 
Disables the echo feature provided by the module 
*/
int emaModemAT_DisableEcho( struct modem_at_context *context )
{
    int st;
    unsigned short len;

    emaModemAt_BuildCmd( context, atCmdATE0 );

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, MODEM_AT_RES_WAITTIME, atOKPattern );
    if ( st > 0 ) {
        // check the response
        st = emaModemAT_CheckOK( context->response, &len );
    }

    return st;
}

/* 
emaModemAT_GetSignalQuality: 
Gets the extended signal quality information from the module. 
*/
int emaModemAT_GetSignalQuality( struct modem_at_context *context, 
                                 unsigned char *rxlev, 
                                 unsigned char *ber,
                                 unsigned char *rscp,
                                 unsigned char *ecno,
                                 unsigned char *rsrq,
                                 unsigned char *rsrp)
{
    int st = MOD_AT_ST_SUCCESS;
    unsigned short len;
    char *p;

    emaModemAt_BuildCmd(context, atCmdCESQ);

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, MODEM_AT_RES_WAITTIME, atOKPattern );
    if ( st > 0 ) {
        // check the response
        st = emaModemAT_CheckOK( context->response, &len );
        if ( st == MOD_AT_ST_SUCCESS ) {
            // strip out the params
            p = strchr( context->response, ' ' );
            *rxlev = atoi( p+1 );
            p = strchr( p, ',' );
            *ber = atoi( p+1 );
            p = strchr( p+1, ',' );
            *rscp = atoi( p+1 );
            p = strchr( p+1, ',' );
            *ecno = atoi( p+1 );
            p = strchr( p+1, ',' );
            *rsrq = atoi( p+1 );
            p = strchr( p+1, ',' );
            *rsrp = atoi( p+1 );
        }
    }

    return st;
}

/* 
emaModemAT_GetNetworkStatus: 
Gets the network status in the form of registration status(*reg), access technology(*acc_tech), and operator(*op). 
This API sends multiple AT cmds to the module 
*/
int emaModemAT_GetNetworkStatus( struct modem_at_context *context, unsigned char *reg, char *acc_tech, char *op )
{
    int st = MOD_AT_ST_SUCCESS;
    unsigned short len;
    char *p, *p2, *p3;

    // check 4g/lte reg 1st
    emaModemAt_BuildCmd(context, atCmdCEREG);

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, MODEM_AT_RES_WAITTIME, atOKPattern );
    if ( st > 0 ) {
        // check the response
        st = emaModemAT_CheckOK( context->response, &len );
        if ( st == MOD_AT_ST_SUCCESS ) {
            // strip out the params
            p = strchr( context->response, ',' );
            *reg = atoi( p+1 );
        }
    }

    if ( *reg == 0 ) {
        // not reg with 4g/lte, check 3g
        emaModemAt_BuildCmd(context, atCmdCREG);

        // call the user provided hook for at comms
        st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, MODEM_AT_RES_WAITTIME, atOKPattern );
        if ( st > 0 ) {
            // check the response
            st = emaModemAT_CheckOK( context->response, &len );
            if ( st == MOD_AT_ST_SUCCESS ) {
                // strip out the params
                p = strchr( context->response, ',' );
                *reg = atoi( p+1 );
            }
        }
    }

    // get op status
    emaModemAt_BuildCmd(context, atCmdCOPS);

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, MODEM_AT_RES_WAITTIME, atOKPattern );
    if ( st > 0 ) {
        // check the response
        st = emaModemAT_CheckOK( context->response, &len );
        if ( st == MOD_AT_ST_SUCCESS ) {
            // strip out the params
            p = strchr( context->response, '"' );
            if ( p ) {
                p2 = strchr( p+1, '"');
                p3 = strchr( p2+1, ',' );
                *acc_tech = atoi( p3+1 );
            }
        }
    }

    // get op status
    emaModemAt_BuildCmd(context, atCmdMONI);

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, MODEM_AT_RES_WAITTIME, atOKPattern );
    if ( st > 0 ) {
        // check the response
        st = emaModemAT_CheckOK( context->response, &len );
        if ( st == MOD_AT_ST_SUCCESS ) {
            // strip out the params
            p = strchr( context->response, ' ' );
            if ( p ) {
                // null terminate after operator for string ops
                p2 = strchr( p+1, ' ' );
                *p2 = 0;
                strcpy( op, p+1 );
            }
        }
    }

    return st;
}

/* 
emaModemAT_ConfigSocket: 
Configures a socket to be used with a socket dial. 
*/
int emaModemAT_ConfigSocket( struct modem_at_context *context, unsigned char connID,                  
                                                               unsigned char cntxID,
                                                               unsigned short TcpPktSz,
                                                               unsigned short inActTimeout,
                                                               unsigned short connTimeout,
                                                               unsigned short txDataTimeout )
{
    int st, l;
    unsigned short len;

    // build the cmd
    l = sprintf( context->at_cmd, "%s", atCmdSCFG );
    l += sprintf( context->at_cmd+l, "%d,%d,%d,%d,%d,%d", connID, cntxID, TcpPktSz, inActTimeout, connTimeout, txDataTimeout );
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, 5000, atOKPattern );
    if ( st > 0 ) {
        // check the response
        st = emaModemAT_CheckOK( context->response, &len );
    }

    return st;
}

/* 
emaModemAT_OpenSocket: 
Starts a socket dial by openeing the socket using a pre-configured connection ID(connID).  
*/
int emaModemAT_OpenSocket( struct modem_at_context *context, unsigned char connID, 
                                                             unsigned char txProt,
                                                             unsigned short remotePort,
                                                             char *IPorHostname,
                                                             unsigned char closeType,
                                                             unsigned short localPort,
                                                             unsigned char connMode )
{
    int st, l;
    unsigned short len;

    // build the cmd
    l = sprintf( context->at_cmd, "%s", atCmdSD );
    l += sprintf( context->at_cmd+l, "%d,%d,%d,\"%s\",%d,%d,%d", connID, txProt, remotePort, IPorHostname, closeType, localPort, connMode );
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, 5000, atOKPattern );
    if ( st > 0 ) {
        // check the response
        if ( connMode == CONN_MODE_COMMAND ) {
            st = emaModemAT_CheckOK(context->response, &len);
        }else{

        }
    }

    return st;
}

/* 
emaModemAT_SocketStatus: 
Gets the status of all sockets exposed by the module's internal IP stack. The status is stored in the 
response buffer(context->response) 
*/
int emaModemAT_SocketStatus( struct modem_at_context *context )
{
    int st;
    unsigned short len;

    emaModemAt_BuildCmd( context, atCmdSS );

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, MODEM_AT_RES_WAITTIME, atOKPattern );
    if ( st > 0 ) {
        // check the response
        st = emaModemAT_CheckOK( context->response, &len );
    }

    return st;
}

/* 
emaModemAT_CloseSocket: 
Closes a previously opened socket used by a socket dial and identified by the connection ID(connID) 
*/
int emaModemAT_CloseSocket( struct modem_at_context *context, unsigned char connID )
{
    int st, l;
    unsigned short len;

    // build the cmd
    l = sprintf( context->at_cmd, "%s", atCmdSH );
    l += sprintf( context->at_cmd+l, "%d", connID );
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, 1000, atOKPattern );
    if ( st > 0 ) {
        // check the response
        st = emaModemAT_CheckOK(context->response, &len);
    }

    return st;
}

/* 
emaModemAT_SocketSend: 
Sends data(*data) using a socket dial on a previously opened socket identified by connection ID(connID) while the module is in command mode. 
*/
int emaModemAT_SocketSend( struct modem_at_context *context, unsigned char connID, char *data )
{
    int st, l;
    unsigned short len;

    // build the cmd
    l = sprintf( context->at_cmd, "%s", atCmdSSEND );
    l += sprintf( context->at_cmd+l, "%d", connID );
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    // this cmd does not have an ok/error response
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, 5000, atSSENDPattern );
    if ( st > 0 ) {
        // look for the data input marker
        st = emaModemAT_CheckSSEND( context->response );
        if ( st == MOD_AT_ST_SUCCESS ) {
            // build the data msg
            l = sprintf( context->at_cmd, "%s", data );
            // add the ctrlz/0x1a to send the data
            *(context->at_cmd+l) = SEND_DATA_CONFIRM;

            // let the tx/rx cmd know this is a special cmd for length calcualtions
            context->at_cmd_flags |= MODULE_FL_SPECIAL_CMD;
            st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, 5000, atSRING );
            if ( st > 0 ) {
                st = emaModemAT_CheckOK(context->response, &len);
            }
        }
    }

    return st;
}

/* 
emaModemAT_SocketRecv: 
Receives data(*recv_data) using a socket dial on a previously opened socket identified by connection ID(connID) while the module is in command mode. 
The length in bytes(*recv_len) is also updated. 
*/
int emaModemAT_SocketRecv( struct modem_at_context *context, unsigned char connID, char *recv_data, unsigned short *recv_len )
{
    int st, l;
    unsigned short len;
    char *p, *p2;
    unsigned char rxConnID;

    // build the cmd
    l = sprintf( context->at_cmd, "%s", atCmdSRECV );
    l += sprintf( context->at_cmd+l, "%d,%d", connID, 1500 );
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, 5000, atOKPattern );
    if ( st > 0 ) {
        // check ok 
        st = emaModemAT_CheckOK(context->response, &len);
        if ( st == MOD_AT_ST_SUCCESS ) {
            // check data recv token
            st = emaModemAT_CheckSRECV( context->response );
            if ( st == MOD_AT_ST_SUCCESS ) {
                // confirm socket id
                p = strchr( context->response , ' ' );
                rxConnID = atoi(p+1);
                if ( rxConnID == connID  ) {
                    // data is for this socket, get it 
                    p = strchr( context->response , ',' );
                    *recv_len = atoi( p+1 );
                    // point to the data
                    p2 = strchr( p, '\n' );
                    // null terminate for string ops, and update the output buff
                    *(p2+1+(*recv_len)+1) = 0;
                    strcpy( recv_data, p2+1 );
                }
            }            
        }
    }

    return st;
}

/* 
emaModemAT_HTTPQueryRaw: 
Executes an http query using raw http data/headers(*http_data) using socket dials in command mode. The pdp context is activated by context ID(cntxID), 
the socket is configured and identified by the connection ID(connID), the raw http response(*http_response) and length(*resp_len) are returned. 
The socket identifed by connection ID(connID) is closed and the pdp context(cntxID) is deactivated before returning. 
*/
int emaModemAT_HTTPQueryRaw( struct modem_at_context *context, unsigned char connID, 
                                                               unsigned char cntxID, 
                                                               char *IPorHostname, 
                                                               char *http_data, 
                                                               char *http_repsonse,
                                                               unsigned short *resp_len )
{
    int st;
    char *p, *p1;
    unsigned char rxConnID;

    // config the socket
    st = emaModemAT_ConfigSocket( context, connID, cntxID, TCP_PCKT_SZ_DEFAULT, INACT_TIMEOUT_DEFAULT, CONN_TIMEOUT_DEFAULT, TX_DATA_TIMEOUT_DEFAULT );
    if ( st == MOD_AT_ST_SUCCESS ) {
        // activate the pdp context
        st = emaModemAT_ActivatePDPCntx( context, cntxID );
        if ( st == MOD_AT_ST_SUCCESS ) {
            // open the socket and connect to the host
            st = emaModemAT_OpenSocket( context, connID, TX_PROTOCOL_TCP, 80, IPorHostname, CLOSE_TYPE_IMMEDIATELY, 0, CONN_MODE_COMMAND );
            if ( st == MOD_AT_ST_SUCCESS ) {
                // send the data
                st = emaModemAT_SocketSend( context, connID, http_data );
                if ( st == MOD_AT_ST_SUCCESS ) {
                    if ( st > 0 ) {
                        // check for the data recevied URC
                        p = strstr(context->response, atSRING);
                        if ( p ) {
                            // we have data, confirm for this socket
                            p1 = strchr( p, ' ' );
                            rxConnID = atoi(p1+1);
                            if ( rxConnID == connID  ) {
                                // data is for this socket, get it 
                                st = emaModemAT_SocketRecv(context, connID, http_repsonse, resp_len );
                                if ( st == MOD_AT_ST_SUCCESS ) {
                                    // get the http result code and return it
                                    p = strstr( http_repsonse, "HTTP" );
                                    if ( p ) {
                                        p1 = strchr( p, ' ');
                                        st = atoi( p1+1 );
                                    }
                                }
                            }else{
                                st = MOD_AT_ST_UNKNOWN; 
                            }
                        }else{
                            st = MOD_AT_ST_UNKNOWN; 
                        }
                    }
                }
            }
        }
    }

    // close the socket and deactivate the cntx before exiting
    emaModemAT_CloseSocket( context, connID );
    emaModemAT_DeactivatePDPCntx( context, cntxID );  

	return st;
}

/* 
emaModemAT_ConfigHTTP: 
Configures an http/s instance using the module's easy IP HTTP stack. The http/s instance is identified by the profile ID(profID), and 
executed on the specified context ID(cntxID). Other standard htttp/s params follow(*IPorHostname, server_port, ssl, recv_timeout, pckt_size). 
The context ID(cntxID) MUST be activated before sending data on this instance. 
*/
int emaModemAT_ConfigHTTP( struct modem_at_context *context, unsigned char profID,  
                                                             char *IPorHostname,  
                                                             unsigned short server_port,
                                                             unsigned char ssl, 
                                                             unsigned short recv_timeout,
                                                             unsigned char cntxID,   
                                                             unsigned short pckt_size )
{
    int st, l;
    unsigned short len;

    // build the cmd
    l = sprintf( context->at_cmd, "%s", atCmdHTTPCFG );
    l += sprintf( context->at_cmd+l, "%d,\"%s\",%d,0,\"\",\"\",%d,%d,%d,%d", profID, IPorHostname, server_port, ssl, recv_timeout, cntxID, pckt_size );
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, 1000, atOKPattern );
    if ( st > 0 ) {
        // check the response
        st = emaModemAT_CheckOK( context->response, &len );
    }

    return st;
}

/* 
emaModemAT_HTTPPost: 
Executes an http/s POST method using the modules easy IP HTTP stack. The http/s instance is identified by the profile ID(profID). 
The pdp context used by this instance MUST be activated prior to calling this API. 
Other standard htttp/s params follow(*http_resource_uri, *content_type_id, *extra_header, *data, data_len ). The following is 
updated(*http_resp_content_type, *http_resp_data, *http_resp_len, *http_resp_code). 
*/
int emaModemAT_HTTPPost( struct modem_at_context *context, unsigned char profID, 
                                                           char *http_resource_uri,
                                                           char *content_type_id,
                                                           char *extra_header, 
                                                           char *data,
                                                           unsigned short data_len,
                                                           char *http_resp_content_type,
                                                           char *http_resp_data,
                                                           unsigned short *http_resp_len,
                                                           int *http_resp_code )
{
    int st;
    char *p, *p1;
    unsigned char rxprofID;

    st = emaModemAT_HTTPSend(context, profID, HTTP_POST, http_resource_uri, content_type_id, extra_header, data, data_len );
    if ( st == MOD_AT_ST_SUCCESS ) {
        if ( st > 0 ) {
            // check for the data recevied URC
            p = strstr(context->response, atHTTPRING);
            if ( p ) {
                // we have data, confirm for this socket
                p1 = strchr( p, ' ' );
                rxprofID = atoi(p1+1);
                if ( rxprofID == profID  ) {
                    // data is for this http profile, check the http response code
                    p = strchr( p1, ',' );
                    *http_resp_code = atoi(p+1);
                    if ( *http_resp_code == 200 ) {
                        // successful http request, get the response content type
                        p1 = strchr( p, '"' );
                        p = strchr( p1+1, '"' );
                        // null terminate for string ops
                        *(p+1) = 0;
                        strcpy( http_resp_content_type, p1 );
                        // get the response data/body
                        st = emaModemAT_HTTPRecv( context, profID, http_resp_len, http_resp_data );
                    }else{
                        // didn't get result of 200, but there might be data still
                        st = emaModemAT_HTTPRecv( context, profID, http_resp_len, http_resp_data );
                    }
                }else{
                    st = MOD_AT_ST_UNKNOWN; 
                }
            }else{
                st = MOD_AT_ST_UNKNOWN; 
            }
        }
    }

	return st;
}

/* 
emaModemAT_HTTPSend: 
Uses the modules easy IP HTTP stack to send an http query. The http/s instance is identified by the profile ID(profID). 
The pdp context used by this instance MUST be activated prior to calling this API. 
Other standard htttp/s params follow(cmd, *http_resource_uri, *content_type_id, *extra_header, *data, data_len ).  
*/
int emaModemAT_HTTPSend( struct modem_at_context *context, unsigned char profID,
                                                           unsigned char cmd,
                                                           char *http_resource_uri,
                                                           char *content_type_id,
                                                           char *extra_header,
                                                           char *data,
                                                           unsigned short data_len )
{
    int st, l;
    unsigned short len;

    // build the cmd
    l = sprintf( context->at_cmd, "%s", atCmdHTTPSND );
    l += sprintf( context->at_cmd+l, "%d,%d,\"%s\",%d", profID, cmd, http_resource_uri, data_len );
    if ( content_type_id ) {
        l += sprintf(context->at_cmd + l, ",\"%s\"", content_type_id );
    }
    if ( extra_header ) {
        l += sprintf(context->at_cmd + l, ",\"%s\"", extra_header );
    }
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    if ( data_len > 0 ) {
        st = (*at_comm_handle)(context, MODEM_AT_TX_WAITITME, 15500, atHTTPSNDPattern);
    }else{
        st = (*at_comm_handle)(context, MODEM_AT_TX_WAITITME, 15500, atHTTPRING);
    }
    if ( st > 0 ) {
        // look for the data input marker
        if ( data_len ) {
            st = emaModemAT_CheckHTTPSND(context->response);
            if ( st == MOD_AT_ST_SUCCESS ) {
                // copy the data to the output buffer, it will send after data_len is reached
                memcpy( context->at_cmd, data, data_len);

                // let the tx/rx call know the data length and send the data
                context->data_length = data_len;
                st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, 10000, atHTTPRING );
                if ( st > 0 ) {
                    st = emaModemAT_CheckOK(context->response, &len);
                }
            }
        }else{
            // check 
            st = emaModemAT_CheckOK(context->response, &len);
        }
    }

    return st;
}

/* 
emaModemAT_HTTPRecv: 
Uses the modules easy IP HTTP stack to receive data from an http query. The http/s instance is identified by the profile ID(profID). 
The pdp context used by this instance MUST be activated prior to calling this API. 
The following is updated(*http_resp_len, *http_resp_data). 
*/
int emaModemAT_HTTPRecv( struct modem_at_context *context, unsigned char profID, unsigned short *http_resp_len, char *http_resp_data )
{
    int st, l;
    unsigned short len;

    // build the cmd
    l = sprintf( context->at_cmd, "%s", atCmdHTTPRCV );
    l += sprintf( context->at_cmd+l, "%d,%d", profID, HTTP_MAX_RECV_SIZE );
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, 5000, atOKPattern );
    if ( st > 0 ) {
        // look for the data input marker
        st = emaModemAT_CheckOK(context->response, &len);
        if ( st == MOD_AT_ST_SUCCESS ) {
            // null terminate for string ops
            *(context->response+len) = 0;
            strcpy( http_resp_data, context->response+5/*skip "crlf<<<"*/ );
            *http_resp_len = len - 5;
        }
    }

    return st;
}

/* 
emaModemAT_GetMobileIP: 
Gets the mobile IP address(*mobileIP) from the module, relative to the current data session on the activated pdp context(cntxID). 
*/
int emaModemAT_GetMobileIP( struct modem_at_context *context, unsigned char cntxID, char *mobileIP )
{                                                                                       
    int st, l;                                                                          
    unsigned short len; 
    char *p1, *p2;                                                                

    // build the cmd
    l = sprintf( context->at_cmd, "%s", atCmdCGPADDR );
    l += sprintf( context->at_cmd+l, "%d", cntxID );
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, MODEM_AT_RES_WAITTIME, atOKPattern );
    if ( st > 0 ) {
        // check the response
        st = emaModemAT_CheckOK( context->response, &len );
        if ( st == MOD_AT_ST_SUCCESS ) {
            // get the ip addr
            p1 = strchr( context->response, '"' );
            p2 = strchr( p1+1, '"' );
            // null terminate for string ops
            *p2 = 0;
            strcpy( mobileIP, p1+1 );
        }
    }

    return st;
}

/* 
emaModemAT_StatusPDPCntx: 
Gets the pdp context(cntxID) activation status(*status) from the module. 
*/
int emaModemAT_StatusPDPCntx( struct modem_at_context *context, unsigned char cntxID, unsigned char *status )
{                                                                                       
    int st, l;                                                                          
    unsigned short len;   
    char *p;                                                              

    // build the cmd
    l = sprintf( context->at_cmd, "%s", atCmdSGACT );
    l += sprintf( context->at_cmd+l-1, "?" );
    sprintf( context->at_cmd+l-1, "\r" );

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, MODEM_AT_RES_WAITTIME, atOKPattern );
    if ( st > 0 ) {
        // check the response
        st = emaModemAT_CheckOK( context->response, &len );
        if ( st == MOD_AT_ST_SUCCESS ) {
            // get the status for the passed in context id
            cntxID += 0x30;
            p = strchr( context->response, cntxID);
            *status = atoi( p+2 );
        }
    }

    return st;
}

/* 
emaModemAT_ActivatePDPCntx: 
Activates a pdp context(cntxID) using the modules easy IP stack for a data session. 
*/
int emaModemAT_ActivatePDPCntx( struct modem_at_context *context, unsigned char cntxID )
{                                                                                       
    int st, l;                                                                          
    unsigned short len;                                                                 

    // build the cmd
    l = sprintf( context->at_cmd, "%s", atCmdSGACT );
    l += sprintf( context->at_cmd+l, "%d,1", cntxID );
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, 10000, atOKPattern );
    if ( st > 0 ) {
        // check the response
        st = emaModemAT_CheckOK( context->response, &len );
    }

    return st;
}

/* 
emaModemAT_ActivatePDPCntx: 
Deactivates a pdp context(cntxID) that has previously been activated. 
*/
int emaModemAT_DeactivatePDPCntx( struct modem_at_context *context, unsigned char cntxID )
{
    int st, l;
    unsigned short len;

    // build the cmd
    l = sprintf( context->at_cmd, "%s", atCmdSGACT );
    l += sprintf( context->at_cmd+l, "%d,0", cntxID );
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    st = (*at_comm_handle)( context, MODEM_AT_TX_WAITITME, 5000, atOKPattern );
    if ( st > 0 ) {
        // check the response
        st = emaModemAT_CheckOK( context->response, &len );
    }

    return st;
}

/*###########################################*/

