#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include "usb2200.h"

#define MAX_DI  64

void Show();

int main(int argc, char **argv)
{
	int handle, err;
  WORD pid = USB2255_32;
  BYTE bid = 0x0;
  char input[64];
  DWORD options;

  handle = USB2200_OpenDevice(pid, bid);
	if(handle < 0) {
		printf("open failed, error code %d\n", handle);
		return 0;
	}

  Show();

  while (1){
    printf("\nPress ESC to exit.\n\n");

    if (scanf("%u", &options) != 1) break;
    int c = getchar();
    if (c == 27) break;

    switch (options){
      case 1: {
        WORD current_filter = 0;
        WORD set_filter, verify_filter;
        char *endptr;
        unsigned long temp_val;
        WORD ret;

        printf("\n--- DI Digital Filter Configuration ---\n");

        /* Step 1: Read-First - Just show the current hardware status */
        ret = USB2200_DI_GetFilter(handle, &current_filter);
        if (ret != NO_ERR) {
            printf("Error: Read Filter failed (0x%04X)\n", ret);
            break;
        }
        
        /* Display current value and explain the unit if possible */
        printf(">> Current Filter Value: %u (Unit: 0.1ms)\n", current_filter);

        /* Step 2: Interactive Prompt */
        printf("Enter new value (0-65535, or press Enter to skip): ");
        if (!fgets(input, sizeof(input), stdin) || input[0] == '\n') {
            /* User just wants to read, exit gracefully */
            break;
        }

        /* Step 3: Input Validation */
        temp_val = strtoul(input, &endptr, 0);
        if (input == endptr || temp_val > 65535) {
            printf(">> Invalid input. No change made.\n");
            break;
        }
        set_filter = (WORD)temp_val;

        /* Step 4: Execute Set API */
        ret = USB2200_DI_SetFilter(handle, set_filter);
        if (ret != NO_ERR) {
            printf(">> Set Filter Error: 0x%04X\n", ret);
            break;
        }

        /* Step 5: Final Verification (Read back to confirm) */
        ret = USB2200_DI_GetFilter(handle, &verify_filter);
        if (ret == NO_ERR && verify_filter == set_filter) {
            printf(">> Success: Filter is now updated to %u.\n", verify_filter);
        } else {
            printf(">> Warning: Verification failed (Read: %u).\n", verify_filter);
        }

        break;
      }
      case 2: {
        BYTE current_inv = 0;
        WORD ret;

        /* 1. First, just READ and display current status */
        ret = USB2200_DI_GetInverse(handle, &current_inv);
        if (ret != NO_ERR) {
            printf("Read Inverse failed: 0x%04X\n", ret);
            break;
        }
        
        printf("Current DI Logic: [%s]\n", (current_inv == 1) ? "Inverted" : "Normal");

        /* 2. Ask if user wants to change it */
        printf("Enter new value (0:Normal, 1:Inverse, or press Enter to skip): ");
        if (!fgets(input, sizeof(input), stdin) || input[0] == '\n') {
            /* If user just pressed Enter, exit the case without setting anything */
            break;
        }

        /* 3. If user provided input, proceed to SET */
        BYTE set_val = (BYTE)strtoul(input, NULL, 0);
        if (set_val > 1) {
            printf("Invalid value, skip setting.\n");
            break;
        }

        ret = USB2200_DI_SetInverse(handle, set_val);
        if (ret == NO_ERR) {
            printf(">> Set successfully.\n");
        } else {
            printf(">> Set failed: 0x%04X\n", ret);
        }
        break;
      }
      case 3: {
        BYTE current_edge = 0xFF;
        BYTE set_edge, verify_edge;
        char *endptr;
        unsigned long temp_val;
        WORD ret;

        printf("\n--- DI Counter Edge Trigger Configuration ---\n");

        /* Step 1: Read-First - Just show the current status */
        ret = USB2200_DI_GetCntEdgeTrigger(handle, &current_edge);
        if (ret != NO_ERR) {
            printf("Error: Read Edge Trigger failed (0x%04X)\n", ret);
            break;
        }

        printf(">> Current Edge Trigger: [%u] %s\n", 
                current_edge, (current_edge == 1) ? "Falling Edge" : "Rising Edge");

        /* Step 2: Interactive Prompt */
        printf("Change setting? (0: Rising, 1: Falling, Enter to Skip): ");
        if (!fgets(input, sizeof(input), stdin) || input[0] == '\n') {
            /* User just wants to check, exit case */
            break;
        }

        /* Step 3: Input Validation */
        temp_val = strtoul(input, &endptr, 0);
        if (input == endptr || (*endptr != '\n' && *endptr != '\0')) {
            printf(">> Invalid input format. No change made.\n");
            break;
        }

        if (temp_val > 1) {
            printf(">> Error: Invalid mode (must be 0 or 1).\n");
            break;
        }
        set_edge = (BYTE)temp_val;

        /* Step 4: Execute Set API */
        ret = USB2200_DI_SetCntEdgeTrigger(handle, set_edge);
        if (ret != NO_ERR) {
            printf(">> Set Edge Trigger Error: 0x%04X\n", ret);
            break;
        }

        /* Step 5: Final Verification (Read back to confirm) */
        ret = USB2200_DI_GetCntEdgeTrigger(handle, &verify_edge);
        if (ret == NO_ERR && verify_edge == set_edge) {
            printf(">> Success: Edge trigger updated to [%s]\n",
                   (set_edge == 1) ? "Falling" : "Rising");
        } else {
            printf(">> Warning: Verification mismatch (Read back: %u)\n", verify_edge);
        }

        break;
      }
      case 4: {
        DWORD channels[MAX_DI];
        BYTE o_byDITotal;
        WORD ret;

        /* Get total DI count */
        ret =  USB2200_GetDITotal(handle, &o_byDITotal);
        if (ret != NO_ERR) {
            printf("Error: Get DO total channel failed (0x%04X)\n", ret);
            break;
        }
        
        
        printf("Reading Counter values for all %d channels...\n", o_byDITotal);

        /* * Fixed: Added the third argument 'o_byDITotal' as required by your header 
         */
        ret = USB2200_DI_ReadCounterValue(handle, channels, o_byDITotal); 
        
        if (ret != NO_ERR) {
            printf("Error: USB2200_DI_ReadCounterValue failed (0x%04X)\n", ret);
            break;
        }

        /* Improved output formatting for Vim/Terminal */
        printf("----------------------------------------\n");
        for (int i = 0; i < o_byDITotal; i++) {
            printf("Channel [%2d]: %10u (0x%08X)\n", i, channels[i], channels[i]);
            
            if ((i + 1) % 8 == 0 && (i + 1) < o_byDITotal) {
                printf("----------------\n");
            }
        }
        printf("----------------------------------------\n");
        break;
      }
      case 5: {
        BYTE channel, o_byDITotal;
        DWORD val;
        WORD ret;
        char *endptr;
        unsigned long long temp_ch;

        /* Get total DI count */
        ret =  USB2200_GetDITotal(handle, &o_byDITotal);
        if (ret != NO_ERR) {
            printf("Error: Get DO total channel failed (0x%04X)\n", ret);
            break;
        }
        
        printf("Read Single DI Counter. Enter Channel (0-%d): ", o_byDITotal - 1);
        
        if (!fgets(input, sizeof(input), stdin)) {
            printf("Input error\n");
            break;
        }

        /* 2. Check for empty input (Just pressed Enter) */
        if (input[0] == '\n' || input[0] == '\0') {
            printf("Error: No channel entered.\n");
            break;
        }

        /* 3. Pre-check for negative sign */
        if (input[0] == '-') {
            printf("Error: Negative channel index is not allowed.\n");
            break;
        }

        /* 4. Convert string to 64-bit unsigned to prevent overflow before cast */
        temp_ch = strtoull(input, &endptr, 0);
        
        if (input == endptr) {
            printf("Error: Invalid numeric format.\n");
            break;
        }

        /* 5. Dynamic range check using your API result */
        if (temp_ch >= (unsigned long long)o_byDITotal) {
            printf("Error: Channel %llu is out of range for this device (0-%d).\n", 
                    temp_ch, o_byDITotal - 1);
            break;
        }

        channel = (BYTE)temp_ch;

        /* 6. Execute API and check return code */
        err = USB2200_DI_ReadChannelCounter(handle, channel, &val);
        if (err != NO_ERR) {
            printf("API Error: USB2200_DI_ReadChannelCounter returned 0x%04X\n", err);
            break;
        }

        /* 7. Success output with both Decimal and Hex formats */
        printf(">> [Channel %u] Counter: %u (Hex: 0x%08X)\n", channel, val, val);
        break;
      }
      case 6: {
        QWORD CntClrMask;
        WORD ret;
        char *endptr;

        printf("Enter DI Counter Clear Mask (e.g., 0xFF for first 8 ch, or 0x11): ");
        if (!fgets(input, sizeof(input), stdin)) {
            printf("Input error\n");
            break;
        }

        /* 1. Use strtoull for 64-bit mask safety */
        /* 2. Check if the input is a valid number */
        CntClrMask = (QWORD)strtoull(input, &endptr, 0);

        /* Added parentheses to clear the logic and suppress GCC warning */
        if (input == endptr || (*endptr == '\n' && input[0] == '\n')) {
          printf("Invalid mask value!\n");
          break;
        }

        ret = USB2200_DI_ClearCounter(handle, CntClrMask);
        
        if (ret == NO_ERR) {
            printf("DI Counter Clear Mask [0x%lX] sent successfully.\n", (unsigned long)CntClrMask);
        } else {
            /* Helpful for debugging in terminal/vim */
            printf("Failed to clear DI Counter. Error code: 0x%04X\n", ret);
        }
        break;
      }
      case 7: {
        BYTE channel, o_byDITotal;
        char *endptr;
        unsigned long long temp_ch;
        WORD ret;

        printf("\n--- Clear DI Counter By Channel ---\n");

        /* Get total DI count */
        ret =  USB2200_GetDITotal(handle, &o_byDITotal);
        if (ret != NO_ERR) {
            printf("Error: Get DO total channel failed (0x%04X)\n", ret);
            break;
        }

        printf("Enter Channel ID to clear (0-%d), or press Enter to cancel: ", o_byDITotal - 1);

        if (!fgets(input, sizeof(input), stdin) || input[0] == '\n') {
            /* User cancelled or input error */
            break;
        }

        /* 2. Convert and check numeric format */
        temp_ch = strtoull(input, &endptr, 0);
        if (input == endptr) {
            printf(">> Error: Invalid numeric format.\n");
            break;
        }

        /* 3. Range Check: Prevent clearing a non-existent channel */
        if (temp_ch >= (unsigned long long)o_byDITotal) {
            printf(">> Error: Channel %llu is out of range (Device has %d channels).\n", 
                    temp_ch, o_byDITotal);
            break;
        }
        channel = (BYTE)temp_ch;

        /* 4. Execute the Single Channel Clear API */
        /* Assuming your API is WORD USB2200_DI_ClearCounterChannel(int handle, BYTE channel) */
        ret = USB2200_DI_ClearCounterChannel(handle, channel);

        if (ret == NO_ERR) {
            printf(">> Success: Counter for Channel %u has been cleared.\n", channel);
        } else {
            printf(">> Error: Failed to clear Channel %u. Code: 0x%04X\n", channel, ret);
        }

        break;
      }
      default:
        Show();
        break;
    }
  }

  USB2200_CloseDevice(handle);

	return 0;
}

void Show()
{
  printf("--------------------------------------------------\n");
  printf(" USB-2200 Digital Input Functions.\n");
  printf("--------------------------------------------------\n");
  printf("1. [Get/Set] DI Digital Filter (0.1ms unit)\n");
  printf("2. [Get/Set] DI Inverse Mode (Normal/Inverted)\n");
  printf("3. [Get/Set] DI Counter Edge (Rising/Falling).\n");
  printf("4. [Read   ] DI All Channel Counter Values\n");
  printf("5. [Read   ] DI Single Channel Counter Value.\n");
  printf("6. [Clear  ] DI Counter (By Mask - Multiple)\n");
  printf("7. [Clear  ] DI Counter (By Channel - Single)\n");
  printf("Others number show this list.\n");
}
