void coap_handle_notification(NetworkAddress * sourceAddress, coap_packet_t * message)
{
    if (message->token_len == sizeof(int))
    {
        int token;
        memcpy(&token, message->token, sizeof(int));
        Observation * observation = NULL;
        int index;
        for (index = 0; index < MAX_COAP_OBSERVATIONS; index++)
        {
            if ((Observations[index].Token == token) && (NetworkAddress_Compare(Observations[index].Address, sourceAddress) == 0))
            {
                observation = &Observations[index];
                break;
            }
        }
        if (observation && observation->Callback)
        {
            AddressType address;
            int ContentType = 0;
            char * payload = NULL;

            NetworkAddress_SetAddressType(sourceAddress, &address);
            coap_get_header_content_format(message, &ContentType);
            int payloadLen = coap_get_payload(message, (const uint8_t **) &payload);

            observation->Callback(observation->Context, &address, observation->Path, COAP_OPTION_TO_RESPONSE_CODE(message->code),
                    ContentType, payload, payloadLen);
        }
    }

}
/*
 * Handle the response to the observe request and the following notifications
 */
static void
notification_callback(coap_observee_t *obs, void *notification, coap_notification_flag_t flag)
{
  int len = 0;
  const uint8_t *payload = NULL;

/*  printf("Notification handler\n"); */
/*  printf("Observee URI: %s\n", obs->url); */
  if(notification) {
    len = coap_get_payload(notification, &payload);
  }
  switch(flag) {
  case NOTIFICATION_OK:
    count_notify++;
    printf("NOTIFICATION OK: %d\n%*s\n", count_notify, len, (char *)payload);
    break;
  case OBSERVE_OK: /* server accepeted observation request */
    printf("OBSERVE_OK: \n%*s\n", len, (char *)payload);
    break;
  case OBSERVE_NOT_SUPPORTED:
    printf("OBSERVE_NOT_SUPPORTED: \n%*s\n", len, (char *)payload);
    obs = NULL;
    break;
  case ERROR_RESPONSE_CODE:
    printf("ERROR_RESPONSE_CODE: \n%*s\n", len, (char *)payload);
    obs = NULL;
    break;
  case NO_REPLY_FROM_SERVER:
    printf("NO_REPLY_FROM_SERVER: "
           "removing observe registration with token %x%x\n",
           obs->token[0], obs->token[1]);
    obs = NULL;
    break;
  }
}
void coap_CoapRequestCallback(void *callback_data, void *response)
{
    TransactionType * transaction = (TransactionType *)callback_data;
    coap_packet_t * coap_response = (coap_packet_t *)response;
    int ContentType = 0;
    const char *url = NULL;
    char * payload = NULL;
    char uriBuf[64] = {0};

    if(callback_data != NULL)
    {
        if(response != NULL)
        {
            int urlLen = 0;
            if ((urlLen = coap_get_header_location_path(response, &url)))
            {
                uriBuf[0] = '/';
                memcpy(&uriBuf[1], url, urlLen);
            }

            coap_get_header_content_format(response, &ContentType);
            int payloadLen = coap_get_payload(response, (const uint8_t **)&payload);

            transaction->Callback(transaction->Context, &transaction->Address, uriBuf, COAP_OPTION_TO_RESPONSE_CODE(coap_response->code), ContentType, payload, payloadLen);
        }
        else
        {
            transaction->Callback(transaction->Context, NULL, NULL, 0, 0, NULL, 0);
        }

        transaction->TransactionUsed = false;
    }
}
/* This function is will be passed to COAP_BLOCKING_REQUEST() to handle responses. */
void
client_chunk_handler(void *response)
{
  const uint8_t *chunk;
  int len = coap_get_payload(response, &chunk);
  printf("|%.*s", len, (char *)chunk);
}
static void
response_handler(coap_packet_t* response)
{
  uint16_t payload_len = 0;
  uint8_t* payload = NULL;
  payload_len = coap_get_payload(response, &payload);

  PRINTF("Response transaction id: %u", response->tid);
  if (payload) {
    memcpy(temp, payload, payload_len);
    temp[payload_len] = 0;
    PRINTF(" payload: %s\n", temp);
  }
}
/* This function is will be passed to COAP_BLOCKING_REQUEST() to handle responses. */
void
client_chunk_handler(void *response)
{
  const uint8_t *chunk;

  int len = coap_get_payload(response, &chunk);

#if PLATFORM_HAS_LEDS
  /* set red led when receiving a packet */
  leds_on(LEDS_RED);
#endif

/*  printf("|%.*s", len, (char *)chunk); */
  printf("RX: %d\n%s\n", len, (char *)chunk);
}
oc_discovery_flags_t
oc_ri_process_discovery_payload(struct coap_packet_rx *rsp,
                                oc_discovery_cb_t *handler,
                                oc_endpoint_t *endpoint)
{
  oc_discovery_flags_t ret = OC_CONTINUE_DISCOVERY;
  oc_string_t uri = {
      .os_sz = 0,
      .os_str = NULL
  };
  oc_string_t di = {
      .os_sz = 0,
      .os_str = NULL
  };
  bool secure = false;
  uint16_t dtls_port = 0, default_port = endpoint->oe_ip.v6.port;
  oc_string_array_t types = {};
  oc_interface_mask_t interfaces = 0;
  oc_server_handle_t handle;
  uint16_t data_off;
  struct os_mbuf *m;
  int len;

  memcpy(&handle.endpoint, endpoint, sizeof(oc_endpoint_t));

  oc_rep_t *array = 0, *rep;

  len = coap_get_payload(rsp, &m, &data_off);
  int s = oc_parse_rep(m, data_off, len, &rep);
  if (s == 0)
    array = rep;
  while (array != NULL) {
    oc_rep_t *device_map = array->value_object;
    while (device_map != NULL) {
      switch (device_map->type) {
      case STRING:
        if (oc_string_len(device_map->name) == 2 &&
            strncmp(oc_string(device_map->name), "di", 2) == 0)
          di = device_map->value_string;
        break;
      default:
        break;
      }
      device_map = device_map->next;
    }
    device_map = array->value_object;
    while (device_map != NULL) {
      switch (device_map->type) {
      case OBJECT_ARRAY: {
        oc_rep_t *links = device_map->value_object_array;
        while (links != NULL) {
          switch (links->type) {
          case OBJECT: {
            oc_rep_t *resource_info = links->value_object;
            while (resource_info != NULL) {
              switch (resource_info->type) {
              case STRING:
                uri = resource_info->value_string;
                break;
              case STRING_ARRAY:
                if (oc_string_len(resource_info->name) == 2 &&
                    strncmp(oc_string(resource_info->name), "rt", 2) == 0)
                  types = resource_info->value_array;
                else {
                  interfaces = 0;
                  int i;
                  for (i = 0; i < oc_string_array_get_allocated_size(
                                    resource_info->value_array);
                       i++) {
                    interfaces |= oc_ri_get_interface_mask(
                      oc_string_array_get_item(resource_info->value_array, i),
                      oc_string_array_get_item_size(resource_info->value_array,
                                                    i));
                  }
                }
                break;
              case OBJECT: {
                oc_rep_t *policy_info = resource_info->value_object;
                while (policy_info != NULL) {
                  if (policy_info->type == INT &&
                      oc_string_len(policy_info->name) == 4 &&
                      strncmp(oc_string(policy_info->name), "port", 4) == 0) {
                    dtls_port = policy_info->value_int;
                  }
                  if (policy_info->type == BOOL &&
                      oc_string_len(policy_info->name) == 3 &&
                      strncmp(oc_string(policy_info->name), "sec", 3) == 0 &&
                      policy_info->value_boolean == true) {
                    secure = true;
                  }
                  policy_info = policy_info->next;
                }
              } break;
              default:
                break;
              }
              resource_info = resource_info->next;
            }
            if (secure) {
              handle.endpoint.oe_ip.v6.port = dtls_port;
              handle.endpoint.oe_ip.flags |= SECURED;
            } else {
              handle.endpoint.oe_ip.v6.port = default_port;
              handle.endpoint.oe_ip.flags &= ~SECURED;
            }

            if (handler(oc_string(di), oc_string(uri), types, interfaces,
                        &handle) == OC_STOP_DISCOVERY) {
              ret = OC_STOP_DISCOVERY;
              goto done;
            }
            dtls_port = 0;
            secure = false;
          } break;
          default:
            break;
          }
          links = links->next;
        }
      } break;
      default:
        break;
      }
      device_map = device_map->next;
    }
    array = array->next;
  }
done:
  oc_free_rep(rep);
  return ret;
}
/*----------------------------------------------------------------------------*/
void
pf_handler(void *request, void *response, uint8_t *buffer,
           uint16_t preferred_size, int32_t *offset, tres_res_t *task)
{
  rest_resource_flags_t method;
  uint32_t num = 0;
  uint8_t more = 0;
  uint16_t size = 0;
  uint32_t b1_offset = 0;
  uint16_t plen;
  const uint8_t *payload;

  method = REST.get_method_type(request);
  PRINTF("Method %d\n", method);
  switch (method) {
  case METHOD_GET:
    //TODO: retrieve python bytecode
    REST.set_response_status(response, REST.status.NOT_IMPLEMENTED);
    break;
  case METHOD_PUT:
    if(coap_get_header_block1(request, &num, &more, &size, &b1_offset)) {
      coap_set_header_block1(response, num, more, size);
    }

    PRINTF("Request on /pf: num = %"PRIu32", more = %"PRIu8", size = %"PRIu16
        ", offset = %"PRIu32"\n", num, more, size, b1_offset);
    // if it's the first packet, stop input resource monitoring
    if(b1_offset == 0) {
      tres_stop_monitoring(task);
      // if the user is actually updating the PF, clear it first
      if(task->pf_img) {
        tres_mem_pf_clear(task->pf_img);
        task_reset_state(task);
      }
    }
    plen = coap_get_payload(request, &payload);
    PRINTF("Payload len: %d\n", plen);
#if TRES_COPPER_WORKAROUND
    // if no block-wise transfer and payload len equal to maximum size, then
    // we probably need block wise transfer. FIXME: that may be not true
    if(size == 0 && plen == REST_MAX_CHUNK_SIZE) {
      more = 1;
      size = REST_MAX_CHUNK_SIZE;
      coap_set_header_block1(response, num, more, size);
    }
#endif
    // if it's the first packet, allocate a memory slot
    if(b1_offset == 0) {
      // ... but, if the first byte is not 0x0a than it is not python bytecode
      if(payload[0] != 0x0a) {
        REST.set_response_status(response, REST.status.BAD_REQUEST);
        return;
      }
      task->sid = tres_mem_pf_store_start();
      if(task->sid < 0) {
        PRINTF("No memory slot available\n");
        // TODO: we should return a server error code
        return;
      }
    }
    // store the current block
    if(tres_mem_pf_store_step(task->sid, payload, plen) < 0) {
      PRINTF("PF too big\n");
      return;
    }
    // if it's the final block, finalize the memory slot
    if(more == 0) {
      task->pf_img = tres_mem_pf_store_done(task->sid);
      PRINTF("task->pf_img: %p\n", task->pf_img);
      REST.set_response_status(response, REST.status.CHANGED);
    }
    break;
  case METHOD_POST:
    REST.set_response_status(response, REST.status.METHOD_NOT_ALLOWED);
    break;
  case METHOD_DELETE:
    if(task->pf_img) {
      tres_stop_monitoring(task);
      tres_mem_pf_clear(task->pf_img);
      task_reset_state(task);
      task->pf_img = NULL;
    }
    REST.set_response_status(response, REST.status.DELETED);
    break;
  default:
    break;
  }
}
static int coap_HandleRequest(void *packet, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset)
{
    int result = 1;
    const char *url = NULL;
    int urlLen = 0;
    const uint8_t * payload = NULL;
    int payloadLen = 0;
    int content = -1;

    coap_packet_t * const request = (coap_packet_t *) packet;

    CoapResponse coapResponse =
    { .responseContent = buffer, .responseContentLen = preferred_size, .responseCode = 400, };

    payloadLen = coap_get_payload(request, &payload);

    if ((urlLen = coap_get_header_uri_path(request, &url)))
    {
        char uriBuf[MAX_COAP_PATH] = { 0 };
        rest_resource_flags_t method = (rest_resource_flags_t) (1 << (((coap_packet_t *) packet)->code - 1)); //coap_get_rest_method(request);

        uriBuf[0] = '/';
        memcpy(&uriBuf[1], url, urlLen);

        char queryBuf[128] = "?";
        const char * query = NULL;

        int queryLength = coap_get_header_uri_query(request, &query);
        if (queryLength > 0)
            memcpy(&queryBuf[1], query, queryLength);

        queryBuf[queryLength+1] = '\0';

        CoapRequest coapRequest =
        { .ctxt = context, .addr =
        { 0 }, .path = uriBuf, .query = queryBuf, .token = request->token, .tokenLength = request->token_len, .requestContent = payload,
                .requestContentLen = payloadLen, };

        NetworkAddress_SetAddressType(sourceAddress, &coapRequest.addr);

        switch (method)
        {
        case METHOD_GET:

            coap_get_header_accept(request, &content);
            coapRequest.contentType = content;

            int32_t observe;

            if (!coap_get_header_observe(request, &observe))
                observe = -1;

            switch (observe)
            {
            case -1:
                Lwm2m_Debug("Coap GET for %s\n", uriBuf);
                coapRequest.type = COAP_GET_REQUEST;
                requestHandler(&coapRequest, &coapResponse);
                break;
            case 0:
                Lwm2m_Debug("Coap OBSERVE for %s\n", uriBuf);

                coapRequest.type = COAP_OBSERVE_REQUEST;
                requestHandler(&coapRequest, &coapResponse);
                coap_set_header_observe(response, 1);
                break;
            case 1:
                Lwm2m_Debug("Coap CANCEL OBSERVE for %s\n", uriBuf);

                coapRequest.type = COAP_CANCEL_OBSERVE_REQUEST;
                requestHandler(&coapRequest, &coapResponse);
                break;
            default:
                break;
            }
            coap_set_header_content_format(response, coapResponse.responseContentType); /* text/plain is the default, hence this option could be omitted. */
            break;

        case METHOD_POST:
            coap_get_header_content_format(request, &content);
            coapRequest.contentType = content;
            coapRequest.type = COAP_POST_REQUEST;
            Lwm2m_Debug("Coap POST for %s\n", uriBuf);
            requestHandler(&coapRequest, &coapResponse);
            if (coapResponse.responseContentLen > 0 && coapResponse.responseCode == 201)
            {
                coap_set_header_location_path(response, coapResponse.responseContent);
            }
            break;

        case METHOD_PUT:
            coap_get_header_content_format(request, &content);
            coapRequest.contentType = content;
            coapRequest.type = COAP_PUT_REQUEST;

            Lwm2m_Debug("Coap PUT for %s\n", uriBuf);
            requestHandler(&coapRequest, &coapResponse);
            break;

        case METHOD_DELETE:
            coapRequest.contentType = ContentType_None;
            coapRequest.type = COAP_DELETE_REQUEST;

            Lwm2m_Debug("Coap DELETE for %s\n", uriBuf);
            requestHandler(&coapRequest, &coapResponse);
            break;

        default:
            break;
        }

        if (coapResponse.responseContentLen > 0 && coapResponse.responseCode == 205)
        {
            coap_set_payload(response, coapResponse.responseContent, coapResponse.responseContentLen);
        }
    }

    coap_set_status_code(response, COAP_RESPONSE_CODE(coapResponse.responseCode));
    return result;
}