/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#include "kubernetes.h"
#include "terminal/terminal.h"

#include <guacamole/client.h>
#include <libwebsockets.h>

#include <pthread.h>
#include <stdbool.h>
#include <string.h>

void guac_kubernetes_receive_data(guac_client* client,
        const char* buffer, size_t length) {

    guac_kubernetes_client* kubernetes_client =
        (guac_kubernetes_client*) client->data;

    /* Strip channel index from beginning of buffer */
    int channel = *(buffer++);
    length--;

    switch (channel) {

        /* Write STDOUT / STDERR directly to terminal as output */
        case GUAC_KUBERNETES_CHANNEL_STDOUT:
        case GUAC_KUBERNETES_CHANNEL_STDERR:
            guac_terminal_write(kubernetes_client->term, buffer, length);
            break;

        /* Ignore data on other channels */
        default:
            guac_client_log(client, GUAC_LOG_DEBUG, "Received %i bytes along "
                    "channel %i.", length, channel);

    }

}

void guac_kubernetes_send_message(guac_client* client,
        int channel, const char* data, int length) {

    guac_kubernetes_client* kubernetes_client =
        (guac_kubernetes_client*) client->data;

    pthread_mutex_lock(&(kubernetes_client->outbound_message_lock));

    /* Add message to buffer if space is available */
    if (kubernetes_client->outbound_messages_waiting
            < GUAC_KUBERNETES_MAX_OUTBOUND_MESSAGES) {

        /* Calculate storage position of next message */
        int index = (kubernetes_client->outbound_messages_top
                  + kubernetes_client->outbound_messages_waiting)
                  % GUAC_KUBERNETES_MAX_OUTBOUND_MESSAGES;

        /* Obtain pointer to message slot at calculated position */
        guac_kubernetes_message* message =
            &(kubernetes_client->outbound_messages[index]);

        /* Copy details of message into buffer */
        message->channel = channel;
        memcpy(message->data, data, length);
        message->length = length;

        /* One more message is now waiting */
        kubernetes_client->outbound_messages_waiting++;

        /* Notify libwebsockets that we need a callback to send pending
         * messages */
        lws_callback_on_writable(kubernetes_client->wsi);
        lws_cancel_service(kubernetes_client->context);

    }

    /* Warn if data has to be dropped */
    else
        guac_client_log(client, GUAC_LOG_WARNING, "Send buffer could not be "
                "flushed in time to handle additional data. Outbound "
                "message dropped.");

    pthread_mutex_unlock(&(kubernetes_client->outbound_message_lock));

}

bool guac_kubernetes_write_pending_message(guac_client* client) {

    bool messages_remain;
    guac_kubernetes_client* kubernetes_client =
        (guac_kubernetes_client*) client->data;

    pthread_mutex_lock(&(kubernetes_client->outbound_message_lock));

    /* Send one message from top of buffer */
    if (kubernetes_client->outbound_messages_waiting > 0) {

        /* Obtain pointer to message at top */
        int top = kubernetes_client->outbound_messages_top;
        guac_kubernetes_message* message =
            &(kubernetes_client->outbound_messages[top]);

        /* Write message including channel index */
        lws_write(kubernetes_client->wsi,
                ((unsigned char*) message) + LWS_PRE,
                message->length + 1, LWS_WRITE_BINARY);

        /* Advance top to next message */
        kubernetes_client->outbound_messages_top++;
        kubernetes_client->outbound_messages_top %=
            GUAC_KUBERNETES_MAX_OUTBOUND_MESSAGES;

        /* One less message is waiting */
        kubernetes_client->outbound_messages_waiting--;

    }

    /* Record whether messages remained at time of completion */
    messages_remain = (kubernetes_client->outbound_messages_waiting > 0);

    pthread_mutex_unlock(&(kubernetes_client->outbound_message_lock));

    return messages_remain;

}


