#ifndef BLINKER_HTTP_SIM7000_H
#define BLINKER_HTTP_SIM7000_H

#if ARDUINO >= 100
    #include <Arduino.h>
#else
    #include <WProgram.h>
#endif

// #include "Adapters/BlinkerSerialMQTT.h"
#include "../Blinker/BlinkerATMaster.h"
#include "../Blinker/BlinkerConfig.h"
#include "../Blinker/BlinkerDebug.h"
#include "../Blinker/BlinkerStream.h"
#include "../Blinker/BlinkerUtility.h"

// #if defined(ESP32)
//     #include <HardwareSerial.h>

//     HardwareSerial *HSerial_HTTP;
// #else
//     #include <SoftwareSerial.h>

//     SoftwareSerial *SSerial_HTTP;
// #endif

#define BLINKER_HTTP_SIM7000_DEFAULT_TIMEOUT 5000UL
#define BLINKER_HTTP_SIM7000_DATA_BUFFER_SIZE 1024

enum sim7000_http_status_t
{
    sim7000_http_url_set,
    sim7000_http_url_success,
    sim7000_http_body_set,
    sim7000_http_body_success,
    sim7000_http_header_set,
    sim7000_http_header_success,
    sim7000_http_con_set,
    sim7000_http_con_success,
    sim7000_http_state_req,
    sim7000_http_state_success,
    sim7000_http_chead_set,
    sim7000_http_chead_success,
    sim7000_http_ahead_set,
    sim7000_http_ahead_success,
    // sim7000_http_body_set,
    // sim7000_http_body_success,
    sim7000_http_send_set,
    sim7000_http_send_success,
    sim7000_http_get_set,
    sim7000_http_get_success,
    sim7000_http_read_set,
    sim7000_http_read_success,
    sim7000_http_disc_set,
    sim7000_http_disc_success,
};

class BlinkerHTTPSIM7000
{
    public :
        BlinkerHTTPSIM7000(Stream& s, bool isHardware, blinker_callback_t func)
        {
            stream = &s; isHWS = isHardware; listenFunc = func; 
            // streamData = (char*)malloc(BLINKER_HTTP_SIM7000_DATA_BUFFER_SIZE*sizeof(char));
        }

        ~BlinkerHTTPSIM7000() { flush(); }

        void streamPrint(const String & s)
        {
            // stream->flush();
            BLINKER_LOG_ALL(BLINKER_F("HTTP streamPrint: "), s);
            stream->println(s);
        }

        void _streamPrint(const String & s)
        {
            // stream->flush();
            BLINKER_LOG_ALL(BLINKER_F("HTTP streamPrint: "), s);
            stream->print(s);
        }

        void _streamPrint()
        {
            // stream->flush();
            BLINKER_LOG_ALL(BLINKER_F("HTTP streamPrint: "));
            stream->println();
        }

        bool begin(String host, String uri) { _host = host; _uri = uri; }
        void setTimeout(uint16_t timeout)   { _httpTimeout = timeout; }

        void reboot()
        {
            // streamPrint(BLINKER_CMD_CRESET_RESQ);
            // ::delay(500);
            // streamPrint(BLINKER_CMD_AT);
            // streamPrint("ATE0");
        }

        bool GET()
        {
            streamPrint(STRING_format(BLINKER_CMD_SHCONF_REQ) + \
                        "=\"URL\",\"" + _host + "\"");
            uint8_t  h_id = 0;
            uint32_t http_time = millis();
            sim7000_http_status_t http_status = sim7000_http_url_set;

            while(millis() - http_time < _httpTimeout * 4)
            {
                if (available())
                {
                    BLINKER_LOG_ALL(BLINKER_F("sim7000_http_url_set check"));
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        BLINKER_LOG_ALL(BLINKER_F("sim7000_http_url_success"));
                        http_status = sim7000_http_url_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_url_success)
            {
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHCONF_REQ) +
                        "=\"BODYLEN\",350");
            http_time = millis();
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_body_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_body_success)
            {
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHCONF_REQ) + \
                        "=\"HEADERLEN\",350");
            http_time = millis();
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_header_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_header_success)
            {
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHCONN_REQ));
            http_time = millis();
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_con_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_con_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHSTATE_REQ) + 
                        "?");
            http_time = millis();

            http_status = sim7000_http_state_req;

            while(millis() - http_time < _httpTimeout * 4)
            {
                if (available())
                {
                    _masterAT = new BlinkerMasterAT();
                    _masterAT->update(STRING_format(streamData));

                    if (_masterAT->getState() != AT_M_NONE &&
                        _masterAT->reqName() == BLINKER_CMD_SHSTATE)
                    {
                        uint8_t get_state = _masterAT->getParam(0).toInt();
                        BLINKER_LOG_ALL(BLINKER_F("sim7000_http_state_req, get_state: "), get_state);
                        http_status = sim7000_http_state_success;
                        free(_masterAT);
                        break;
                    }

                    free(_masterAT);
                }
            }

            // if (http_status != sim7000_http_state_success)
            // {
            //     streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
            //     return false;
            // }

            streamPrint(STRING_format(BLINKER_CMD_SHCHEAD_REQ));
            http_time = millis();
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_chead_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_chead_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHAHEAD_REQ) + \
                        "=\"User-Agent\",\"curl/7.47.0\"");
            http_time = millis();

            http_status = sim7000_http_ahead_set;
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_ahead_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_ahead_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHAHEAD_REQ) + \
                        "=\"Cache-control\",\"no-cache\"");
            http_time = millis();

            http_status = sim7000_http_ahead_set;
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_ahead_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_ahead_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHAHEAD_REQ) + \
                        "=\"Connection\",\"keep-alive\"");
            http_time = millis();

            http_status = sim7000_http_ahead_set;
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_ahead_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_ahead_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHAHEAD_REQ) + \
                        "=\"Accept\",\"*/*\"");
            http_time = millis();

            http_status = sim7000_http_ahead_set;
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_ahead_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_ahead_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHREQ_REQ) + \
                        "=\"" + _uri + "\",1");
            http_time = millis();

            http_status = sim7000_http_send_set;
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_send_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_send_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            String get_string;
            uint16_t get_code;
            uint16_t get_len;

            while(millis() - http_time < _httpTimeout * 4)
            {
                if (available())
                {
                    _masterAT = new BlinkerMasterAT();
                    _masterAT->update(STRING_format(streamData));

                    if (_masterAT->getState() != AT_M_NONE &&
                        _masterAT->reqName() == BLINKER_CMD_SHREQ)
                    {
                        get_string = _masterAT->getParam(0);
                        get_code = _masterAT->getParam(1).toInt();
                        get_len = _masterAT->getParam(2).toInt();
                        BLINKER_LOG_ALL(BLINKER_F("get_string: "), get_string, \
                                        BLINKER_F(", get_code: "), get_code, \
                                        BLINKER_F(", get_len: "), get_len);
                        http_status = sim7000_http_get_success;
                        free(_masterAT);
                        break;
                    }

                    free(_masterAT);
                }
            }

            if (http_status != sim7000_http_get_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHREAD_REQ) + \
                        "=0," + get_len + "");
            http_time = millis();

            while(millis() - http_time < _httpTimeout * 4)
            {
                if (available())
                {
                    _masterAT = new BlinkerMasterAT();
                    _masterAT->update(STRING_format(streamData));

                    if (_masterAT->getState() != AT_M_NONE &&
                        _masterAT->reqName() == BLINKER_CMD_SHREAD)
                    {
                        String read_string = _masterAT->getParam(0);
                        BLINKER_LOG_ALL(BLINKER_F("read_string: "), read_string);
                        http_status = sim7000_http_read_success;
                        free(_masterAT);
                        break;
                    }

                    free(_masterAT);
                }
            }

            if (http_status != sim7000_http_read_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            http_time = millis();

            while(millis() - http_time < _httpTimeout * 4)
            {
                if (available())
                {
                    String http_data = STRING_format(streamData);
                    BLINKER_LOG_ALL(BLINKER_F("http_data: "), http_data);
                    
                    if (isFreshPayload) free(payload);
                    payload = (char*)malloc((http_data.length() + 1)*sizeof(char));
                    memset(payload, '\0', (http_data.length() + 1));
                    strcpy(payload, http_data.c_str());
                    isFreshPayload = true;
                    break;
                }
            }

            streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
            http_time = millis();
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_disc_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_disc_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }
            return true;
        }

        bool POST(String _msg, String _type, String _application)
        {
            streamPrint(STRING_format(BLINKER_CMD_SHCONF_REQ) + \
                        "=\"URL\",\"" + _host + "\"");
            uint8_t  h_id = 0;
            uint32_t http_time = millis();
            sim7000_http_status_t http_status = sim7000_http_url_set;

            while(millis() - http_time < _httpTimeout * 4)
            {
                if (available())
                {
                    BLINKER_LOG_ALL(BLINKER_F("sim7000_http_url_set check"));
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        BLINKER_LOG_ALL(BLINKER_F("sim7000_http_url_success"));
                        http_status = sim7000_http_url_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_url_success)
            {
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHCONF_REQ) +
                        "=\"BODYLEN\",350");
            http_time = millis();
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_body_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_body_success)
            {
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHCONF_REQ) + \
                        "=\"HEADERLEN\",350");
            http_time = millis();
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_header_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_header_success)
            {
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHCONN_REQ));
            http_time = millis();
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_con_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_con_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHSTATE_REQ) + 
                        "?");
            http_time = millis();

            http_status = sim7000_http_state_req;

            while(millis() - http_time < _httpTimeout * 4)
            {
                if (available())
                {
                    _masterAT = new BlinkerMasterAT();
                    _masterAT->update(STRING_format(streamData));

                    if (_masterAT->getState() != AT_M_NONE &&
                        _masterAT->reqName() == BLINKER_CMD_SHSTATE)
                    {
                        uint8_t get_state = _masterAT->getParam(0).toInt();
                        BLINKER_LOG_ALL(BLINKER_F("sim7000_http_state_req, get_state: "), get_state);
                        http_status = sim7000_http_state_success;
                        free(_masterAT);
                        break;
                    }

                    free(_masterAT);
                }
            }

            // if (http_status != sim7000_http_state_success)
            // {
            //     streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
            //     return false;
            // }

            streamPrint(STRING_format(BLINKER_CMD_SHCHEAD_REQ));
            http_time = millis();
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_chead_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_chead_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHAHEAD_REQ) + \
                        "=\"Content-Type\",\"application/json\"");
            http_time = millis();

            http_status = sim7000_http_ahead_set;
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_ahead_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_ahead_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHAHEAD_REQ) + \
                        "=\"Cache-control\",\"no-cache\"");
            http_time = millis();

            http_status = sim7000_http_ahead_set;
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_ahead_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_ahead_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHAHEAD_REQ) + \
                        "=\"Connection\",\"keep-alive\"");
            http_time = millis();

            http_status = sim7000_http_ahead_set;
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_ahead_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_ahead_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHAHEAD_REQ) + \
                        "=\"Accept\",\"*/*\"");
            http_time = millis();

            http_status = sim7000_http_ahead_set;
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_ahead_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_ahead_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            uint16_t _msg_len = _msg.length();
            _msg.replace("\"", "\\\"");
            streamPrint(STRING_format(BLINKER_CMD_SHBOD_REQ) + \
                        "=\"" + _msg + "\"," + STRING_format(_msg_len));
            http_time = millis();

            http_status = sim7000_http_body_set;
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_body_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_body_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }
            
            streamPrint(STRING_format(BLINKER_CMD_SHREQ_REQ) + \
                        "=\"" + _uri + "\",3");
            http_time = millis();

            http_status = sim7000_http_send_set;
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_send_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_send_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            String get_string;
            uint16_t get_code;
            uint16_t get_len;

            while(millis() - http_time < _httpTimeout * 4)
            {
                if (available())
                {
                    _masterAT = new BlinkerMasterAT();
                    _masterAT->update(STRING_format(streamData));

                    if (_masterAT->getState() != AT_M_NONE &&
                        _masterAT->reqName() == BLINKER_CMD_SHREQ)
                    {
                        get_string = _masterAT->getParam(0);
                        get_code = _masterAT->getParam(1).toInt();
                        get_len = _masterAT->getParam(2).toInt();
                        BLINKER_LOG_ALL(BLINKER_F("get_string: "), get_string, \
                                        BLINKER_F(", get_code: "), get_code, \
                                        BLINKER_F(", get_len: "), get_len);
                        if (get_code == 200)
                        {
                            http_status = sim7000_http_get_success;
                        }
                        free(_masterAT);
                        break;
                    }

                    free(_masterAT);
                }
            }

            if (http_status != sim7000_http_get_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHREAD_REQ) + \
                        "=0," + get_len + "");
            http_time = millis();

            while(millis() - http_time < _httpTimeout * 4)
            {
                if (available())
                {
                    // _masterAT = new BlinkerMasterAT();
                    // _masterAT->update(STRING_format(streamData));

                    // if (_masterAT->getState() != AT_M_NONE &&
                    //     _masterAT->reqName() == BLINKER_CMD_SHREAD)
                    // {
                    //     String read_string = _masterAT->getParam(0);
                    //     BLINKER_LOG_ALL(BLINKER_F("read_string: "), read_string);
                    //     http_status = sim7000_http_read_success;
                    //     free(_masterAT);
                    //     break;
                    // }

                    // free(_masterAT);

                    BLINKER_LOG_ALL(BLINKER_F("read_string: "), streamData);
                }
            }

            if (http_status != sim7000_http_get_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }

            streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
            http_time = millis();
            
            while(millis() - http_time < _httpTimeout * 2)
            {
                if (available())
                {
                    if (strncmp(streamData, BLINKER_CMD_OK, 2) == 0)
                    {
                        http_status = sim7000_http_disc_success;
                        break;
                    }
                }
            }

            if (http_status != sim7000_http_disc_success)
            {
                streamPrint(STRING_format(BLINKER_CMD_SHDISC_REQ));
                return false;
            }
            return true;
        }

        String getString()
        {
            if (isFreshPayload) return payload;//TBD
            return "";
        }

        void flush()
        {
            if (isFreshPayload) free(payload); 
            if (isFresh) free(streamData);

            BLINKER_LOG_ALL(BLINKER_F("flush sim7000 http"));
        }

    protected :
        class BlinkerMasterAT * _masterAT;
        Stream* stream;
        // String  streamData;
        // char    streamData[1024];
        char*   streamData;
        // char    streamBuffer[2048] = { '\0' };
        char*   payload;
        bool    isFreshPayload = false;
        bool    isFresh = false;
        bool    isHWS = false;
        String  _host;
        String  _uri;

        blinker_callback_t listenFunc = NULL;

        uint16_t _httpTimeout = BLINKER_HTTP_SIM7000_DEFAULT_TIMEOUT;

        char c_hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

        int8_t dcode_data(char d_test)
        {
            for (uint8_t num = 0; num < 16; num++)
            {
                if (d_test == c_hex[num]) return num;
            }

            return -1;
        }

        char ecode_data(uint8_t d_test)
        {
            return c_hex[d_test];
        }

        String encode(String data)
        {
            // char _d[2048] = {'\0'};

            String _d = "";

            BLINKER_LOG_FreeHeap_ALL();
            BLINKER_LOG_ALL("encode data: ", data);
            BLINKER_LOG_ALL("encode len: ", data.length());

            for(uint16_t num = 0; num < data.length(); num++)
            {
                _d += (ecode_data((uint8_t)data.c_str()[num] >> 4));
                _d += (ecode_data((uint8_t)data.c_str()[num] & 0x0F));
            }

            BLINKER_LOG_ALL("encode data: ", _d);

            return _d;
        }

        // String encode(char data[])
        // {
        //     // char _d[2048] = {'\0'};
        //     String _d = "";

        //     BLINKER_LOG_FreeHeap_ALL();
        //     BLINKER_LOG_ALL("encode data: ", data);
        //     BLINKER_LOG_ALL("encode len: ", strlen(data));

        //     for(uint16_t num = 0; num < strlen(data); num++)
        //     {
        //         _d += (ecode_data(data[num] >> 4));
        //         _d += (ecode_data(data[num] & 0x0F));
        //     }

        //     BLINKER_LOG_ALL("encode data: ", _d);

        //     return _d;
        // }

        // for(uint16_t num = 0; num < strlen(test); num++)
        // {
        //     Serial.print(ecode_data(test[num] >> 4));
        //     Serial.print(ecode_data(test[num] & 0x0F));
        // }
        // Serial.println();

        int timedRead()
        {
            int c;
            uint32_t _startMillis = millis();
            do {
                c = stream->read();
                if (c >= 0) return c;
            } while(millis() - _startMillis < 1000);
            return -1; 
        }

        bool available(const char *str)
        {
            uint32_t http_time = millis();
            char _data[1024];// = { '\0' };
            memset(_data, '\0', 1024);

            BLINKER_LOG_ALL(BLINKER_F("available str: "), str);
            while(millis() - http_time < _httpTimeout * 4)
            {
                if (stream->available())
                {
                    // if (isFresh) free(streamData);
                    // streamData = (char*)malloc(1*sizeof(char));
                    // isFresh = true;
                    // if (strspn(streamData, str)) return true;
                    // // BLINKER_LOG_ALL(BLINKER_F("handleSerial rs: "), stream->readStringUntil('\n'));
                    
                    strcpy(_data, stream->readStringUntil('\n').c_str());
                    BLINKER_LOG_ALL(BLINKER_F("handleSerial rs available(const char *str): "), _data);
                    if (strstr(_data, str))
                    {
                        _data[strlen(_data) - 1] = '\0';
                        if (isFresh) free(streamData);
                        streamData = (char*)malloc((strlen(_data) + 1)*sizeof(char));
                        strcpy(streamData, _data);
                        isFresh = true;
                        return true;
                    }
                    BLINKER_LOG_ALL(BLINKER_F("strstr(_data, str): "), strstr(_data, str));
                    memset(_data, '\0', 1024);
                }
                yield();
                // delay(100);
            }

            return false;
        }

        bool available()
        {
            yield();

            if (!isHWS)
            {
                // #if defined(__AVR__) || defined(ESP8266)
                //     if (!SSerial_HTTP->isListening())
                //     {
                //         SSerial_HTTP->listen();
                //         ::delay(100);
                //     }
                // #endif
                if (listenFunc) listenFunc();
            }

            // char _data[BLINKER_HTTP_SIM7000_DATA_BUFFER_SIZE];// = { '\0' };
            // memset(_data, '\0', BLINKER_HTTP_SIM7000_DATA_BUFFER_SIZE);

            // if (!isFresh) streamData = (char*)malloc(BLINKER_HTTP_SIM7000_DATA_BUFFER_SIZE*sizeof(char));

            if (stream->available())
            {
                // strcpy(_data, stream->readStringUntil('\n').c_str());
                String _data = stream->readStringUntil('\n');
                // if ( _data[_data.length() - 1] == '\n')
                //     _data[_data.length() - 1] = '\0';
                BLINKER_LOG_ALL(BLINKER_F("handleSerial rs: "), _data);
                if (isFresh) 
                {
                    free(streamData);
                    isFresh = false;
                }

                if (_data.length() <= 1) return false;
                
                streamData = (char*)malloc((_data.length() + 1)*sizeof(char));
                strcpy(streamData, _data.c_str());
                if (_data.length() > 0) streamData[_data.length()] = '\0';
                isFresh = true;
                return true;
            }
            else
            {
                return false;
            }
        }
};

#endif