#include "ltr/include/ltr030api.h"
#include "ltr/include/ltrapi.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "getopt.h"
#ifdef _WIN32
#include "WinSock2.h"
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif


#define OPT_HELP       'h'
#define OPT_VERBOSE    'v'
#define OPT_CRATE_SN   'c'
#define OPT_INTERFACE  'i'
#define OPT_IP_ADDR    'n'
#define OPT_IP_MASK    'm'
#define OPT_GATE       'g'

#define OPT_SRV_ADDR    0x1000

#define ERR_INVALID_USAGE           -1


static const struct option f_long_opt[] =
{
    {"help",         no_argument,       0, OPT_HELP},
    {"csn",          required_argument, 0, OPT_CRATE_SN},
    {"iface",        required_argument, 0, OPT_INTERFACE},
    {"intf",         required_argument, 0, OPT_INTERFACE},
    {"ip-addr",      required_argument, 0, OPT_IP_ADDR},
    {"ip-mask",      required_argument, 0, OPT_IP_MASK},
    {"gate",         required_argument, 0, OPT_GATE},
    {"srv-addr",     required_argument, 0, OPT_SRV_ADDR},
    {0,0,0,0}
};

static const char* f_opt_str = "hc:i:n:m:g:";


#define CMD_READ 0
#define CMD_WRITE 1


typedef struct
{
    const char *csn;
    int iface;
    int cmd;
    int ip_addr_present;
    int ip_mask_present;
    int gate_present;
    DWORD ip_addr;
    DWORD ip_mask;
    DWORD gate;
    DWORD srv_addr;
} t_ltrcfg_state;




static const char* f_usage_descr = \
"\nUsage: ltreu-config [OPTIONS]\n\n" \
" ltreu-config is command line utility for change LTR-EU interface settings.\n"
"   For settings setup you need connect LTR-EU by USB-interface, make sure\n"
"   that ltrd IS running and run ltreu-config with required options.\n"
"   After update you need remove supply voltage and apply it again.\n"
" By default ltreu-config connect to the first crate with usb interface, but\n"
"   you can specify crate serial number with option --csn=<serial> if you have\n"
"   more than one crate.\n\n"
" For set usb interface run:\n"
"     ltreu-config --intf=usb\n"
" For set TCP/IP interface run:\n"
"     ltreu-config --intf=eth --ip-addr=<addr> --ip-mask=<mask> --gate=<gate>\n\n"
" You can run ltreu-config without -i,--intf,--iface option to read current\n"
"   crate settings.\n";

static const char* f_options_descr =
"Options:\n" \
" -c, --csn=crate-serial  - Crate serial number. If not specified, utility\n"
"                            open first LTR-EU crate with USB interface\n"
" -h, --help              - Print this help and exit\n"
" -i, --iface=interface   - Specify crate interface you wish to set.\n"
"                            Valid interfaces are:\n"
"                            - usb - USB interface\n"
"                            - eth - Ethernet (TCP/IP) interface\n"
"     --intf=interface    - Alternative name for --iface option\n"
"     --srv-addr=addr     - ltrd ip-address if you want to connect to remote\n"
"                            instance of ltrd\n\n"
"Options for specify parameters for eth interface:\n"
" -g, --gate=addr         - Gateway IP-addres\n"
" -m, --ip-mask=mask      - Crate IP network mask\n"
" -n, --ip-addr=addr      - Crate IP-addres\n";


static void  f_print_usage(void)
{
    unsigned int i;
    fprintf(stdout, "%s", f_usage_descr);
    fprintf(stdout,"\n");
    fprintf(stdout, "%s", f_options_descr);
}

static int f_stricmp (const char *p1, const char *p2)
{
    unsigned char *s1 = (unsigned char *) p1;
    unsigned char *s2 = (unsigned char *) p2;
    unsigned char c1, c2;

    do
    {
        c1 = (unsigned char) toupper((int)*s1++);
        c2 = (unsigned char) toupper((int)*s2++);
    }
    while ((c1 == c2) && (c1 != '\0'));

    return c1 - c2;
}


static int f_parse_options(t_ltrcfg_state* st, int argc, char **argv, int* out)
{
    int err = 0;
    int opt = 0;


    *out = 0;

    memset(st, 0, sizeof(*st));
    st->srv_addr = SADDR_DEFAULT;

    /************************** разбор опций *****************************/
    while ((opt!=-1) && !err && !*out)
    {
        opt = getopt_long(argc, argv, f_opt_str,
                          f_long_opt, 0);
        switch (opt)
        {
            case -1:
                break;
            case OPT_HELP:
                f_print_usage();
                *out = 1;
                break;
            case OPT_CRATE_SN:
                st->csn = optarg;
                break;
            case OPT_INTERFACE:
                st->cmd = CMD_WRITE;
                if (!f_stricmp("usb", optarg))
                    st->iface = CRATE_IFACE_USB;
                else if (!f_stricmp("eth", optarg))
                    st->iface = CRATE_IFACE_TCPIP;
                else
                {
                    err = ERR_INVALID_USAGE;
                    fprintf(stderr, "Invalid interface specified!\n");
                }
                break;
            case OPT_IP_ADDR:
                st->ip_addr_present = 1;
                st->ip_addr = ntohl(inet_addr(optarg));
                if ((st->ip_addr==INADDR_NONE) || (st->ip_addr==INADDR_ANY))
                {
                    fprintf(stderr, "Invalid IP-Address!\n");
                    err = ERR_INVALID_USAGE;
                }
                break;
            case OPT_IP_MASK:
                st->ip_mask_present = 1;
                st->ip_mask = ntohl(inet_addr(optarg));
                if ((st->ip_mask==INADDR_NONE) || (st->ip_mask==INADDR_ANY))
                {
                    fprintf(stderr, "Invalid IP-mask!\n");
                    err = ERR_INVALID_USAGE;
                }
                break;
            case OPT_GATE:
                st->gate_present = 1;
                st->gate = ntohl(inet_addr(optarg));
                if ((st->gate==INADDR_NONE) || (st->gate==INADDR_ANY))
                {
                    fprintf(stderr, "Invalid gateway address!\n");
                    err = ERR_INVALID_USAGE;
                }
                break;
            case OPT_SRV_ADDR:
                st->srv_addr = inet_addr(optarg);
                if ((st->srv_addr==INADDR_NONE) || (st->srv_addr==INADDR_ANY))
                {
                    fprintf(stderr, "Invalid ltrd IP-address!\n");
                    err = ERR_INVALID_USAGE;
                }
                break;
            default:
                break;
        }
    }

    if (!err)
    {
        if (st->iface==CRATE_IFACE_TCPIP)
        {
            if (!st->ip_addr_present)
            {
                fprintf(stderr, "IP-address is not specified!\n");
                err = ERR_INVALID_USAGE;
            }
            if (!st->ip_mask_present)
            {
                fprintf(stderr, "IP-mask is not specified!\n");
                err = ERR_INVALID_USAGE;
            }
            if (!st->gate_present)
            {
                fprintf(stderr, "Gate is not specified!\n");
                err = ERR_INVALID_USAGE;
            }
        }
    }

    return err;
}

int main(int argc, char** argv)
{
    t_ltrcfg_state st;
    int out, err;

    memset(&st, 0, sizeof(st));
    /* разбор опций */
    err = f_parse_options(&st, argc, argv, &out);

    if (!err && !out)
    {
        TLTR030 hltr;
        LTR030_Init(&hltr);
        err = LTR030_Open(&hltr, st.srv_addr, SPORT_DEFAULT, CRATE_IFACE_USB, st.csn);
        if (err!=LTR_OK)
        {
            fprintf(stderr, "Cannot open LTR-EU crate. Error %d: %s\n",
                    err, LTR030_GetErrorString(err));
        }
        else
        {
            printf("Crate was opened successfully (serial = %s)\n", hltr.Channel.csn);
            if (st.cmd == CMD_WRITE)
            {
                if (st.iface == CRATE_IFACE_USB)
                {
                    err = LTR030_SetInterfaceUsb(&hltr);
                }
                else
                {
                    err = LTR030_SetInterfaceTcp(&hltr, st.ip_addr, st.ip_mask, st.gate);
                }

                if (err==LTR_OK)
                {
                    printf("Crate settings was written successfully!\n");
                }
                else
                {
                    fprintf(stderr, "Can not write crate settings! Error %d: %s\n",
                            err, LTR030_GetErrorString(err));
                }
            }
            else
            {
                TLTR030_CONFIG cfg;
                cfg.Size = sizeof(cfg);
                err = LTR030_GetConfig(&hltr, &cfg);
                if (err==LTR_OK)
                {
                    printf("Crate configuration:\n");
                    printf("  Interface  : ");
                    if (cfg.Interface==CRATE_IFACE_USB)
                    {
                        printf("USB\n");
                    }
                    else if (cfg.Interface==CRATE_IFACE_TCPIP)
                    {
                        printf("Ethernet (TCP/IP)\n");
                    }
                    else
                    {
                        printf("Unknown\n");
                    }

                    printf("  IP-Addr    : %d.%d.%d.%d\n",
                           (cfg.IpAddr >> 24) & 0xFF, (cfg.IpAddr >> 16) & 0xFF,
                           (cfg.IpAddr >> 8) & 0xFF, cfg.IpAddr & 0xFF);
                    printf("  IP-Mask    : %d.%d.%d.%d\n",
                           (cfg.IpMask >> 24) & 0xFF, (cfg.IpMask >> 16) & 0xFF,
                           (cfg.IpMask >> 8) & 0xFF, cfg.IpMask & 0xFF);
                    printf("  IP-Addr    : %d.%d.%d.%d\n",
                           (cfg.Gateway >> 24) & 0xFF, (cfg.Gateway >> 16) & 0xFF,
                           (cfg.Gateway >> 8) & 0xFF, cfg.Gateway & 0xFF);
                }
                else
                {
                    fprintf(stderr, "Can not read crate settings! Error %d: %s\n",
                            err, LTR030_GetErrorString(err));
                }
            }
            LTR030_Close(&hltr);
        }
    }
    return err;
}
