
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "../../include/scramrs.h"

void * globall_malloc_f(size_t sz, size_t align)
{
    return malloc((unsigned long)sz);
}

void * globall_malloc_zr_f(size_t sz, size_t align)
{
    return calloc(1, (unsigned long) sz);
}


void globall_dealloc_f(void * ptr)
{
    free(ptr);
}

void* globall_realloc(void * ptr, size_t new_size)
{
    return realloc(ptr, (unsigned long) new_size);
}

int main(void)
{
    #ifdef CAPI_SCRAM_NOSTD
        set_allocator(globall_malloc_f, globall_malloc_zr_f, globall_dealloc_f, globall_realloc);
    #endif

    const char * usename = "user";
    const char * password = "pencil";
    int32_t res = 0;
    CApiScramRuntimeError * err = NULL;

    const char *client_nonce = "rOprNGfwEbeRWgbNEkqO";
    const char *client_init = "n,,n=user,r=rOprNGfwEbeRWgbNEkqO";
    const char *server_init = "r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096";
    const char *client_final = "c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ=";
    const char *server_final = "v=6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4=";

    ScramKey * scram_key = NULL;

    res = capi_scram_key(NULL, 0, NULL, 0, &scram_key, &err);

    if (res > 0)
    {
        const char * err_text_dest = capi_scram_error_get_descr(err);

        printf("nonce err text: %s", err_text_dest);

        capi_scram_error_free(err);

        return -1;
    }
    else if (res < 0)
    {
        printf("argument %d is invalid", -res);

        return -1;
    }


    

    CApiNonce * scram_nonce = NULL;
    err = NULL;

    res = capi_scram_nonce_base64(&scram_nonce, client_nonce, &err);
    
    if (res > 0)
    {
        const char * err_text_dest = capi_scram_error_get_descr(err);

        printf("nonce err text: %s", err_text_dest);

        capi_scram_error_free(err);

        return -1;
    }
    else if (res < 0)
    {
        printf("argument %d is invalid", -res);

        return -1;
    }
    CApiScramClient * scram_client = NULL;

    // init the instance
    res =
        capi_scram_client_init(RUST_NATIVE, "SCRAM-SHA-256", usename, 
            NULL, password, scram_key, scram_nonce, false, &scram_client, &err);

    if ( res < 0 )
    {
        printf("capi_scram_client_init() argument %d is invalid", res);
        return -1;
    }
    else if ( res > 0 )
    {
        enum ScramErrorCode err_code = capi_scram_error_get_code(err);
        const char * err_text_dest = capi_scram_error_get_descr(err);

        printf("client init error: err code %i, err text: %s", err_code, err_text_dest);

        capi_scram_error_free(err);

        return -1;
    }    

    CApiScramResultClient *init_msg = NULL;

    // create init message
    int32_t res_init = capi_scram_client_init_msg(scram_client, &init_msg);

    const char * init_msg_raw = NULL;

    if (capi_scram_client_result_plain(init_msg, &init_msg_raw) != 0)
    {
        printf("init message failed");

        capi_scram_client_result_free(init_msg);

        return -1;
    }

    // todo send!
    

    if (strcmp(init_msg_raw, client_init) == 0)
    {
        printf("[MATCH]sending init message to host \ngener:%s\nconst:%s\n", init_msg_raw, client_init);
    }
    else
    {
        printf("[UNMATCH]sending init message to host \n%s\n%s\n", init_msg_raw, client_init);
        return -1;
    }

    // deinit the message
    capi_scram_client_result_free(init_msg);
    init_msg = NULL;

    int i = 0;
    bool is_final = false;
    const char * cli_cur_msg = client_final;
    const char * serv_cur_resp = server_init;

    while(i < 2)
    {
        CApiScramResultClient *client_resp_msg = NULL;

        // parse "resp" from server
        res = 
            capi_scram_client_parse_resp_plain(scram_client, serv_cur_resp, &client_resp_msg, &err);

        // next round should recv final
        serv_cur_resp = server_final;
        i++;

        if ( res < 0 )
        {
            printf("aqrgument %d is invalid", res);
            return -1;
        }
        else if ( res > 0 )
        {
            const char * err_text_dest = capi_scram_error_get_descr(err);

            printf("response err text: %s", err_text_dest);
    
            capi_scram_error_free(err);
    
            return -1;
        }

        // set is final to exit
        is_final = capi_scram_client_result_is_final(client_resp_msg);
        if ( is_final == true )
        {
            printf("Final MSG received, AUTH completed!\n");

            capi_scram_client_free(scram_client);

            return 0;
        }

        // todo send

        const char * msg_raw = NULL;

        res = capi_scram_client_result_plain(client_resp_msg, &msg_raw);
        if ( res > 0 )
        {
            printf("init message failed");
    
            capi_scram_client_result_free(client_resp_msg);
    
            return -1;
        }

        if (strcmp(msg_raw, cli_cur_msg) == 0)
        {
            printf("[MATCH]sending message to host \ngener:%s\nconst:%s\n", msg_raw, cli_cur_msg);
        }
        else
        {
            printf("[UNMATCH]sending message to host \n%s\n%s\n", msg_raw, cli_cur_msg);
            return -1;
        }

        // deinit the message
        capi_scram_client_result_free(client_resp_msg);

    }
    
    printf("AUTH failed!\n");

    capi_scram_client_free(scram_client);

    return 0;
}
