/**
 * Copyright (c) 2017 - 2020, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
/** @file
 *
 * @defgroup simple_coap_client_example_main main.c
 * @{
 * @ingroup simple_coap_client_example_example
 * @brief Simple CoAP Client Example Application main file.
 *
 * @details This example demonstrates a CoAP client application that enables to control BSP_LED_0
 *          on a board with related Simple CoAP Server application via CoAP messages.
 *
 */
#include "mbedtls/base64.h"
#include "mbedtls/md.h"
#include "app_scheduler.h"
#include "app_timer.h"
#include "bsp_thread.h"
#include "nrf_log_ctrl.h"
#include "nrf_log.h"
#include "nrf_log_default_backends.h"

#include "thread_coap_utils.h"
#include "thread_utils.h"

#include <openthread/instance.h>
#include <openthread/thread.h>

#define SCHED_QUEUE_SIZE      32                                                      /**< Maximum number of events in the scheduler queue. */
#define SCHED_EVENT_DATA_SIZE APP_TIMER_SCHED_EVENT_DATA_SIZE                         /**< Maximum app_scheduler event size. */

static thread_coap_utils_light_command_t m_command = THREAD_COAP_UTILS_LIGHT_CMD_OFF; /**< This variable stores command that has been most recently used. */

char *OneNetmethod  = "sha1";
char *OneNetres     = "products/CxD2hb4NHT/devices/tttt";
char *OneNetversion  = "2018-10-31";
char *OneNetkey     = "WWtxWyeJ+wPB/08/6f3THe8rNSe5i1ubPyex+Qu7N08=";
char *OneNetEndpoint = "183.230.102.116:5683";
char *OneNetPathLogin    = "$sys/CxD2hb4NHT/tttt/log_in";
char *OneNetPathLogout    = "$sys/CxD2hb4NHT/tttt/log_out";
char *OneNetPathKeepAlive = "$sys/CxD2hb4NHT/tttt/keep_alive";
char *OneNetPathPropertyPost   = "$sys/CxD2hb4NHT/tttt/thing/property/post";
char *OneNetPathPropertyGet    = "$sys/CxD2hb4NHT/tttt/thing/property/get";
char *OneNetPathPropertyDelete = "$sys/CxD2hb4NHT/tttt/thing/property/delete";
char *OneNetPathPropertySet    = "$sys/CxD2hb4NHT/tttt/thing/property/set";
/***************************************************************************************************
 * @section TheThings.io
 **************************************************************************************************/

static unsigned char hexchars[] = "0123456789ABCDEF";
char *url_encode(char const *s, int len, int *new_length)
{
    register unsigned char c;
    unsigned char *to, *start;
    unsigned char const *from, *end;
    
    from = (unsigned char *)s;
    end  = (unsigned char *)s + len;
    start = to = (unsigned char *) nrf_malloc(1, 3*len+1);

    while (from < end) 
    {
        c = *from++;

        if (c == ' ') 
        {
            *to++ = '+';
        } 
        else if ((c < '0' && c != '-' && c != '.') ||
                 (c < 'A' && c > '9') ||
                 (c > 'Z' && c < 'a' && c != '_') ||
                 (c > 'z')) 
        {
            to[0] = '%';
            to[1] = hexchars[c >> 4];//将2进制转换成16进制表示
            to[2] = hexchars[c & 15];//将2进制转换成16进制表示
            to += 3;
        }
        else 
        {
            *to++ = c;
        }
    }
    *to = 0;
    if (new_length) 
    {
        *new_length = to - start;
    }
    return (char *) start;
}

char* OneNetLogInPayload()
{
    char *OneNetet = "1600913071";
    uint8_t hash[20]={0};

    uint8_t content[100]={0};
    sprintf(content, "%s\n%s\n%s\n%s", OneNetet, OneNetmethod, OneNetres, OneNetversion);

    uint8_t key[100] = {0};
    size_t olen = strlen(OneNetkey);
    mbedtls_base64_decode(key, olen, &olen, (const unsigned char *)OneNetkey, olen);

    mbedtls_md_context_t ctx;
    const mbedtls_md_info_t *info;
    mbedtls_md_init(&ctx);
    info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
    mbedtls_md_setup(&ctx, info, 1);
    mbedtls_md_hmac_starts(&ctx, key, strlen( (const char *)key));
    mbedtls_md_hmac_update(&ctx, content, strlen( (const char *)content));
    mbedtls_md_hmac_finish(&ctx, hash);
    mbedtls_md_free(&ctx);

    uint8_t old_sign[100];
    size_t len = sizeof(hash);
    mbedtls_base64_encode(old_sign, sizeof(old_sign), &len, hash, len);
//    printf("Old Sign:%s\n",old_sign);
//    printf("Old Res:%s\n",OneNetres);


    char *new_sign = url_encode(old_sign,strlen(old_sign),NULL);
    char *new_res = url_encode(OneNetres,strlen(OneNetres),NULL);

//    printf("New Sign:%s\n",new_sign);
//    printf("New Res:%s\n",new_res);
    char token[300]={0};
    sprintf(token,"version=%s&res=%s&et=%s&method=%s&sign=%s",
            OneNetversion, new_res, OneNetet, OneNetmethod, new_sign);
//    printf("Token:%s\n",token);

    char *payload = nrf_malloc(300);
    sprintf(payload,"{\"lt\":86400,\"st\":\"%s\"}",token);

    return payload;
}

#define TYZ
#ifdef TYZ
static uint16_t     m_counter       = 0;
static int32_t      m_temperature   = 0;
static otIp6Address m_cloud_address = {0};
static bool         m_associated    = false;
unsigned char m_ccode[8];
#endif
/***************************************************************************************************
 * @section Buttons
 **************************************************************************************************/



#ifdef TYZ
#define ONENET_COAP_IOT_CORE_SERVER_ADDRESS "64:FF9B::B7E6:6674"
#define ONENET_URI_PATH_PROPERTY    "$sys/CxD2hb4NHT/tttt/thing/property/post"
void my_otCoapResponseHandler(void *               aContext,
                                      otMessage *          aMessage,
                                      const otMessageInfo *aMessageInfo,
                                      otError              aResult); 
static void coap_client_cloud_update_2(void)
{
    char            payload_buffer[256];
    otError         error;
    otMessage     * p_request;
    otMessageInfo   message_info;
    otInstance    * p_instance = thread_ot_instance_get();
    char aValue;
    otCoapResponseHandler     aHandler;
    char  host[16];
    short port;
    if (m_associated == false)
    {
        return;
    }
    
    error = otIp6AddressFromString(ONENET_COAP_IOT_CORE_SERVER_ADDRESS, &m_cloud_address);
    ASSERT(error == OT_ERROR_NONE);

    snprintf(payload_buffer, sizeof(payload_buffer),
        "{\"id\":\"43725\",\"version\":\"1.0\",\"params\":{\"count\":{\"value\":%d},\"someString\":{\"value\":\"this is a string\"}}}",m_counter++);

    do
    {
        p_request = otCoapNewMessage(p_instance, NULL);
        if (p_request == NULL)
        {
            NRF_LOG_INFO("Failed to allocate message for CoAP Request\r\n");
            break;
        }

        otCoapMessageInit(p_request, OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST);
        otCoapMessageSetToken(p_request,m_ccode,8);

        error = otCoapMessageAppendUriPathOptions(p_request, ONENET_URI_PATH_PROPERTY);
        ASSERT(error == OT_ERROR_NONE);

        error = otCoapMessageAppendContentFormatOption(p_request, OT_COAP_OPTION_CONTENT_FORMAT_JSON);
        ASSERT(error == OT_ERROR_NONE);

        aValue=OT_COAP_OPTION_CONTENT_FORMAT_JSON;
        otCoapMessageAppendOption(p_request, OT_COAP_OPTION_ACCEPT, 1, (const void *)&aValue);

        error = otCoapMessageSetPayloadMarker(p_request);
        ASSERT(error == OT_ERROR_NONE);

        error = otMessageAppend(p_request, payload_buffer, strlen(payload_buffer));
        if (error != OT_ERROR_NONE)
        {
            break;
        }

        memset(&message_info, 0, sizeof(message_info));
        message_info.mPeerPort = OT_DEFAULT_COAP_PORT;
        message_info.mPeerAddr = m_cloud_address;

        error = otCoapSendRequest(p_instance, p_request, &message_info, my_otCoapResponseHandler, NULL);

    } while (false);

    if ((error != OT_ERROR_NONE) && (p_request != NULL))
    {
        NRF_LOG_INFO("Failed to send CoAP Request: %d\r\n", error);
        otMessageFree(p_request);
    }
}

static void coap_client_cloud_update(void)
{
    char            payload_buffer[256];
    otError         error;
    otMessage     * p_request;
    otMessageInfo   message_info;
    otInstance    * p_instance = thread_ot_instance_get(); 
    otCoapResponseHandler     aHandler;
    printf("hello\n");
    if (m_associated == false)
    {
        return;
    }
    char *pl = OneNetLogInPayload();
    error = otIp6AddressFromString(ONENET_COAP_IOT_CORE_SERVER_ADDRESS, &m_cloud_address);
    ASSERT(error == OT_ERROR_NONE);

    // If IPv6 address of the cloud is unspecified try to resolve hostname.
    #if 0
    if (otIp6IsAddressUnspecified(&m_cloud_address))
    {
        error = thread_dns_utils_hostname_resolve(CLOUD_HOSTNAME,
                                                  dns_response_handler,
                                                  NULL);
        ASSERT(error == OT_ERROR_NONE);
        return;
    } 
    #endif

    snprintf(payload_buffer, sizeof(payload_buffer),
        "{\"lt\":36000,\"st\":\"version=2018-10-31&res=products/CxD2hb4NHT/devices/tttt&et=1607290563&method=md5&sign=N7wUQoYBamNqlkRwKpyRHQ%%3D%%3D\"}");

    do
    {
        p_request = otCoapNewMessage(p_instance, NULL);
        if (p_request == NULL)
        {
            NRF_LOG_INFO("Failed to allocate message for CoAP Request\r\n");
            break;
        }

        otCoapMessageInit(p_request, OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST);
        //otCoapMessageGenerateToken(p_request, 4);

#define ONENET_URI_PATH    "$sys/CxD2hb4NHT/tttt/log_in"

        error = otCoapMessageAppendUriPathOptions(p_request, ONENET_URI_PATH);
        //error = otCoapMessageAppendUriPathOptions(p_request, CLOUD_URI_PATH);
        ASSERT(error == OT_ERROR_NONE);

        error = otCoapMessageAppendContentFormatOption(p_request, OT_COAP_OPTION_CONTENT_FORMAT_JSON);
        ASSERT(error == OT_ERROR_NONE);

        error = otCoapMessageSetPayloadMarker(p_request);
        ASSERT(error == OT_ERROR_NONE);


        //error = otMessageAppend(p_request, pl, strlen(pl));
        error = otMessageAppend(p_request, payload_buffer, strlen(payload_buffer));
        if (error != OT_ERROR_NONE)
        {
            break;
        }

        memset(&message_info, 0, sizeof(message_info));
        message_info.mPeerPort = OT_DEFAULT_COAP_PORT;
        message_info.mPeerAddr = m_cloud_address;
        printf("11111\n");
        error = otCoapSendRequest(p_instance, p_request, &message_info, my_otCoapResponseHandler, NULL);

    } while (false);

    if ((error != OT_ERROR_NONE) && (p_request != NULL))
    {
        NRF_LOG_INFO("Failed to send CoAP Request: %d\r\n", error);
        otMessageFree(p_request);
    }
}

void my_otCoapResponseHandler(void *               aContext,
                                      otMessage *          aMessage,
                                      const otMessageInfo *aMessageInfo,
                                      otError              aResult)
{
    if (otCoapMessageGetType(aMessage) != OT_COAP_TYPE_CONFIRMABLE &&
        otCoapMessageGetType(aMessage) != OT_COAP_TYPE_NON_CONFIRMABLE)
    {
        printf("tyz=1\n");
    }

    if (otCoapMessageGetCode(aMessage) != OT_COAP_CODE_CREATED)
    {
        printf("tyz=2\n");
    }

    if (otMessageRead(aMessage, otMessageGetOffset(aMessage), m_ccode, 8) != 1)
    {
        NRF_LOG_INFO("light handler - missing command\r\n");
    }
}

static void bsp_event_handler(bsp_event_t event)
{
    switch (event)
    {
        case BSP_EVENT_KEY_0:
            coap_client_cloud_update();
            break;
        case BSP_EVENT_KEY_1:
            coap_client_cloud_update_2();
            break;
        case BSP_EVENT_KEY_3:
            break;

        default:
            return; // no implementation needed
    }
}
#else
static void bsp_event_handler(bsp_event_t event)
{
    switch (event)
    {
        case BSP_EVENT_KEY_0:
            thread_coap_utils_unicast_light_request_send(THREAD_COAP_UTILS_LIGHT_CMD_TOGGLE);
            break;

        case BSP_EVENT_KEY_1:
        {
            m_command = ((m_command == THREAD_COAP_UTILS_LIGHT_CMD_OFF) ? THREAD_COAP_UTILS_LIGHT_CMD_ON :
                                                                          THREAD_COAP_UTILS_LIGHT_CMD_OFF);

            thread_coap_utils_multicast_light_request_send(m_command,
                                                           THREAD_COAP_UTILS_MULTICAST_REALM_LOCAL);
            break;
        }

        case BSP_EVENT_KEY_3:
            thread_coap_utils_provisioning_request_send();
            break;

        default:
            return; // no implementation needed
    }
}
#endif
/***************************************************************************************************
 * @section Callbacks
 **************************************************************************************************/

static void thread_state_changed_callback(uint32_t flags, void * p_context)
{
    if (flags & OT_CHANGED_THREAD_ROLE)
    {
        switch (otThreadGetDeviceRole(p_context))
        {
            case OT_DEVICE_ROLE_CHILD:
            case OT_DEVICE_ROLE_ROUTER:
            case OT_DEVICE_ROLE_LEADER:
                m_associated = true;
                break;

            case OT_DEVICE_ROLE_DISABLED:
            case OT_DEVICE_ROLE_DETACHED:
                m_associated = false;
            default:
                thread_coap_utils_peer_addr_clear();
                break;
        }
    }

    NRF_LOG_INFO("State changed! Flags: 0x%08x Current role: %d\r\n",
                 flags,
                 otThreadGetDeviceRole(p_context));
}

/***************************************************************************************************
 * @section Initialization
 **************************************************************************************************/

/**@brief Function for initializing the Thread Board Support Package
 */
static void thread_bsp_init(void)
{
    uint32_t error_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, bsp_event_handler);
    APP_ERROR_CHECK(error_code);

    error_code = bsp_thread_init(thread_ot_instance_get());
    APP_ERROR_CHECK(error_code);
}


/**@brief Function for initializing the Application Timer Module
 */
static void timer_init(void)
{
    uint32_t error_code = app_timer_init();
    APP_ERROR_CHECK(error_code);
}


/**@brief Function for initializing the nrf log module.
 */
static void log_init(void)
{
    ret_code_t err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEFAULT_BACKENDS_INIT();
}


/**@brief Function for initializing the Thread Stack
 */
static void thread_instance_init(void)
{
    thread_configuration_t thread_configuration =
    {
        .radio_mode        = THREAD_RADIO_MODE_RX_ON_WHEN_IDLE,
        .autocommissioning = true,
    };

    thread_init(&thread_configuration);
    thread_cli_init();
    thread_state_changed_callback_set(thread_state_changed_callback);
}


/**@brief Function for initializing the Constrained Application Protocol Module
 */

static void thread_coap_init(void)
{
    thread_coap_utils_configuration_t thread_coap_configuration =
    {
        .coap_server_enabled               = true,
        .coap_client_enabled               = true,
        .configurable_led_blinking_enabled = false,
    };

    thread_coap_utils_init(&thread_coap_configuration);
}


/**@brief Function for initializing scheduler module.
 */
static void scheduler_init(void)
{
    APP_SCHED_INIT(SCHED_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE);
}


/***************************************************************************************************
 * @section Main
 **************************************************************************************************/

int main(int argc, char * argv[])
{
    log_init();
    scheduler_init();
    timer_init();

    thread_instance_init();
    thread_coap_init();
    thread_bsp_init();
    while (true)
    {
        thread_process();
        app_sched_execute();

        if (NRF_LOG_PROCESS() == false)
        {
            thread_sleep();
        }
    }
}

/**
 *@}
 **/
