/**************************************************************************//**
* @file
* @brief         Source file for the DDP protocol driver for USB communication
*                with the Atmel USB HID DLL.
*                Compliant with the DDP Protocol v1.1.
* @headerfile    DDProtocol.h
* @details
******************************************************************************
* @author        M. Eng. CCR (Technikreferent des Durstigen Donnerstags)
* @version       1.0
* @date          Q1 2016
******************************************************************************
* @bug
* @warning       Because of the lazyness of the author there are a lot of magic numbers
******************************************************************************
* @copyright     <b>CC-Software</b> - http://www.cc-software.de
*                Open Source Software for http://www.durstiger-donnerstag.de
* @section       License
*                This code is open source. Feel free to use it for non-commercial purposes.
*                However consider:
*                - always mention the origin (CC-Software and Durstiger Donnerstag)
*                - if you altered code, additionally mark it as altered by you
*                - the author doesn't guarantee functionality and is not liable for
*                  any accidents or other occurrences due to the usage of this code
*******************************************************************************/

#include "stdafx.h" 

#include "DDProtocol.h"
#include <windows.h>
#include "AtUsbHid.h"

/**************************************************************************//**
* @addtogroup AlcoLyzer Console
*****************************************************************************/

//if debug is enabled, all received and transmitted packets are printed in detail
//#define DEBUG

// Handle to AtUsbHid.dll
HINSTANCE hLib = NULL;

//VID and PID of BierAlyzer, VID is Atmel-VID
#define VID 0x03EB
#define PID 0x3006

/**************************************************************************//**
* @brief
*    Initializes the DDP protocol driver and loads the DLL.
*    Checks if the BierAlyzer is conencted after opening the DLL.
*
* @return
*    Zero if DLL was loaded correctly and BierAlyzer was found. -1 otherwise.
*****************************************************************************/
char initDDP(void)
{
    printf("<> Lade DLL ...\n");

    hLib = LoadLibrary(AT_USB_HID_DLL);
    if (hLib != NULL) {
        //printf("<> DLL erfolgreich geladen.\n");
    }
    else {
        if (GetLastError() == ERROR_MOD_NOT_FOUND) {
            printf("<> ERROR: Die Datei AtUsbHid.dll wurde nicht gefunden.\n");
            //printf("       Please update the PATH variable.\n");
        }
        else {
            printf("<> ERROR: Fehler beim Oeffnen der DLL.\n");
        }
        return -1;
    }

    if (loadFuncPointers(hLib)) {
        printf("<> DLL erfolgreich geladen.\n");
    }
    else {
        printf("<> ERROR: Fehler beim Laden der DLL.\n");
        return -1;
    }


    if (DYNCALL(findHidDevice)(VID, PID)) {
        printf("<> Der BierAlyzer ist angeschlossen.\n");
    }
    else {
        switch (GetLastError()) {
        case ERROR_USB_DEVICE_NOT_FOUND:
            printf("<> ERROR: Der BierAlyzer wurde nicht gefunden.\n");
            return -1;
        case ERROR_USB_DEVICE_NO_CAPABILITIES:
            printf("<> ERROR: Fehler beim Laden der USB Peripherie.\n");
            return -1;
        default:
            printf("<> ERROR: Kommunikationsfehler mit dem BierAlyzer.\n");
            return -1;
        }
    }
    return 0;
}

/**************************************************************************//**
* @brief
*    Deinitializes the DDP protocol driver and closes the DLL handle.
*
*****************************************************************************/
void deinitDDP(void){
    // Close USB HID device.    
    DYNCALL(closeDevice)();
    printf("<> USB Verbindung zum BierAlyzer wurde geschlossen\n");

    // Wait thread to be close before fee DLL
    Sleep(1000);
    FreeLibrary(hLib);
}

/**************************************************************************//**
* @brief
*    Function to plot the entire information of one DDP packet for debug.
*
* @param packet
*    The packet to print all information from.
*****************************************************************************/
void printPacket(unsigned char *packet){
    //print type
    switch (packet[1]){
    case DDP_PACKET_TYPE_REQUEST: printf("<<Request-");
        break;
    case DDP_PACKET_TYPE_DATA: printf("<<Data-");
        break;
    case DDP_PACKET_TYPE_RESPONSE: printf(">>Response-");
        break;
    case DDP_PACKET_TYPE_ACKNOWLEDGE: printf(">>Ack-");
        break;
    case DDP_PACKET_TYPE_NOT_ACKNOWLEDGE: printf(">>NAck-");
        break;
    }

    int i = 0;

    //print content
    switch (packet[2]){
    case DDP_PACKET_CONTENT_USER:
        printf("User\n");
        printf("------------------------\n");
        if (packet[1] == DDP_PACKET_TYPE_DATA){
            printf("|  Command:   %02X:", packet[3]);
            switch (packet[3]){
                case 0xDD: 
                    printf("Neuen Trinker registrieren\n");
                    break;
                case 0x33:
                    printf("Diesen Trinker loeschen\n");
                    break;
            }
        }
        
        printf("|  HexID:     %02X\n", packet[4]);
        if (packet[1] == DDP_PACKET_TYPE_REQUEST){
            printf("\n");
            break;
        }
        printf("|  RFID-ID:   ");
        for (i = 5; i <= 9; i++){
            printf("%02X ", packet[i]);
        }
        printf("\n|  Name:      ");
        for (; i <= 19; i++){
            printf("%c", packet[i]);
            if (packet[i] == 0){
                i = 19;
            }
        }
        printf("\n|  Bierliste: ");
        for (; i <= 51; i++){
            printf("%i,%i ", packet[i]/10, packet[i]%10);
        }
        printf("\n|  Status:    %02X\n\n", packet[52]);
        break;

    case DDP_PACKET_CONTENT_EVENT:
        printf("Event\n");
        printf("------------------------\n");
        if (packet[1] == DDP_PACKET_TYPE_DATA){
            printf("|  Command:   %02X:", packet[3]);
            switch (packet[3]){
            case 0xDD:
                printf("Neues Event registrieren\n");
                break;
            case 0x33:
                printf("Dieses Event loeschen\n");
                break;
            }
        }
        printf("|  EventID:   %02X\n", packet[4]);
        if (packet[1] == DDP_PACKET_TYPE_REQUEST){
            printf("\n");
            break;
        }
        printf("|  Beschr.:   ");
        for (i=5; i <= 36; i++){
            printf("%c", packet[i]);
            if (packet[i] == 0){
                i = 36;
            }
        }
        printf("\n|  Datum:     %i%i.%i%i.20%i%i", packet[42] >> 4, packet[42] & 0xF, packet[41] >> 4, packet[41] & 0xF, packet[40] >> 4, packet[40] & 0xF);
        printf("\n|  Uhrzeit:   %i%i:%i%i:%i%i", packet[37] >> 4, packet[37] & 0xF, packet[38] >> 4, packet[38] & 0xF, packet[39] >> 4, packet[39] & 0xF);
        printf("\n|  Dauer:     %i", packet[43]);
        printf("\n|  Status:    %02X\n\n", packet[44]);
        break;

    case DDP_PACKET_CONTENT_SETTINGS: 
        printf("Settings\n");
        printf("------------------------\n");
        if (packet[1] == DDP_PACKET_TYPE_DATA){
            printf("|  Command:   %02X:", packet[3]);
            if (packet[3] & 0x01){
                printf("Datum/Uhrzeit speichern, ");
            }
            if (packet[3] & 0x02){
                printf("EventID einstellen ");
            }
            printf("\n");
        }
        if (packet[1] == DDP_PACKET_TYPE_REQUEST){
            printf("\n");
            break;
        }
        printf("|  Datum:     %i%i.%i%i.20%i%i\n", packet[9] >> 4, packet[9] & 0xF, packet[8] >> 4, packet[8] & 0xF, packet[7] >> 4, packet[7] & 0xF);
        printf("|  Tag:       ");
        switch (packet[10]){
            case 1: printf("Montag\n");
                break;
            case 2: printf("Dienstag\n");
                break;
            case 3: printf("Mittwoch\n");
                break;
            case 4: printf("Donnerstag\n");
                break;
            case 5: printf("Freitag\n");
                break;
            case 6: printf("Samstag\n");
                break;
            case 7: printf("Sonntag\n");
                break;
        }
        printf("|  Uhrzeit:   %i%i:%i%i:%i%i\n", packet[4] >> 4, packet[4] & 0xF, packet[5] >> 4, packet[5] & 0xF, packet[6] >> 4, packet[6] & 0xF);
        printf("|  Firmware:  %i.%i ", packet[11] >> 6, (packet[11] >> 2) & 0x03);
        switch (packet[11] & 0x03){
            case 0: printf("experimental\n");
                break;
            case 1: printf("alpha\n");
                break;
            case 2: printf("beta\n");
                break;
            case 3: printf("stable\n");
                break;
        }
        printf("|  EventID:   %i\n", packet[12]);
        printf("|  RFID-ID:   ");
        for (i = 13; i <= 17; i++){
            printf("%02X ", packet[i]);
        }
        printf("\n\n");
        break;

    case DDP_PACKET_CONTENT_PING: 
        printf("Ping\n");
        printf("------------------------\n\n");
        break;
    }
}

/**************************************************************************//**
* @brief
*    Sends a DDP packet via USB.
*
* @param payload
*    The payload of the DDP packet.
*
* @param type
*    The type field of the DDP packet.
*
* @param content
*    The content field of the DDP packet.
*****************************************************************************/
void sendPacket(unsigned char *payload, unsigned char type, unsigned char content)
{
    unsigned char packet[64], i;
    packet[0] = 0xDD;
    packet[1] = type;
    packet[2] = content;

    for (i = 0; i < 61; i++){
        packet[i + 3] = payload[i];
    }

    DYNCALL(writeData)(packet);
#ifdef DEBUG
    printPacket(packet);
#endif
}

/**************************************************************************//**
* @brief
*    Tries to receive a DDP packet from USB.
*
* @param payload
*    A pointer to where the payload of the received packet is to be written to.
*
* @param type
*    A pointer to where the type field of the received packet is to be written to.
*
* @param content
*    A pointer to where the content field of the received packet is to be written to.
*
* @param recv_timeout
*    Timeout in milliseconds. Determines how long the driver tries to receive a packet
*
* @return
*    0 if timeout or non-DDP packet was received, 
*    1 if proper DDP packet was received.
*****************************************************************************/
char receivePacket(unsigned char *payload, unsigned char *type, unsigned char *content, int recv_timeout)
{   
    unsigned char packet[64], i;
    int timeout = recv_timeout;

    while (DYNCALL(readData(packet)) == 0){
        if (!(--timeout)){
            return 0;
        }
        Sleep(1);
    }

    if (packet[0] != 0xDD){
        return 0;
    }

    *type = packet[1];
    *content = packet[2];

    for (i = 0; i < 61; i++){
        payload[i] = packet[i + 3];
    }

#ifdef DEBUG
    printPacket(packet);
#endif

    return 1;
}


