/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * are also available at https://curl.se/docs/copyright.html.
 *
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 * copies of the Software, and permit persons to whom the Software is
 * furnished to do so, under the terms of the COPYING file.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 * SPDX-License-Identifier: curl
 *
 ***************************************************************************/
/* <DESC>
 * A basic application wait on select
 * </DESC>
 */

#include <stdio.h>
#include <string.h>

/* somewhat unix-specific */
#include <sys/time.h>
#include <unistd.h>

/* curl stuff */
#include <curl/curl.h>

/*
 * Download an HTTP file and upload an FTP file simultaneously.
 */

#define HANDLECOUNT 2   /* Number of simultaneous transfers */
#define HTTP_HANDLE 0   /* Index for the HTTP transfer */
#define WS_HANDLE 1    /* Index for the WS transfer */

void printHelp()
{
    printf(
                "Commands\n"
                "--------\n"
                " h = Help\n"
                " p = Query Http\n"
                " w = Start Websocket Connection\n"
                " q = Quit\n"
                );
}

int main(void)
{
    CURL *handles[HANDLECOUNT];
    CURLM *multi_handle;

    int still_running = 0; /* keep number of running handles */
    int i;

    CURLMsg *msg; /* for picking up messages with the transfer status */
    int msgs_left; /* how many messages are left */

    /* Allocate one CURL handle per transfer */
    for(i = 0; i<HANDLECOUNT; i++)
        handles[i] = curl_easy_init();


    /* init a multi stack */
    multi_handle = curl_multi_init();


    /* we start some action by calling perform right away */
    curl_multi_perform(multi_handle, &still_running);

    char cmd=0;
    char quitFlag=0;
    char httpHandleLoaded=0;
    char wsHandleLoaded=0;

    printHelp();

    while(!quitFlag) {
        struct timeval timeout;
        int rc; /* select() return code */
        CURLMcode mc; /* curl_multi_fdset() return code */

        fd_set fdread;
        fd_set fdwrite;
        fd_set fdexcep;
        int maxfd = -1;

        long curl_timeo = -1;


        int selected=0;
        while(!selected)
        {
            curl_timeo = -1;

            FD_ZERO(&fdread);
            FD_ZERO(&fdwrite);
            FD_ZERO(&fdexcep);


            /* set a suitable timeout to play around with */
            timeout.tv_sec = 1;
            timeout.tv_usec = 0;

            curl_multi_timeout(multi_handle, &curl_timeo);
            if(curl_timeo >= 0) {
                timeout.tv_sec = curl_timeo / 1000;
                if(timeout.tv_sec > 1)
                    timeout.tv_sec = 1;
                else
                    timeout.tv_usec = (int)(curl_timeo % 1000) * 1000;
            }

            /* get file descriptors from the transfers */
            mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);

            if(mc != CURLM_OK) {
                fprintf(stderr, "curl_multi_fdset() failed, code %d.\n", mc);
                break;
            }


/* On success the value of maxfd is guaranteed to be >= -1.
 *
 * We call select(maxfd + 1, ...); specially in case of (maxfd == -1) there are
 * no fds ready yet so we call select(0, ...) --or Sleep() on Windows--
 * to sleep 100ms, which is the minimum suggested value in the
 * curl_multi_fdset() doc. */

// ATTENTION: In this code maxfd never get -1
            FD_SET(0,&fdread);
            if(maxfd == -1)
                maxfd = 0; //keyboard handle

            if(maxfd == -1) {
#ifdef _WIN32
                Sleep(100);
                rc = 0;
#else
                /* Portable sleep for platforms other than Windows. */
                struct timeval wait = { 0, 100 * 1000 }; /* 100ms */
                rc = select(0, NULL, NULL, NULL, &wait);
#endif
            }
            else {
                /* Note that on some platforms 'timeout' may be modified by select().
         If you need access to the original value save a copy beforehand. */
                rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
            }

            switch(rc) {
                case -1:
                    /* select error */
                break;
                case 0: /* timeout */
                    curl_multi_perform(multi_handle, &still_running);
                break;
                default: /* action */
                    curl_multi_perform(multi_handle, &still_running);
                    selected = 1;
                break;
            }
        }

// Checking keyboard action
        if(FD_ISSET(0,&fdread))
        {
            char key;
            while(read(0, &cmd, 1) == 1)
            {
                if(cmd == '\n')
                    break;
                key=cmd;
            }

            if(key == 'h')
            {
                printHelp();
                break;
            }
            else if(key == 'q')
            {
                quitFlag = 1;
                break;
            }
            else if(key == 'p')
            {
                CURLMcode m = curl_multi_remove_handle(multi_handle, handles[HTTP_HANDLE]);
                if(m != CURLM_OK) {
                    fprintf(stderr, "curl_multi_remove_handle() failed, code %d.\n", m);
                }
                else {
                    curl_easy_reset(handles[HTTP_HANDLE]);
                    curl_easy_setopt(handles[HTTP_HANDLE], CURLOPT_URL, "http://localhost:8080/query");
                    curl_easy_setopt(handles[HTTP_HANDLE], CURLOPT_POSTFIELDS, "");
                    curl_easy_setopt(handles[HTTP_HANDLE], CURLOPT_POSTFIELDSIZE, 0);
                    curl_easy_setopt(handles[HTTP_HANDLE], CURLOPT_CUSTOMREQUEST, "GET");
                    m = curl_multi_add_handle(multi_handle, handles[HTTP_HANDLE]);
                    if(m != CURLM_OK)
                        fprintf(stderr, "curl_multi_add_handle() failed, code %d.\n", m);
                    else
                    {
                        printf("Sending HTTP query\n");
                        httpHandleLoaded = 1;
                        curl_multi_perform(multi_handle, &still_running);
                    }
                }
            }
            else if(key == 'w')
            {
                CURLMcode m = curl_multi_remove_handle(multi_handle, handles[WS_HANDLE]);
                if(m != CURLM_OK) {
                    fprintf(stderr, "curl_multi_remove_handle() failed, code %d.\n", m);
                }
                else {
                    curl_easy_reset(handles[WS_HANDLE]);
                    curl_easy_setopt(handles[WS_HANDLE], CURLOPT_URL, "ws://localhost:8080/ws");
                    curl_easy_setopt(handles[WS_HANDLE], CURLOPT_CONNECT_ONLY, 2L) ;
                    m = curl_multi_add_handle(multi_handle, handles[WS_HANDLE]);
                    if(m != CURLM_OK)
                        fprintf(stderr, "curl_multi_add_handle() failed, code %d.\n", m);
                    else
                    {
                        printf("Connecting WS query\n");
                        curl_multi_perform(multi_handle, &still_running);
                    }
                }
            }
        }
        /* See how the transfers went */
        while((msg = curl_multi_info_read(multi_handle, &msgs_left)))
        {
            if(msg->msg == CURLMSG_DONE)
            {
                int idx;

                /* Find out which handle this message is about */
                if(msg->easy_handle == handles[HTTP_HANDLE])
                {
                    printf("\nHTTP transfer completed with status %d\n", msg->data.result);

                }
                else if(msg->easy_handle == handles[WS_HANDLE])
                {
                    printf("WS transfer completed with status %d\n", msg->data.result);
                    size_t rlen;
                    struct curl_ws_frame *meta;
                    char buffer[256];
                    CURLcode result = curl_ws_recv(msg->easy_handle , buffer, sizeof(buffer), &rlen, &meta);
                    buffer[rlen]='\0';
                    if(result == CURLE_OK)
                        printf("Read Data %s\n", buffer);
                    else
                        printf("Error reading data\n");
                    CURLMcode m;
                    // This code reconnect ws conection. An endless loop begins
                    //------------------------------------------------------------
                    //     m = curl_multi_remove_handle(multi_handle, msg->easy_handle);
                    //     printf("curl_multi_remove_handle WS -> %d\n",m);
                    //     m = curl_multi_add_handle(multi_handle, msg->easy_handle);
                    //     printf("curl_multi_add_handle WS -> %d\n",m);

                    // This code causes an error 7 CURLM_ADDED_ALREADY
                    // -----------------------------------------------
                    //     m = curl_multi_add_handle(multi_handle, msg->easy_handle);
                    //     printf("curl_multi_add_handle WS -> %d\n",m);

// curl_multi_perform() no error, but nothing happens
                        m = curl_multi_perform(multi_handle, &still_running);
                        printf("curl_multi_perform WS -> %d\n",m);
                    break;
                }
            }
        }
    }


    curl_multi_cleanup(multi_handle);

    /* Free the CURL handles */
    for(i = 0; i<HANDLECOUNT; i++)
        curl_easy_cleanup(handles[i]);

    return 0;
}
