#include "unity.h"
#include "UsbDataHandler.h"
#include "pb_encode.h"
#include "firmware.pb.h"


#define START_PACKAES 10
#define START_SIZE 123456
#define START_CRC 0xffff
#define START_DEVICE 0x04
#define START_NAME "slave0x04.bin"

#define PACK_COUTNER 3
#define PACK_CRC 0xabcd
#define PACK_DEVICE 0x04
#define PACK_DATA_SIZE 50

#define ACK_COUTNER 43
#define ACK_CRC 0xabcdef
#define ACK_DEVICE 0x9

#define DONE_CRC 0xffffffff
#define DONE_DEVICE 0xf
#define DONE_SIZE 1024 * 65

uint16_t StartCounter = 0;
uint16_t PackageCounter = 0;
uint16_t AckCounter = 0;
uint16_t DoneCounter = 0;


void transmit_package(uint16_t id, const pb_msgdesc_t *fields , const void* msg){
    UsbDataPacket buffer = {0};
    buffer.head.type = id;    
    pb_ostream_t ostream = pb_ostream_from_buffer(buffer.data, sizeof(buffer.data));
    pb_encode(&ostream, fields, msg);
    buffer.head.length= ostream.bytes_written;   
    
    buffer.head.check = UsbDataPacket_head_sum(&buffer);

    UsbDataHandler_RxCallback((uint8_t*)(&buffer), buffer.head.length+sizeof(UsbDataPacketHead));
}

void test_UsbDataHandler(void) {
    // Set up the module
    UsbDataHandler_Start();

    // Call the runner with no messages this should do nothing
    UsbDataHandler_Runner();

    FirmwareStart start ={ 
        .name=START_NAME,
        .packages=START_PACKAES,
        .size=START_SIZE,
        .crc_fw=START_CRC,
        .device_id=START_DEVICE,
    };
    transmit_package(UsbPackageType_FIRMWARESTART, FirmwareStart_fields, &start);

    // Call the runner with start message in the queue
    UsbDataHandler_Runner();
    TEST_ASSERT_EQUAL(StartCounter,1);


    //Put 2 packages in the buffer an parse them
    transmit_package(UsbPackageType_FIRMWARESTART, FirmwareStart_fields, &start);
    transmit_package(UsbPackageType_FIRMWARESTART, FirmwareStart_fields, &start);
    UsbDataHandler_Runner();
    UsbDataHandler_Runner();
    TEST_ASSERT_EQUAL(StartCounter,3);



    FirmwarePackage pack = {
        .counter =PACK_COUTNER ,
        .crc_pac = PACK_CRC,
        .device_id= PACK_DEVICE,
        .data={.size = PACK_DATA_SIZE,
        .bytes={0}
        },
    };
    transmit_package(UsbPackageType_FIRMWAREPACKAGE, FirmwarePackage_fields, &pack);
    UsbDataHandler_Runner();
    TEST_ASSERT_EQUAL(PackageCounter,1);

    FirmwarePackageAck ack = {
        .ack =true,
        .counter = ACK_COUTNER,
        .crc_pac = ACK_CRC,
        .device_id = ACK_DEVICE,
    };
    transmit_package(UsbPackageType_FIRMWAREPACKAGEACK, FirmwarePackageAck_fields, &ack);
    UsbDataHandler_Runner();
    TEST_ASSERT_EQUAL(PackageCounter,1);


    FirmwareDone done = {
        .crc_fw = DONE_CRC,
        .device_id = DONE_DEVICE,
        .size = DONE_SIZE,
    };

    transmit_package(UsbPackageType_FIRMWAREDONE, FirmwareDone_fields, &done);
    UsbDataHandler_Runner();
    TEST_ASSERT_EQUAL(DoneCounter,1);

}


void DataClbk_FirmwareStart(void * msg, uint32_t length) {
    FirmwareStart start;
    memcpy(&start,msg,sizeof(start));
    TEST_ASSERT_EQUAL(sizeof(FirmwareStart), length);
    TEST_ASSERT_EQUAL(START_SIZE, start.size);
    TEST_ASSERT_EQUAL(START_PACKAES, start.packages);
    TEST_ASSERT_EQUAL(START_CRC, start.crc_fw);
    TEST_ASSERT_EQUAL(START_DEVICE, start.device_id);
    TEST_ASSERT_EQUAL_STRING(START_NAME,start.name);
    StartCounter++;
}
void DataClbk_FirmwarePackage(void * msg,  uint32_t length) {
    FirmwarePackage pack;
    memcpy(&pack,msg,sizeof(pack));
    TEST_ASSERT_EQUAL(sizeof(FirmwarePackage), length);
    TEST_ASSERT_EQUAL(PACK_COUTNER, pack.counter);
    TEST_ASSERT_EQUAL(PACK_CRC, pack.crc_pac);
    TEST_ASSERT_EQUAL(PACK_DEVICE, pack.device_id);
    TEST_ASSERT_EQUAL(PACK_DATA_SIZE, pack.data.size);
    PackageCounter++;
}
void DataClbk_FirmwarePackageAck(void * msg,  uint32_t length) {
    FirmwarePackageAck ack;
    memcpy(&ack,msg,sizeof(ack));
    TEST_ASSERT_EQUAL(sizeof(FirmwarePackageAck), length);
    TEST_ASSERT(ack.ack);
    TEST_ASSERT_EQUAL(ACK_COUTNER, ack.counter); 
    TEST_ASSERT_EQUAL(ACK_CRC, ack.crc_pac); 
    TEST_ASSERT_EQUAL(ACK_DEVICE, ack.device_id);

    AckCounter++;
}
void DataClbk_FirmwareDone(void * msg,  uint32_t length) {
    FirmwareDone done;
    memcpy(&done,msg,sizeof(done));
    TEST_ASSERT_EQUAL(sizeof(FirmwareDone), length);
    TEST_ASSERT_EQUAL(DONE_CRC,done.crc_fw);
    TEST_ASSERT_EQUAL(DONE_DEVICE,done.device_id);
    TEST_ASSERT_EQUAL(DONE_SIZE,done.size);
    DoneCounter++;
}