/* This source file is part of the ATMEL AVR-UC3-SoftwareFramework-1.7.0 Release */

/*! \page License
 * Copyright (C) 2009, H&D Wireless AB All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The name of H&D Wireless AB may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY H&D WIRELESS AB ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND
 * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include <top_defs.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>

#include <cmd_wl.h>

#include <wl_cm.h>
#include <console.h>
#include <util.h>
#include <lwip_setup.h>

#include "lwip/netif.h"
#include "lwip/dns.h"
#include "debug.h"
#include "ard_spi.h"

extern void showTTCPstatus();

#define ENABLE_DEBUG_LEVEL 1
#define VERBOSE_DEBUG_LEVEL 2

#define CHECK_ENA_DEBUG(LEVEL, FLAG)  		\
	do{										\
		if (LEVEL >= ENABLE_DEBUG_LEVEL) enableDebug |= FLAG;		\
		else enableDebug &= ~FLAG;			\
		}while(0);

#define CHECK_VERB_DEBUG(LEVEL, FLAG)  		\
	do{										\
		if (LEVEL >= VERBOSE_DEBUG_LEVEL) verboseDebug |= FLAG;		\
		else verboseDebug &= ~FLAG;			\
		}while(0);

#define  _DNS_CMD_

/**
 *
 */
cmd_state_t 
cmd_scan(int argc, char* argv[], void* ctx)
{
        /* Note that the scan results presented will
         * be from the last scan, not this one.
         */
        wl_scan();
        print_network_list();
        return CMD_DONE;
}

cmd_state_t 
cmd_debug_toggle(int argc, char* argv[], void* ctx)
{
        extern uint8_t tr_data_trace;
        if ( argc != 2 ) {
                printk("usage: dt <1|0>\n");
                return CMD_DONE;
        }
        if ( '0' == argv[1][0] ) {
                tr_data_trace = 0;
        }
        if ( '1' == argv[1][0] ) {
                tr_data_trace = 1;
        }
        return CMD_DONE;
}

/**
 *
 */
cmd_state_t 
cmd_connect(int argc, char* argv[], void* ctx)
{
        struct wl_ssid_t ssid;
        char desired_ssid[WL_SSID_MAX_LENGTH];
        int len = 0;
        
        if (argc < 2) {
                printk("usage: connect <ssid>\n");
                return CMD_DONE;
        }
        
        len = join_argv(desired_ssid, sizeof desired_ssid, argc - 1, argv + 1);
        if (0 == len) {
                return CMD_DONE;
        }

        memcpy(ssid.ssid, desired_ssid, len);
        ssid.len = len;
        /* Start connection manager */
        wl_cm_set_network(&ssid, NULL);
        wl_cm_start();
        return CMD_DONE;
}

#ifdef WFE_6_12
cmd_state_t 
cmd_ibss(int argc, char* argv[], void* ctx)
{
        struct wl_ssid_t ssid;
        char desired_ssid[WL_SSID_MAX_LENGTH];
        uint8_t channel;
        enum wl_auth_mode amode;
        int len = 0;
        wl_err_t ret;
        
        if ( 2 == argc && ! strncmp(argv[1], "none", 4) ) {
                printk("Disconnecting\n");
                wl_disconnect();
                wl_cm_stop();
                return CMD_DONE;
        }
        if (argc < 4) {
                printk("usage: ibss <ssid> <channel (1-14)> <wep_enable (1|0)>\n");
                printk("       ibss none\n");
                return CMD_DONE;
        }
        
        channel = atoi(argv[argc - 2]);
        if ( *argv[argc - 1] == '0' ) {
                amode = AUTH_MODE_OPEN_SYSTEM;
        } else {
                amode = AUTH_MODE_SHARED_KEY;
        }
        len = join_argv(desired_ssid, sizeof desired_ssid, argc - 3, argv + 1);
        if (0 == len) {
                return CMD_DONE;
        }
        if ( channel > 14 ) {
                printk("Invalid channel %d\n", (int)channel);
                return CMD_DONE;
        }
        printk("%s : Start with ssid \"%s\", channel %d\n", __func__,
               desired_ssid, channel);
        memcpy(ssid.ssid, desired_ssid, len);
        ssid.len = len;
        /* Stop the connection manager */
        wl_cm_stop();
        
        ret = wl_start_adhoc_net(ssid, channel, amode);
        switch (ret) {
        case WL_BUSY:
                printk("Driver is busy. Already connected?\n");
                break;
        case WL_RETRY:
                printk("Driver is busy. Retry operation\n");
                break;
        case WL_OOM:
                printk("Out of memory\n");
                break;
        case WL_INVALID_ARGS:
                printk("Invalid argument\n");
                break;
        case WL_SUCCESS:
                break;
        default:
                printk("Unknown error %d\n", ret);
                break;
        }
        return CMD_DONE;
}
#endif
/**
 *
 */
cmd_state_t 
cmd_set_ip(int argc, char* argv[], void* ctx)
{
        struct net_cfg *ncfg = ctx;
        struct ip_addr lwip_addr;
        struct netif *nif = ncfg->netif;

        if (argc == 2 && 
            (strncmp(argv[1], "none", 4) == 0)) {
                ncfg->dhcp_enabled = 1;
                
                return CMD_DONE;
        }
        else if (argc != 4 ) {
                printk("usage: ip <ip> <netmask> <gateway-ip>\n");
                printk("  or : ip none (to enable DHCP)\n");
                return CMD_DONE;
        }
        /* IP address */
        lwip_addr = str2ip(argv[1]);
        netif_set_ipaddr(nif, &lwip_addr);
        /* Netmask */
        lwip_addr = str2ip(argv[2]);
        netif_set_netmask(nif, &lwip_addr);
        /* Default Gateway address */
        lwip_addr = str2ip(argv[3]);
        netif_set_gw(nif, &lwip_addr);
        /* Disable DHCP */
        ncfg->dhcp_enabled = 0;

        return CMD_DONE;
}

#ifdef WITH_WPA

/**
 *
 */
cmd_state_t
cmd_delpass(int argc, char* argv[], void* ctx)
{
        const char *usage = "usage: dpass <ssid>\n";
        struct wl_network_t net;
        char desired_ssid[WL_SSID_MAX_LENGTH];
        int len = 0;

        if (argc != 2) {
                printk(usage);
                return CMD_DONE;
        }

        memset(&net, 0, sizeof net);
        memset(net.bssid.octet, 0xFF, sizeof net.bssid.octet);

        len = join_argv(desired_ssid, sizeof desired_ssid, argc - 1, argv + 1);
        if (0 == len) {
                return CMD_DONE;
        }
        memcpy(net.ssid.ssid, desired_ssid, len);
        net.ssid.len = len;
        net.enc_type = ENC_TYPE_AUTO;
        if (wl_clear_passphrase(&net) != WL_SUCCESS) {
                printk("%s : Failed to delete passphrase\n", __func__);
        }

        return CMD_DONE;
}


/**
 *
 */
cmd_state_t
cmd_setpass(int argc, char* argv[], void* ctx)
{
        const char *usage = "usage: wpass <ssid> <passphrase>\n";
        struct wl_network_t net;
        char desired_ssid[WL_SSID_MAX_LENGTH];
        int len = 0;

        if (argc < 3) {
                printk(usage);
                return CMD_DONE;
        }
        /* Not really kosher, an ssid may legally contain 0-bytes but
         * the console interface does not deal with that.
         */
        memset(&net, 0, sizeof net);
        memset(net.bssid.octet, 0xFF, sizeof net.bssid.octet);

        len = join_argv(desired_ssid, sizeof desired_ssid, argc - 2, argv + 1);
        if (0 == len) {
                return CMD_DONE;
        }

        memcpy(net.ssid.ssid, desired_ssid, len);
        net.ssid.len = len;
        net.enc_type = ENC_TYPE_AUTO;
        if (wl_set_passphrase(&net, 
                              argv[argc - 1], 
                              strlen(argv[argc - 1]), 
                              ENC_TYPE_AUTO,
                              AUTH_MODE_AUTO) 
            != WL_SUCCESS) {
                printk("%s : Failed to add passphrase\n", __func__);
        }

        return CMD_DONE;
}
#endif

#ifdef _DNS_CMD_
void foundHost(const char *name, struct ip_addr *ipaddr, void *callback_arg)
{
	printk("Found Host: name=%s ip=0x%x\n", name, ipaddr->addr);
}

/**
 *
 */
cmd_state_t
cmd_gethostbyname(int argc, char* argv[], void* ctx)
{
        const char *usage = "usage: getHost <hostname>\n";
        char hostname[DNS_MAX_NAME_LENGTH];
        struct ip_addr _addr;
        int len = 0;

        if (argc < 2) {
                printk(usage);
                return CMD_DONE;
        }

        len = join_argv(hostname, sizeof hostname, argc - 1, argv + 1);
        if (0 == len) {
                return CMD_DONE;
        }
        err_t err = dns_gethostbyname(hostname, &_addr, foundHost, NULL);
        if (err == ERR_OK)
        {
        	printk("Found Host: name=%s ip=0x%x\n", hostname, _addr.addr);
        }

        return CMD_DONE;
}

/**
 *
 */
cmd_state_t
cmd_setDnsServer(int argc, char* argv[], void* ctx)
{
        const char *usage = "usage: setdns [1-2] aaa.bbb.ccc.ddd\n";
        struct ip_addr dnsIp;
        int dnsIdx = 0;

        if (argc < 3) {
                printk(usage);
                return CMD_DONE;
        }

        /* DNS IDX */
        dnsIdx = atoi(argv[1])-1;
        /* IP address */
        dnsIp = str2ip(argv[2]);

        printk("Set DNS server %d to %s\n", dnsIdx, ip2str(dnsIp));
        dns_setserver(dnsIdx, &dnsIp);
        struct ip_addr addr1 = dns_getserver(0);
        struct ip_addr addr2 = dns_getserver(1);

        printk("==> DNS1: %s\n", ip2str(addr1), addr1);
        printk("==> DNS2: %s\n", ip2str(addr2), addr2);

        return CMD_DONE;
}

/**
 *
 */
cmd_state_t
cmd_startTcpSrv(int argc, char* argv[], void* ctx)
{
        const char *usage = "usage: startTcpSrv <port> <sock>\n";

        int port = 0;
        int sock = 0;

        if (argc < 3) {
                printk(usage);
                return CMD_DONE;
        }

        /* TCP port */
        port = atoi(argv[1]);
        /* socket index */
        sock = atoi(argv[2]);

        printk("Start TCP server on port %d sock %d\n", port, sock);
        if (start_server_tcp(port, sock) != -1)
        {
        	printk("Start TCP server on port %d sock %d FAILED\n", port, sock);
        }
        return CMD_DONE;
}


#endif


/**
 *
 */
cmd_state_t
cmd_status(int argc, char* argv[], void* ctx)
{
        struct net_cfg *ncfg = ctx;
        struct wl_network_t* net;
        uint8_t mac[WL_MAC_ADDR_LENGTH];

        printk("wl_api version " WL_API_RELEASE_NAME "\n");
        /* print mac address */
        if (wl_get_mac_addr(mac) != WL_SUCCESS) {
                printk("failed to get mac address\n");
        }else{
        printk("hw addr: %s\n", mac2str(mac));
        }

        /* print network info */
        net = wl_get_current_network();
        printk("link status: "); 
        if (!net) { 
                printk("down\n");

        }else{
        print_network(net);
        }
        
        /* print ip address */
        if (netif_is_up(netif_default))
                printk("ip addr: %s\n", ip2str(netif_default->ip_addr));
        else
                printk("ip interface is down\n");
        printk("dhcp : ");
        if (ncfg->dhcp_enabled) {
                printk("enabled\n");
        }
        else {
                printk("disabled\n");
        }
        struct ip_addr addr1 = dns_getserver(0);
        struct ip_addr addr2 = dns_getserver(1);

        printk("==> DNS1: %s\n", ip2str(addr1), addr1);
        printk("==> DNS2: %s\n", ip2str(addr2), addr2);

        showTTCPstatus();
        return CMD_DONE;
}

#ifdef ADD_CMDS
/**
 *
 */
cmd_state_t
cmd_power(int argc, char* argv[], void* ctx)
{
        const char *usage = "usage: powersave <on|off>\n";
        
        if (argc < 2) {
                printk(usage);
                return CMD_DONE;
        }
        
        if (!strcmp(argv[1], "on")) {
                if (wl_enable_ps() != WL_SUCCESS) {
                        printk("could not enable power save\n");
                        return CMD_DONE;
                }
                return CMD_DONE;
        }
        else if(!strcmp(argv[1], "off")) {
                if (wl_disable_ps() != WL_SUCCESS) {
                        printk("could not disable power save\n");
                        return CMD_DONE;
                }
                return CMD_DONE;
        }

        printk(usage);
        return CMD_DONE;
}
#endif

#ifdef ADD_CMDS
/**
 *
 */
cmd_state_t
cmd_psconf(int argc, char* argv[], void* ctx)
{
        const char *usage = 
                "usage: psconf <use_ps_poll>      (0/1       default 0)\n" \
                "              <traffic_timeout>  ([ms]      default 10)\n" \
                "              <ps_delay>         ([ms]      default 5000)\n"\
                "              <rx_all_dtim>      (0/1       default 1)\n"\
                "              <listen_interval>  ([beacons] default 20)\n";
        
        uint8_t use_ps_poll;
        uint32_t traffic_timeout;
        uint32_t ps_delay;
        uint8_t rx_all_dtim;
        uint16_t listen_interval;
        
        if (argc < 6) {
                printk(usage);
                return CMD_DONE;
        }
        
        use_ps_poll = atoi(argv[1]);
        traffic_timeout = atoi(argv[2]);
        ps_delay = atoi(argv[3]);
        rx_all_dtim = atoi(argv[4]);
        listen_interval = atoi(argv[5]);

        if (use_ps_poll > 1) {
                printk(usage);
                return CMD_DONE;
        }

        if (rx_all_dtim > 1) {
                printk(usage);
                return CMD_DONE;
        }

        if (wl_conf_ps(use_ps_poll, traffic_timeout, ps_delay,
                       rx_all_dtim, listen_interval) != WL_SUCCESS)
                printk("configuration failed\n");
        
        return CMD_DONE;
}
#endif

/**
 *
 */
cmd_state_t
cmd_setkey(int argc, char* argv[], void* ctx)
{
        int idx, len;
        char key[13];
        struct wl_mac_addr_t bssid;
        const char *usage = "usage: setkey <key_idx (0-3)> <key in hex>\n\t "\
                "or: setkey none\n";

        memset(&bssid.octet, 0xff, sizeof bssid.octet);
        if (argc == 2 && strcmp(argv[1], "none") == 0) {
                printk("Deleting WEP keys\n");
                wl_delete_wep_key(0, &bssid);
                wl_delete_wep_key(1, &bssid);
                wl_delete_wep_key(2, &bssid);
                wl_delete_wep_key(3, &bssid);
                return CMD_DONE;
        }
        if (argc < 3) {
                printk(usage);
                return CMD_DONE;
        }
        idx = atoi(argv[1]);
        len = strlen(argv[2]);
        /* Pass phrase? */
        if ( 5 == len || 13 == len ) {
                strncpy(key, argv[2], len);
        }
        /* Otherwise it's a hex string */
        else {
                len = ascii_to_key(key, argv[2]);
                if (0 == len || idx > 3 || idx < 0 || (idx == 0 && *argv[1] != '0')) {
                        printk(usage);
                        return CMD_DONE;
                }
                if (len != 5 && len != 13) {
                        printk(" WEP key must be 10 (WEP-40) or 26 (WEP-104) digits\n");
                        return CMD_DONE;
                }
        }
        wl_add_wep_key(idx, len, key, &bssid);
        wl_set_default_wep_key(idx);

        return CMD_DONE;
}

cmd_state_t
cmd_debug(int argc, char* argv[], void* ctx)
{
        int level;
        const char *usage = "usage: debug <section> <level>\n\t"\
        		"section: init, cm, spi, tcp , util, warn\n\t"
        		"level  : 0 (off), 1 (on), 2 (verbose)\n\t"
                "or: debug print/on/off\n";

        if (argc == 2 && strcmp(argv[1], "off") == 0) {
                printk("Debug OFF\n");
                enableDebug = DEFAULT_INFO_FLAG;
                verboseDebug = 0;
                return CMD_DONE;
        }else if (argc == 2 && strcmp(argv[1], "print") == 0) {
            printk("Debug enabled: 0x%x\n", enableDebug);
            printk("Verbose enabled: 0x%x\n", verboseDebug);
            return CMD_DONE;
        }else if (argc == 2 && strcmp(argv[1], "on") == 0) {
            printk("Debug ON\n");
            enableDebug = 0xff;
            return CMD_DONE;
        }
        if (argc < 3) {
                printk(usage);
                return CMD_DONE;
        }
        level = atoi(argv[2]);
        if (argc == 3 && strcmp(argv[1], "init") == 0) {
        	CHECK_ENA_DEBUG(level, INFO_INIT_FLAG);
        	CHECK_VERB_DEBUG(level, INFO_INIT_FLAG);
        }else if (argc == 3 && strcmp(argv[1], "spi") == 0) {
        	CHECK_ENA_DEBUG(level, INFO_SPI_FLAG);
        	CHECK_VERB_DEBUG(level, INFO_SPI_FLAG);
        }else if (argc == 3 && strcmp(argv[1], "tcp") == 0) {
        	CHECK_ENA_DEBUG(level, INFO_TCP_FLAG);
        	CHECK_VERB_DEBUG(level, INFO_TCP_FLAG);
        }else if (argc == 3 && strcmp(argv[1], "cm") == 0) {
        	CHECK_ENA_DEBUG(level, INFO_CM_FLAG);
        	CHECK_VERB_DEBUG(level, INFO_CM_FLAG);
        }else if (argc == 3 && strcmp(argv[1], "util") == 0) {
        	CHECK_ENA_DEBUG(level, INFO_UTIL_FLAG);
        	CHECK_VERB_DEBUG(level, INFO_UTIL_FLAG);
        }else if (argc == 3 && strcmp(argv[1], "warn") == 0) {
        	CHECK_ENA_DEBUG(level, INFO_WARN_FLAG);
        	CHECK_VERB_DEBUG(level, INFO_WARN_FLAG);
        }
        return CMD_DONE;
}
