#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>

#include <mpack.h>

#include <command.h>
#include <logger.h>
#include <helpers.h>
#include <enums.h>
#include <models/controller.h>

int
command_set_relay_schedule(relay_t *relay)
{
    controller_t *controller = controller_get_by_id(relay->controller_id);
    if(!controller)
    {
        LOGGER_ERR("couldn't find controller\n");
        return 1;
    }

    char* payload;
    size_t payload_size;
    mpack_writer_t writer;
    mpack_writer_init_growable(&writer, &payload, &payload_size);

    // 3 = code, relay num, relay name, schedules(array)
    mpack_start_map(&writer, 3);

    mpack_write_uint(&writer, COMMAND_MAPPING_CODE);
    mpack_write_u8(&writer, COMMAND_CODE_SET_SCHEDULE);

    mpack_write_uint(&writer, COMMAND_MAPPING_RELAY_NUM);
    mpack_write_u8(&writer, relay->number);

    mpack_write_uint(&writer, COMMAND_MAPPING_SCHEDULES_ARRAY);
    // 7 = days of week
    mpack_start_array(&writer, 7);
    for(int i = 0; i < 7; ++i)
    {
        uint16_t *periods_blob = schedule_periods_to_blob(relay->schedules[i]);
        uint16_t periods_count = periods_blob[0];

        // 3 = code, relaynum, schedules(array)
        mpack_start_map(&writer, 3);

        mpack_write_uint(&writer, COMMAND_MAPPING_PERIODS_COUNT);
        mpack_write_u16(&writer, periods_count);

        mpack_write_uint(&writer, COMMAND_MAPPING_SCHEDULE_ID);
        mpack_write_bin(&writer, (char*)relay->schedules[0]->uid, sizeof(uuid_t));

        mpack_write_uint(&writer, COMMAND_MAPPING_PERIODS_BLOB);
        // periods + 1 to skip length in periods[0]
        // periods_count * 2 because each uint16_t is a timestamp. 2 are start and end
        mpack_write_bin(&writer, (char*)(periods_blob + 1), sizeof(uint16_t) * periods_count * 2);

        mpack_finish_map(&writer);

        free(periods_blob);
    }
    mpack_finish_array(&writer);

    mpack_finish_map(&writer);

    // finish writing
    if (mpack_writer_destroy(&writer) != mpack_ok)
    {
        LOGGER_ERR("an error occurred encoding the data");
        controller_free(controller);
        return 1;
    }

    int result = command_send(controller, COMMAND_CODE_SET_SCHEDULE, payload, payload_size);

    controller_free(controller);
    free(payload);
    return result;
}

int
command_set_controller_name(controller_t *controller)
{
    char* payload;
    size_t payload_size;
    mpack_writer_t writer;
    mpack_writer_init_growable(&writer, &payload, &payload_size);

    // write the example on the msgpack homepage
    mpack_start_map(&writer, 2);

    mpack_write_uint(&writer, COMMAND_MAPPING_CODE);
    mpack_write_u8(&writer, COMMAND_CODE_SET_NAME);

    mpack_write_uint(&writer, COMMAND_MAPPING_NAME);
    mpack_write_cstr(&writer, controller->name);

    mpack_finish_map(&writer);

    // finish writing
    if (mpack_writer_destroy(&writer) != mpack_ok)
    {
        LOGGER_ERR("an error occurred encoding the data");
        return 1;
    }

    int result = command_send(controller, COMMAND_CODE_SET_NAME, payload, payload_size);

    free(payload);
    return result;
}
    
int
command_send(controller_t *controller, int command_code, char *payload, uint32_t payload_size)
{
    LOGGER_DEBUG("commanding %d\n", command_code);

    int bytes_transferred;

    int fd_controller = helper_connect_tcp_server(controller->ip, controller->port);

    if(fd_controller == -1)
    {
        LOGGER_ERR("can't open command socket %s:%d\n", controller->ip, controller->port);
        return 1;
    }

    if((bytes_transferred = send(fd_controller, &payload_size, sizeof(payload_size), 0)) <= 0)
    {
        LOGGER_ERR("error during sending size\n");
        return 1;
    }
    if((bytes_transferred = send(fd_controller, payload, payload_size, 0)) <= 0)
    {
        LOGGER_ERR("error during sending\n");
        return 1;
    }

    close(fd_controller);
    return 0;
}

int
command_pulse(relay_t *relay, uint8_t duration)
{
    controller_t *controller = controller_get_by_id(relay->controller_id);
    if(!controller)
    {
        LOGGER_ERR("couldn't find controller\n");
        return 1;
    }

    char* payload;
    size_t payload_size;
    mpack_writer_t writer;
    mpack_writer_init_growable(&writer, &payload, &payload_size);

    // 3 = code, relay num, relay name, schedules(array)
    mpack_start_map(&writer, 3);

    mpack_write_uint(&writer, COMMAND_MAPPING_CODE);
    mpack_write_u8(&writer, COMMAND_CODE_PULSE);

    mpack_write_uint(&writer, COMMAND_MAPPING_RELAY_NUM);
    mpack_write_u8(&writer, relay->number);

    mpack_write_uint(&writer, COMMAND_MAPPING_PULSE_DURATION);
    mpack_write_u8(&writer, duration);

    mpack_finish_map(&writer);

    // finish writing
    if (mpack_writer_destroy(&writer) != mpack_ok)
    {
        LOGGER_ERR("an error occurred encoding the data");
        controller_free(controller);
        return 1;
    }

    int result = command_send(controller, COMMAND_CODE_PULSE, payload, payload_size);

    controller_free(controller);
    free(payload);
    return result;
}