#include "moonlight.hpp"

#include "ppapi/cpp/var_array_buffer.h"

#include <http.h>
#include <errors.h>
#include <string.h>

#include <mkcert.h>
#include <openssl/bio.h>
#include <openssl/pem.h>

X509 *g_Cert;
EVP_PKEY *g_PrivateKey;
char *g_UniqueId;
char *g_CertHex;

void MoonlightInstance::MakeCert(int32_t callbackId, pp::VarArray args)
{
    pp::VarDictionary ret;
    ret.Set("callbackId", pp::Var(callbackId));
    ret.Set("type", pp::Var("resolve"));
    
    pp::VarDictionary retData;
    
    CERT_KEY_PAIR certKeyPair = mkcert_generate();
    
    BIO* bio = BIO_new(BIO_s_mem());
    
    PEM_write_bio_X509(bio, certKeyPair.x509);
    BUF_MEM *mem = NULL;
    BIO_get_mem_ptr(bio, &mem);
    
    std::string cert(mem->data, mem->length);
    
    BIO_free(bio);
    
    BIO* biokey = BIO_new(BIO_s_mem());
    PEM_write_bio_PrivateKey(biokey, certKeyPair.pkey, NULL, NULL, 0, NULL, NULL);
    BIO_get_mem_ptr(biokey, &mem);
    
    std::string pkey(mem->data, mem->length);
    
    BIO_free(biokey);
    
    retData.Set("privateKey", pkey.c_str());
    retData.Set("cert", cert.c_str());
    
    ret.Set("ret", retData);
    PostMessage(ret);
}

void MoonlightInstance::LoadCert(const char* certStr, const char* keyStr)
{
    char* _certStr = strdup(certStr);
    char* _keyStr = strdup(keyStr);
    
    BIO *bio = BIO_new_mem_buf(_certStr, -1);
    if(!(g_Cert = PEM_read_bio_X509(bio, NULL, NULL, NULL))) {
        PostMessage(pp::Var("Error loading cert into memory"));
    }
    BIO_free_all(bio);
    
    bio = BIO_new_mem_buf(_keyStr, -1);
    if(PEM_read_bio_PrivateKey(bio, &g_PrivateKey, NULL, NULL) == NULL) {
        PostMessage(pp::Var("Error loading private key into memory"));
    }
    BIO_free_all(bio);
    
    // Convert the PEM cert to hex
    g_CertHex = (char*)malloc((strlen(certStr) * 2) + 1);
    for (int i = 0; i < strlen(certStr); i++) {
        sprintf(&g_CertHex[i * 2], "%02x", certStr[i]);
    }
    
    free(_certStr);
    free(_keyStr);
}

void MoonlightInstance::NvHTTPInit(int32_t callbackId, pp::VarArray args)
{
    std::string _cert = args.Get(0).AsString();
    std::string _key = args.Get(1).AsString();
    std::string _uniqueId = args.Get(2).AsString();
    
    LoadCert(_cert.c_str(), _key.c_str());
    g_UniqueId = strdup(_uniqueId.c_str());
    
    pp::VarDictionary ret;
    ret.Set("callbackId", pp::Var(callbackId));
    ret.Set("type", pp::Var("resolve"));
    ret.Set("ret", pp::Var(""));
    PostMessage(ret);
}

void MoonlightInstance::NvHTTPRequest(int32_t /*result*/, int32_t callbackId, std::string url, bool binaryResponse)
{
    char* _url = strdup(url.c_str());
    PHTTP_DATA data = http_create_data();
    int err;
    
    if (data == NULL) {
        pp::VarDictionary ret;
        ret.Set("callbackId", pp::Var(callbackId));
        ret.Set("type", pp::Var("reject"));
        ret.Set("ret", pp::Var("Error when creating data buffer."));
        PostMessage(ret);
        goto clean_data;
    }
    
    err = http_request(_url , data);
    if (err) {
        pp::VarDictionary ret;
        ret.Set("callbackId", pp::Var(callbackId));
        ret.Set("type", pp::Var("reject"));
        ret.Set("ret", pp::Var(err));
        PostMessage(ret);
        goto clean_data;
    }
    
    if (binaryResponse) {
        // Response data will be returned to JS as an ArrayBuffer
        
        pp::VarDictionary ret;
        ret.Set("callbackId", pp::Var(callbackId));
        ret.Set("type", pp::Var("resolve"));
        
        // Construct an array buffer and copy the response data into it
        pp::VarArrayBuffer arrBuf = pp::VarArrayBuffer(data->size);
        memcpy(arrBuf.Map(), data->memory, data->size);
        arrBuf.Unmap();
        
        ret.Set("ret", arrBuf);
        PostMessage(ret);
    } else {
        // Response data will be returned to JS as a UTF-8 string
        
        pp::VarDictionary ret;
        ret.Set("callbackId", pp::Var(callbackId));
        ret.Set("type", pp::Var("resolve"));
        ret.Set("ret", pp::Var(data->memory));
        PostMessage(ret);
    }
    
clean_data:
    http_free_data(data);
    free(_url);
}
