#include "jscore.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#ifdef _WIN32
    #include <winsock2.h>
#else
    #include <sys/socket.h>
    #include <netdb.h>
    #include <netinet/in.h>
#endif
#include <unistd.h>
#include <string>
#include <vector>

namespace jscore {

    using namespace std;
    struct user {
        string name;
        int points;
    };
    vector<user> score;

    #define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
    int sock;
    struct sockaddr_in client;

    int PORT = 9911;
    string HOST = "jaildoctor.duckdns.org";

#ifdef WIN32
    WSADATA     WsaData;  
#endif

    bool jscore_error = false;
    string error_message;

    void init(std::string host, const int port) {
        PORT = port;
        HOST = host;
    }

    void setErrorMessage(string message) {
        jscore_error = true;
        error_message = message;
    }

    string sendRequest(const string request) {
#ifdef WIN32
        int ret = WSAStartup(0x101,&WsaData); 
        if (ret != 0) return 0;  
#endif
        struct hostent * host = gethostbyname(HOST.c_str());

        if ( (host == NULL) || (host->h_addr == NULL) ) {
            setErrorMessage("Error retrieving DNS information.\n");
            return "";
        }

        bzero(&client, sizeof(client));
        client.sin_family = AF_INET;
        client.sin_port = htons( PORT );
        memcpy(&client.sin_addr, host->h_addr, host->h_length);

        sock = socket(AF_INET, SOCK_STREAM, 0);

        if (sock < 0) {
            setErrorMessage("Error creating socket.\n");
            return "";
        }

        if ( connect(sock, (struct sockaddr *)&client, sizeof(client)) < 0 ) {
            close(sock);
            setErrorMessage("Could not connect\n");
            return "";
        }

        string r = request + " HTTP/1.1\r\nHost: "+HOST+"\r\nConnection: close\r\n\r\n\r\n";
        if (send(sock, r.c_str(), r.length(), 0) != (int)r.length()) {
            setErrorMessage("Error sending request.\n");
            return "";
        }

        char cur;
        char start[5]="\r\n\r\n";
        int pos = 0;
        while ( recv(sock, &cur, 1,0) > 0 ) {
            if (cur==start[pos]) { pos++; if (pos == 4) break; } else { pos = 0; }
        }

        char buffer[1024]; buffer[0]=0; pos=0;
        while ( recv(sock, &cur, 1,0) > 0 ) {
            buffer[pos] = cur;
            pos++;
        }
#ifdef WIN32
        WSACleanup();
#endif
        buffer[pos]=0;
        return buffer;
    }

    const bool initOnlineScore(string game) {
        string strbuff = sendRequest("GET /score-list.php?game=" + game);
        if (jscore_error) return not jscore_error;

        user u;
        char buffer[1024];
        strcpy(buffer, strbuff.c_str());
        char *str = buffer;
        char *p = str;
        score.clear();
        while (*p!=0) {
            while (*p!=',') {p++;}
            *p=0; u.name = str; p++; str=p;
            while (*p!='\n') {p++;}
            *p=0; u.points = atoi(str); p++; str=p;
            score.push_back(u);
        }
        return not jscore_error;
    }

    const int getNumUsers() {
        return score.size();
    }
    string getUserName(const int index) {
        return score[index].name;
    }
    const int getPoints(const int index) {
        return score[index].points;
    }

    const bool updateUserPoints(string game, string user, const int points) {
        string strbuff = sendRequest("GET /score-update.php?game=" + game + "&user=" + user + "&points=" + to_string(points));
        initOnlineScore(game);
        return not jscore_error;
    }

    const int getUserPoints(string game, std::string user) {
        return atoi(sendRequest("GET /getuserpoints.php?game=" + game + "&user=" + user).c_str());
    }

    string getUserData(string game, string user) {
        return sendRequest("GET /getuserdata.php?game=" + game + "&user=" + user);
    }

    void setUserData(string game, string user, string data) {
        sendRequest("GET /setuserdata.php?game=" + game + "&user=" + user + "&data=" + data);
    }

};

