//      SerialDemo
//
//      Illustrates how to interface with the HD Barcode Code Generator
//
//      last updated 04/23/2016


#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include    <direct.h>
#include	<winsock2.h>
#include    <Ws2tcpip.h>

#include    "CodeGenerator.h"

#define     _CRT_SECURE_NO_WARNINGS
#define     _WINSOCK_DEPRECATED_NO_WARNINGS
#pragma     warning (disable: 4996)
#pragma     comment             (lib, "Ws2_32.lib")


#define     BROADCAST_IP        "255.255.255.255"
#define		BUFFER_SIZE         4096
#define     SECRET_PHRASE       "MagicWand"
#define     IDENTIFICATION_TAG  "SerialDemo Test"


WSADATA             Wsa;
SOCKET              Socket;
struct sockaddr_in  Local;
struct sockaddr_in  Remote;
char               *CodeGeneratorIpAddress;


void FindCodeGenerator(void)
{
    int         Try;
    int         Length;
    int         RetCode;
    DWORD       Timeout;
    char        BroadcastPermission;
    BYTE        Response    [BUFFER_SIZE];

    /* Create socket */
    Socket = socket(AF_INET, SOCK_DGRAM, 0);
    if (Socket < 0)
    { 
        printf("socket() failed with error code %d\n", Socket);
        exit(0);
    }

    /* Enable broadcast */
    BroadcastPermission = 1;
    RetCode = setsockopt(Socket, SOL_SOCKET, SO_BROADCAST, &BroadcastPermission, sizeof(BroadcastPermission));
    if (RetCode < 0)
    {
        printf("setsockopt() failed with error code %d\n", RetCode);
        exit(0);
    }

    /* Construct Server structure */
    Local.sin_family      = AF_INET;                 
    Local.sin_addr.s_addr = inet_addr(BROADCAST_IP);
    Local.sin_port        = htons(HDBARCODE_PORT);       

    /* Set timeout */
    Timeout = 1000;
    RetCode = setsockopt(Socket, SOL_SOCKET, SO_RCVTIMEO, (const char *) &Timeout, sizeof(Timeout));

    /* Try 3 times */
    for (Try = 1; Try <= 3 && CodeGeneratorIpAddress == NULL; Try++)
    { 
        /* Send message */
        RetCode = sendto(Socket, UDP_QUERY, strlen(UDP_QUERY), 0, (struct sockaddr *) &Local, sizeof(Local));
        if (RetCode != strlen(UDP_QUERY))
        {
            printf("sendto() failed with error code %d\n", RetCode);
            exit(0);
        }

        /* Listen for response */
        Length  = sizeof(Remote);
        RetCode = recvfrom(Socket, (char *) Response, sizeof(Response), 0, (struct sockaddr *) &Remote, &Length);
        if (RetCode > (int) strlen(UDP_RESPONSE) && strncmp((char *) Response, UDP_RESPONSE, strlen(UDP_RESPONSE)) == 0)
        {
            CodeGeneratorIpAddress = inet_ntoa(Remote.sin_addr);
            printf("Code Generator for %s found at %s\n", Response + strlen(UDP_RESPONSE), CodeGeneratorIpAddress);
            break;
        }
    }

    /* Bail now if failure */
    if (CodeGeneratorIpAddress == NULL)
    {
        printf("Unable to locate Code Generator\n");
        exit(0);
    }

    /* Close socket */
    closesocket(Socket);

    /* Reopen for TCP */
    Socket = socket(AF_INET, SOCK_STREAM, 0);
    if (Socket < 0)
    { 
        printf("socket() failed with error code %d\n", Socket);
        exit(0);
    }

    /* Construct Server structure */
    Local.sin_family      = AF_INET;                 
    Local.sin_addr.s_addr = inet_addr(CodeGeneratorIpAddress);
    Local.sin_port        = htons(HDBARCODE_PORT);       

    /* Set timeout */
    Timeout = 1000;
    RetCode = setsockopt(Socket, SOL_SOCKET, SO_RCVTIMEO, (const char *) &Timeout, sizeof(Timeout));

    /* Connect */
    RetCode = connect(Socket, (struct sockaddr *) &Remote, sizeof(Remote));
    if (RetCode < 0)
    {
        printf("connect() failed with error code %d\n", RetCode);
    }
}



void SendPacket(BYTE *Request, int Bytes)
{
    int		RetCode;
    BYTE   *Packet;
    BYTE   *ReadPtr;
    BYTE   *WritePtr;

    Packet   = (BYTE *) malloc(2 * Bytes + 1);
    ReadPtr  = Request;
    WritePtr = Packet;

    while (Bytes > 0)
    {
        Bytes--;
        switch (*Request)
        {
            case EndPacketByte:
            case EscPacketByte:
                *WritePtr++ = EscPacketByte;
                *WritePtr++ = *Request++ ^ 0xFF;
                break;

            default:
                *WritePtr++ = *Request++;
                break;
        }
    }
    *WritePtr++ = EndPacketByte;

#if 0
    {
        int Scan;

        for (Scan = 0; Scan < WritePtr - Packet; Scan++)
        {
            printf("%02X ", Packet[Scan]);
        }
        printf("\n");
        fflush(stdout);
    }
#endif

    RetCode = send(Socket, (char *) Packet, (int) (WritePtr - Packet), 0);
    if (RetCode < 0)
    {
        printf("send() failed with error code %d\n", RetCode);
        exit(0);
    }

    free(Packet);
}



int ReceivePacket(BYTE *Response, int ResponseSize)
{
    bool    Escape;
    int     RetCode;
    BYTE   *ReadPtr;
    BYTE   *WritePtr;

    Escape = false;
    ReadPtr = Response;
    WritePtr = Response;
    for (;;)
    {
        RetCode = recv(Socket, (char *) ReadPtr, ResponseSize, 0);
        if (RetCode <= 0)
        {
            printf("recv() failed with error code %d\n", RetCode);
            exit(0);
        }
        while (RetCode > 0)
        {
            if (Escape)
            {
                *WritePtr++ = *ReadPtr ^ 0xFF;
                Escape = false;
            }
            else if (*ReadPtr == EndPacketByte)
            {
                return(WritePtr - Response);
            }
            else if (*ReadPtr == EscPacketByte)
            {
                Escape = true;
            }
            else
            {
                *WritePtr++ = *ReadPtr;
            }
            ReadPtr++;
            RetCode--;
        }
    }
}



int AskCodeGenerator(BYTE *Request, int RequestSize, BYTE *Response, int ResponseSize)
{
    int     RetCode;

    /* Send request */
    SendPacket(Request, RequestSize);

    /* Get response */
    RetCode = ReceivePacket(Response, ResponseSize);

    /* Done */
    return(RetCode);
}



void AskCodeGeneratorAndExpectOkay(BYTE *Request, int RequestSize)
{
    int     RetCode;
    BYTE    Response    [BUFFER_SIZE];

    RetCode = AskCodeGenerator(Request, RequestSize, Response, sizeof(Response));
    if (Response[0] != Request[0] || Response[1] != 0x00)
    {
        printf("Code generator did not respond as expected - Received %d bytes (maybe %02X %02X)\n", RetCode, Response[0], Response[1]);
        exit(0);
    }
}



void CreateHDBarcodeToFile(BYTE CommandCode, char *Text, char *Filename)
{
    int         RetCode;
    FILE       *fBmp;
    BYTE        Request     [BUFFER_SIZE];
    BYTE        Response    [BUFFER_SIZE];

    /* Open the file */
    fBmp = fopen(Filename, "wb");
    if (fBmp == NULL)
    {
        printf("fopen(%s) failed\n", Filename);
        exit(0);
    }

    /* Set text if applicable */
    if (Text != NULL)
    {
        sprintf((char *) Request, "%c%s", SetText, Text);
        AskCodeGeneratorAndExpectOkay(Request, strlen((char *) Request));
    }

    /* Generate HD barcode */
    sprintf((char *) Request, "%c", CommandCode);
    AskCodeGeneratorAndExpectOkay(Request, strlen((char *) Request));

    /* Get header */
    sprintf((char *) Request, "%c", RetrieveFirstLine);
    RetCode = AskCodeGenerator(Request, strlen((char *) Request), Response, sizeof(Response));
    if (RetCode != 2 + 62)
    {
        printf("Unexpected responose to %02X command\n", RetrieveFirstLine);
        exit(0);
    }
    fwrite(Response + 2, 1, RetCode - 2, fBmp);

    /* Keep building BMP until no more lines */
    for (;;)
    {
        sprintf((char *) Request, "%c", RetrieveNextLine);
        RetCode = AskCodeGenerator(Request, strlen((char *) Request), Response, sizeof(Response));
        if (RetCode == 2 && Response[1] == NoMoreLines)
        {
            break;
        }
        else if (RetCode <= 2 || Response[1] != NoError)
        {
            printf("Unexpected responose to %02X command\n", RetrieveNextLine);
            exit(0);
        }
        fwrite(Response + 2, 1, RetCode - 2, fBmp);
    }

    /* Close the file */
    fclose(fBmp);
}



void main(void)
{
	int			Index;
    int         RetCode;
    char 		Text	    [10 + 1];
    char        Filename    [80];
    BYTE        Request     [BUFFER_SIZE];

    /* Create folders and ignore any errors */
    _mkdir("Public");
    _mkdir("Private");
    _mkdir("Authorization");

    /* Initialize windows sockets */
    RetCode = WSAStartup(MAKEWORD(2, 2), &Wsa);
    if (RetCode != 0)
    {
        printf("WSAStartup() failed with error code %d\n", RetCode);
        exit(0);
    }
    
	/* Find and connect to code generator */
	FindCodeGenerator();

    /* Build request */
    Request[0] = SetGeometry;   // Command code
    Request[1] = 0;             // Rows             (0 = automatic)
    Request[2] = 0;             // Colums           (0 = automatic)
    Request[3] = 0;             // Error correction (0 = automatic)
    Request[4] = 8;             // Grid size
    Request[5] = 1;             // Style            (1 = lines)
    Request[6] = 3;             // Thickness

    /* Send request and expect no error in response */
    AskCodeGeneratorAndExpectOkay(Request, 1 + 6);

	/* Generate 100 public barcodes */
	for (Index = 1; Index <= 100; Index++)
	{
		/* Generate the text to encode */
		sprintf(Text, "ABCDE%05d", Index);

        /* Generate the file name */
        sprintf(Filename, "Public/%s.bmp", Text);

        /* Build a public bitmap */
        CreateHDBarcodeToFile(GeneratePublic, Text, Filename);

        /* Update display */
        printf("%3d/%3d public barcodes generated\r", Index, 100);
        fflush(stdout);
    }
    printf("\n");

    /* Set secret phrase */
    sprintf((char *) Request, "%c%s", SetSecretPhrase, SECRET_PHRASE);
    AskCodeGeneratorAndExpectOkay(Request, strlen((char *) Request));

    /* Generate 100 private barcodes */
    for (Index = 1; Index <= 100; Index++)
    {
        /* Generate the text to encode */
        sprintf(Text, "XYZZY%05d", Index);

        /* Generate the file name */
        sprintf(Filename, "Private/%s.bmp", Text);

        /* Build a private bitmap */
        CreateHDBarcodeToFile(GenerateCompanyPrivate, Text, Filename);

        /* Update display */
        printf("%3d/%3d private barcodes generated\r", Index, 100);
        fflush(stdout);
    }
    printf("\n");

    /* Set identification tag */
    sprintf((char *) Request, "%c%s", SetIdentificationTag, IDENTIFICATION_TAG);
    AskCodeGeneratorAndExpectOkay(Request, strlen((char *) Request));

    /* Generate authorization barcode */
    CreateHDBarcodeToFile(GenerateTemporaryAuth, NULL, "Authorization/TemporaryAuth.bmp");

    /* Update display */
    printf("1 temporary authorization barcode generated\n");

	/* Done */
	closesocket(Socket);
    WSACleanup();
}
