#include "ShimClient.h"

#include <Arduino.h>

#include <ctime>
#include <iostream>

#include "trace.h"

extern "C" {
unsigned long millis(void) {
    return (unsigned long)time(0) * 1000UL;
}
}

ShimClient::ShimClient() {
    this->responseBuffer = new Buffer();
    this->expectBuffer = new Buffer();
    this->_allowConnect = true;
    this->_connected = false;
    this->_error = false;
    this->expectAnything = true;
    this->_received = 0;
    this->_expectedPort = 0;
}

int ShimClient::connect(IPAddress ip, uint16_t port) {
    if (this->_allowConnect) {
        this->_connected = true;
    }
    if (this->_expectedPort != 0) {
        if (memcmp(&ip, &this->_expectedIP, 4) != 0) {
            TRACE("ip mismatch\n");
            this->_error = true;
        }
        if (port != this->_expectedPort) {
            TRACE("port mismatch\n");
            this->_error = true;
        }
    }
    return this->_connected;
}
int ShimClient::connect(const char *host, uint16_t port) {
    if (this->_allowConnect) {
        this->_connected = true;
    }
    if (this->_expectedPort != 0) {
        if (strcmp(host, this->_expectedHost) != 0) {
            TRACE("host mismatch\n");
            this->_error = true;
        }
        if (port != this->_expectedPort) {
            TRACE("port mismatch\n");
            this->_error = true;
        }
    }
    return this->_connected;
}
size_t ShimClient::write(uint8_t b) {
    this->_received += 1;
    TRACE(std::hex << (unsigned int)b);
    if (!this->expectAnything) {
        if (this->expectBuffer->available()) {
            uint8_t expected = this->expectBuffer->next();
            if (expected != b) {
                this->_error = true;
                TRACE("!=" << (unsigned int)expected);
            }
        } else {
            this->_error = true;
        }
    }
    TRACE("\n" << std::dec);
    return 1;
}
size_t ShimClient::write(const uint8_t *buf, size_t size) {
    this->_received += size;
    TRACE("[" << std::dec << (unsigned int)(size) << "] ");
    uint16_t i = 0;
    for (; i < size; i++) {
        if (i > 0) {
            TRACE(":");
        }
        TRACE(std::hex << (unsigned int)(buf[i]));

        if (!this->expectAnything) {
            if (this->expectBuffer->available()) {
                uint8_t expected = this->expectBuffer->next();
                if (expected != buf[i]) {
                    this->_error = true;
                    TRACE("!=" << (unsigned int)expected);
                }
            } else {
                this->_error = true;
            }
        }
    }
    TRACE("\n" << std::dec);
    return size;
}
int ShimClient::available() {
    return this->responseBuffer->available();
}
int ShimClient::read() {
    return this->responseBuffer->next();
}
int ShimClient::read(uint8_t *buf, size_t size) {
    uint16_t i = 0;
    for (; i < size; i++) {
        buf[i] = this->read();
    }
    return size;
}
int ShimClient::peek() {
    return 0;
}
void ShimClient::flush() {}
void ShimClient::stop() {
    this->setConnected(false);
}
uint8_t ShimClient::connected() {
    return this->_connected;
}
ShimClient::operator bool() {
    return true;
}

ShimClient *ShimClient::respond(uint8_t *buf, size_t size) {
    this->responseBuffer->add(buf, size);
    return this;
}

ShimClient *ShimClient::expect(uint8_t *buf, size_t size) {
    this->expectAnything = false;
    this->expectBuffer->add(buf, size);
    return this;
}

void ShimClient::setConnected(bool b) {
    this->_connected = b;
}
void ShimClient::setAllowConnect(bool b) {
    this->_allowConnect = b;
}

bool ShimClient::error() {
    return this->_error;
}

uint16_t ShimClient::received() {
    return this->_received;
}

void ShimClient::expectConnect(IPAddress ip, uint16_t port) {
    this->_expectedIP = ip;
    this->_expectedPort = port;
}

void ShimClient::expectConnect(const char *host, uint16_t port) {
    this->_expectedHost = host;
    this->_expectedPort = port;
}
