/*
 * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * This example shows how to update Greengrass local shadow and get the 
 * shadow document back.
 */

#include <stdio.h>
#include <stdlib.h>
#include "greengrasssdk.h"

#define BUFFER_SIZE 512

/* loop read the request bytes into buffer. */
gg_error loop_request_read(gg_request ggreq, void *buffer,
                           size_t buffer_size, size_t *total_read) {
    gg_error err = GGE_SUCCESS;
    uint8_t *read_index = (uint8_t*)buffer;
    size_t remaining_buf_size = buffer_size;
    size_t amount_read = 0;

    do {
        err = gg_request_read(ggreq, read_index, remaining_buf_size,
            &amount_read);
        if(err) {
            gg_log(GG_LOG_ERROR, "gg_request_read had an error");
            goto cleanup;
        }
        *total_read += amount_read;
        read_index += amount_read;
        remaining_buf_size -= amount_read;
    } while(amount_read);

cleanup:
    return err;
}

gg_error update_thing_shadow() {
    gg_error err = GGE_SUCCESS;
    gg_request ggreq = NULL;
    const char thing_name[] = "foo";
    const char payload[] =
        "{"
            "\"state\": {"
                "\"desired\": {"
                    "\"mode\": \"OFF\""
                "}"
            "}"
        "}";
    size_t amount_read = 0;
    struct gg_request_result result;
    char read_buf[BUFFER_SIZE];

    err = gg_request_init(&ggreq);
    if(err) {
        gg_log(GG_LOG_ERROR, "Failed to initialize request");
        goto done;
    }

    err = gg_update_thing_shadow(ggreq, thing_name, payload, &result);
    if(err) {
        gg_log(GG_LOG_ERROR, "gg_update_thing_shadow failed with err %d",
            err);
        goto cleanup;
    }

    gg_log(GG_LOG_INFO, "gg_update_thing_shadow had result request_status %d",
            result.request_status);
    if(result.request_status != GG_REQUEST_SUCCESS) {
        // shadow update failed, reads error response
        err = loop_request_read(ggreq, read_buf, BUFFER_SIZE, &amount_read);
        if(err) {
            gg_log(GG_LOG_ERROR,
                "Failed to read shadow data. amount_read(%zu), BUFFER_SIZE(%zu), err(%d)",
                amount_read, BUFFER_SIZE, err);
            goto cleanup;
        }

        gg_log(GG_LOG_ERROR, "update_thing_shadow failed. error messsage: %.*s",
                (int)amount_read, read_buf);
    } else {
        // shadow update succeeded, reads success response
        err = loop_request_read(ggreq, read_buf, BUFFER_SIZE, &amount_read);
        if(err) {
            gg_log(GG_LOG_ERROR,
                "Failed to read shadow data. amount_read(%zu), BUFFER_SIZE(%zu), err(%d)",
                amount_read, BUFFER_SIZE, err);
            goto cleanup;
        }
        gg_log(GG_LOG_INFO, "update_thing_shadow succeed. response: %.*s",
                (int)amount_read, read_buf);
    }

cleanup:
    gg_request_close(ggreq);

done:
    return err;
}

gg_error get_thing_shadow() {
    gg_error err = GGE_SUCCESS;
    gg_request ggreq = NULL;
    const char thing_name[] = "foo";
    size_t amount_read = 0;
    struct gg_request_result result;
    char read_buf[BUFFER_SIZE];

    err = gg_request_init(&ggreq);
    if(err) {
        gg_log(GG_LOG_ERROR, "Failed to initialize request");
        goto done;
    }

    err = gg_get_thing_shadow(ggreq, thing_name, &result);
    if(err) {
        gg_log(GG_LOG_ERROR, "gg_get_thing_shadow failed with err %d", err);
        goto cleanup;
    }

    gg_log(GG_LOG_INFO, "gg_get_thing_shadow had result request_status %d",
            result.request_status);
    if(result.request_status != GG_REQUEST_SUCCESS) {
        // get shadow failed, reads error response
        err = loop_request_read(ggreq, read_buf, BUFFER_SIZE, &amount_read);
        if(err) {
            gg_log(GG_LOG_ERROR,
                    "Failed to read shadow data. amount_read(%zu), READ_BUFFER_SIZE(%zu), err(%d)",
                    amount_read, BUFFER_SIZE, err);
            goto cleanup;
        }

        gg_log(GG_LOG_ERROR, "get_thing_shadow failed. error message: %.*s",
                (int)amount_read, read_buf);
    } else {
        // get shadow succeeded, reads success response
        err = loop_request_read(ggreq, read_buf, BUFFER_SIZE, &amount_read);
        if(err) {
            gg_log(GG_LOG_ERROR,
                    "Failed to read shadow data. amount_read(%zu), READ_BUFFER_SIZE(%zu), err(%d)",
                    amount_read, BUFFER_SIZE, err);
            goto cleanup;
        }

        gg_log(GG_LOG_INFO, "get_thing_shadow succeeded. response: %.*s",
                (int)amount_read, read_buf);
    }
cleanup:
    gg_request_close(ggreq);

done:
    return err;
}


void handler(const gg_lambda_context *cxt) {
    /* cxt is not used. */
    (void) cxt;

    update_thing_shadow();
    get_thing_shadow();
}

int main() {
    gg_error err = GGE_SUCCESS;

    err = gg_global_init(0);
    if(err) {
        gg_log(GG_LOG_ERROR, "gg_global_init failed %d", err);
        goto cleanup;
    }

    /* start the runtime in blocking mode. This blocks forever. */
    gg_runtime_start(handler, 0);

cleanup:
    return -1;
}