// This is a personal academic project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <stdio.h>
#include <stdlib.h>

#include "auth.h"

// es imprimible y distinto de ':'
bool is_valid(uint8_t c) {
    return c >= 0x20 && c <= 0x7E && c != 0x3A;
}

void auth_parser_init(struct auth_parser * p) {
    p->state = auth_version;
    p->remaining = 0;
}

enum auth_state auth_parser_feed(struct auth_parser * p, uint8_t b) {
    switch (p->state) {
        case auth_version:
            if (0x01 == b) {
                p->state = auth_ulen;
            } else {
                p->state = auth_error_unsupported_version;
            }
            break;
        case auth_ulen:
            p->remaining = b;
            p->state = auth_uname;

            if (p->remaining == 0) {
                p->state = auth_plen;
            }
            break;
        case auth_uname:
            if (!is_valid(b)) {
                p->state = auth_user_error;
            }
            *(p->username + p->idx++) = (char) b;
            p->remaining--;

            if (p->remaining == 0) {
                *(p->username + p->idx++) = 0;
                if (NULL != p->on_username) {
                    p->on_username(p, p->username);
                }
                p->idx = 0;
                p->state = auth_plen;
            }

            break;
        case auth_plen:
            p->remaining = b;
            p->state = auth_passwd;

            if (p->remaining == 0) {
                p->state = auth_done;
            }
            break;
        case auth_passwd:
            if (!is_valid(b)) {
                p->state = auth_user_error;
            }
            *(p->password + p->idx++) = (char) b;
            p->remaining--;

            if (p->remaining == 0) {
                *(p->password + p->idx++) = 0;
                if (NULL != p->on_password) {
                    p->on_password(p, p->password);
                }
                p->idx = 0;
                p->state = auth_done;
            }
            break;
        case auth_done:
        case auth_error_unsupported_version:
        case auth_user_error:
            break;
        default:
            fprintf(stderr, "Unknown state %d\n", p->state);
            abort();
    }

    return p->state;
}

extern bool auth_is_done(const enum auth_state state, bool * errored) {
    bool ret = true;

    switch (state) {
        case auth_user_error:
        case auth_error_unsupported_version:
            if (0 != errored) {
                *errored = true;
            }
            break;
        case auth_done:
            break;
        default:
            ret = false;
            break;
    }

    return ret;
}

extern const char * auth_error(const struct auth_parser *p) {
    char * ret;
    switch (p->state) {
        case auth_error_unsupported_version:
            ret = "Unsupported version";
            break;
        case auth_user_error:
            ret = "User error";
            break;
        default:
            ret = "";
            break;
    }

    return ret;
}

extern void auth_parser_close(struct auth_parser * p) {}


extern enum auth_state auth_consume(buffer * b, struct auth_parser *p, bool * errored) {
    enum auth_state st = p->state;

    while (buffer_can_read(b)) {
        const uint8_t c = buffer_read(b);
        st = auth_parser_feed(p, c);
        if (auth_is_done(st, errored)) {
            break;
        }
    }

    return st;
}

extern int auth_marshall(buffer * b, uint8_t status) {
    size_t n;
    uint8_t * buff = buffer_write_ptr(b, &n);
    if (n < 2) {
        return -1;
    }

    buff[0] = 0x01;
    buff[1] = status;

    buffer_write_adv(b, 2);
    return 2;
}
