
#include <assert.h>
#include <conio.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <winsock2.h>

//#pragma comment(lib, "Ws2_32.lib")

int status;

void help() {
    printf("The Following key combinations are supported:\n");
    printf("1:     run test \n"
           "2:     test binary mode\n"
           "H:     Prints this message\n"
           "X:     Exits\n");
}

void hex_dump(void *hex, int size) {
    int i;
    unsigned char *hex_c = (unsigned char *)hex;  // FIX: use unsigned char to avoid sign issues
    for (i = 0; i < size; i++) {
        printf("%02x ", hex_c[i]);
        if (((i + 1) % 16) == 0)  // FIX: was (i + 1 % 16) — wrong precedence
            printf("\n");
    }
    printf("\n");
}

// FIX: parse ASCII hex string ("8101ff") into raw bytes, returns byte count
static int parse_hex_string(const char *hex_str, unsigned char *out, int out_size) {
    int len = (int)strlen(hex_str);
    if (len % 2 != 0) return -1;
    int count = len / 2;
    if (count > out_size) return -1;
    for (int i = 0; i < count; i++) {
        unsigned int byte;
        if (sscanf(&hex_str[i * 2], "%02x", &byte) != 1) return -1;
        out[i] = (unsigned char)byte;
    }
    return count;
}

// FIX: recv with timeout; returns bytes received or -1 on timeout/error
static int recv_with_timeout(SOCKET soc, char *buffer, int size, int timeout_ms) {
    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(soc, &fds);
    struct timeval tv;
    tv.tv_sec  = timeout_ms / 1000;
    tv.tv_usec = (timeout_ms % 1000) * 1000;
    int sel = select(0, &fds, NULL, NULL, &tv);
    if (sel <= 0) return -1;  // timeout or error
    return recv(soc, buffer, size, 0);
}

void visca_send_binary_version(SOCKET soc, char *receive, int size, int do_dump) {
    const unsigned char queryVersionBin[] = {0x81, 0x09, 0x00, 0x02, 0xff};
    unsigned char msg[128] = {};
    int payload_len = (int)sizeof(queryVersionBin);
    int total_len = payload_len + 5;  // 4-byte header + payload + 1-byte checksum

    msg[0] = 0xA5;
    msg[1] = 0xde;
    msg[2] = 0;                          // checksum placeholder
    msg[3] = (unsigned char)(payload_len + 1);  // length field
    memcpy(&msg[4], queryVersionBin, payload_len);
    msg[total_len - 1] = 0;  // checksum byte slot

    unsigned char sum = 0;
    for (int i = 0; i < total_len; i++)
        sum += msg[i];
    msg[2] = sum;

    send(soc, (const char *)msg, total_len, 0);

    char buffer[256];
    // FIX: use timeout instead of infinite spin
    int ret = recv_with_timeout(soc, buffer, sizeof(buffer), 3000);
    if (ret < 4) {
        printf("visca_send_binary_version: no/short response (%d)\n", ret);
        return;
    }
    unsigned char payload_size = (unsigned char)buffer[3];  // FIX: cast to unsigned
    if (payload_size > 0 && payload_size <= (unsigned char)(ret - 4)) {
        memcpy(receive, &buffer[4], payload_size);
        receive[payload_size] = 0;
        if (do_dump)
            hex_dump(receive, payload_size);
    }
}

// FIX: cmd is now an ASCII hex string; we parse it to binary before sending
void visca_send(FILE *fp, SOCKET soc, const char *cmd_hex, char *receive, int size, int do_dump) {
    unsigned char payload[64];
    int payload_len = strlen(cmd_hex);
    if (payload_len <= 0) {
        printf("visca_send: invalid hex string: %s\n", cmd_hex);
        return;
    }

    unsigned char msg[128] = {};
    int total_len = payload_len + 5;

    msg[0] = 0xA5;
    msg[1] = 0xde;
    msg[2] = 0;                                   // checksum placeholder
    msg[3] = (unsigned char)(payload_len );    // FIX: use binary length, not strlen
    memcpy(&msg[4], cmd_hex, payload_len);
    msg[total_len - 1] = 0;

    unsigned char sum = 0;
    for (int i = 0; i < total_len; i++)
        sum += msg[i];
    msg[2] = sum;

    send(soc, (const char *)msg, total_len, 0);

    char buffer[256];
    // FIX: use timeout instead of infinite spin
    int ret = recv_with_timeout(soc, buffer, sizeof(buffer), 3000);
    if (ret < 4) {
        printf("visca_send: no/short response for cmd %s (%d)\n", cmd_hex, ret);
        return;
    }
    unsigned char payload_size = (unsigned char)buffer[3];  // FIX: unsigned
    if (payload_size > 4) {
      //  printf("-> ");
        // print printable chars; rest as hex
        fprintf(fp,"[");
        for (int i = 0; i < payload_size; i++) {
            unsigned char c = (unsigned char)buffer[4 + i];
            if (c >= 0x20 && c < 0x7f)
                fprintf(fp,"%c", c);
            //else
            //    printf("\\x%02x", c);
        }
        printf("\n");
    } else if (do_dump && payload_size > 0) {
      //  hex_dump(&buffer[4], payload_size);
    }
    if (payload_size > 0 && payload_size <= (unsigned char)(ret - 4)) {
        memcpy(receive, &buffer[4], payload_size);
        receive[payload_size] = 0;
    }
    fprintf(fp,"]\n");
}

unsigned short getUInt16(unsigned char *buffer) {  // FIX: unsigned char*
    return ((unsigned short)buffer[1] << 8) | buffer[0];
}

unsigned int getUInt32(unsigned char *buffer) {  // FIX: unsigned char*
    unsigned short lower = ((unsigned short)buffer[1] << 8) | buffer[0];
    unsigned short upper = ((unsigned short)buffer[3] << 8) | buffer[2];
    return ((unsigned int)upper << 16) | lower;
}

void getSystemInfo(SOCKET soc) {
    unsigned char header[4] = {0xA5, 113,0, 0};
    send(soc, (const char *)header, 4, 0);

    unsigned char buffer[256] = {};
    int ret = recv_with_timeout(soc, (char *)buffer, sizeof(buffer), 3000);  // FIX: timeout
    if (ret < 4 || (unsigned char)buffer[3] != (unsigned char)(ret - 4)) {
        printf("SystemInfo: unexpected response len=%d payload=%d\n", ret, (int)buffer[3]);
        hex_dump(buffer, ret > 0 ? ret : 0);
        return;
    }
    printf("SystemInfo:\n");
    printf("Board version : %s\n",  (char *)&buffer[4]);
    printf("CPU type      : %s\n",  (char *)&buffer[24]);
    printf("CPU number    : %d\n",  (int)buffer[34]);
    printf("CPU speed     : %d\n",  (int)getUInt16(&buffer[35]));
    printf("Date          : %s\n",  (char *)&buffer[37]);
    printf("UDVP          : %s\n",  (char *)&buffer[67]);
    printf("LDVC          : %u\n",  getUInt32(&buffer[77]));
    printf("FPGA          : %d\n",  (int)buffer[81]);
    printf("IP Addr       : %s\n",  (char *)&buffer[82]);
    printf("MAC Addr      : %s\n",  (char *)&buffer[102]);
    printf("CAM If        : %d\n",  (int)buffer[122]);
    printf("BoardName     : %s\n",  (char *)&buffer[123]);
}

int main() {
    int ch = 0;
    SOCKET s;
    FILE *fp=0;
    char buf[255];
    char *fn="/sonytest.txt";

    const char *ip = "192.168.0.25";
    int portno     = 3100;

    struct sockaddr_in serv_addr;
    WSADATA wsa;

    fp = fopen(fn,"w");
    if(fp ==0){
        printf("fail to oepn %s \n\r",fn);
        return 0;
    }
    fprintf(fp,"Initialising Winsock...\n");
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
        printf("WSAStartup failed: %d\n", WSAGetLastError());
        return -1;
    }

    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s == INVALID_SOCKET) {
        printf("error opening socket: %d\n", WSAGetLastError());
        return -2;
    }

    fprintf(fp,"connecting to %s:%d\n", ip, portno);

    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family      = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(ip);
    serv_addr.sin_port        = htons(portno);

    if (connect(s, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        printf("ERROR connecting\n");
        closesocket(s);
        WSACleanup();
        return -3;
    }
    printf("connected!\n");

    help();
    getSystemInfo(s);

    while (1) {
      //  ch = _getch();

        if (1) {
            // VISCA ZoomDirect and block inquiry — ASCII hex strings parsed to binary
            const char zoomDirectNear[] = "8101044700000004ff";
            const char zoomDirectFar[]  = "8101044703000000ff";
            const char block_inq0[]     = "81097e7e00ff";
            const char tepm_inq0[]      = "81090468ff";


            for (int l = 0; l < 100;l++ ) {
                fprintf(fp,"session -------------------(%d)---------------------------\n",l );
                fprintf(fp,"send ZoomDirect FAR %s\n", zoomDirectFar);
                visca_send(fp,s, zoomDirectFar, buf, sizeof(buf), 1);
                for (int i = 0; i < 5; i++) {
                    fprintf(fp,"send block inq1\n", block_inq0);
                    visca_send(fp,s, block_inq0, buf, sizeof(buf), 1);
                    fprintf(fp,"send temp inq\n", tepm_inq0);
                    visca_send(fp,s, tepm_inq0, buf, sizeof(buf), 1);

                }
                fprintf(fp,"send ZoomDirect Near %s\n", zoomDirectNear);
                visca_send(fp,s, zoomDirectNear, buf, sizeof(buf), 1);
                for (int i = 0; i < 10; i++) {
                    fprintf(fp,"send block inq2\n", block_inq0);
                    visca_send(fp,s, block_inq0, buf, sizeof(buf), 1);
                    fprintf(fp,"send temp inq\n", tepm_inq0);
                    visca_send(fp,s, tepm_inq0, buf, sizeof(buf), 1);
                }
            }
        }

        fprintf(fp,"finish\n");

        fclose(fp);
        fp =0;
        break;

    }

    closesocket(s);
    WSACleanup();
    return 0;
}
