#include <stdlib.h>
#include <xdc/std.h>
#include <xdc/runtime/System.h>
#include <ti/sysbios/knl/Clock.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/ndk/inc/netmain.h>
#include "icss_iSwitchDrv.h"

#include "user_acd_ndk.h"

static HANDLE hCfg;
static HANDLE cfgHostname;
static HANDLE cfgAddress;
static HANDLE cfgGateway;

extern void llTimerTick();

extern char *ti_ndk_config_Global_HostName;
/* Static IP Address settings */
extern char *LocalIPAddr;
extern char *LocalIPMask;
extern char *GatewayIP;
extern char *DomainName;

extern unsigned int acdARPFlag;
extern int acdIPAssign;
extern int acdCablePull;
int startupMode =EIP_STARTUP_STATICIP;

extern Void ti_ndk_config_tcp_init(HANDLE hCfg);

/**DNS Server*/
char *DNSServer   = "0.0.0.0";
/**DHCP Options*/
UINT8 DHCP_OPTIONS[] = { DHCPOPT_SERVER_IDENTIFIER, DHCPOPT_ROUTER,DHCPOPT_SUBNET_MASK };
static void   ServiceReport( uint Item, uint Status, uint Report, HANDLE hCfgEntry );
HANDLE dhcphandle;
unsigned int startUPType=EIP_STARTUP_STATICIP;
/* ========================================================================== */
/*                          Function Definitions                              */
/* ========================================================================== */


/**
 *  \name DHCPReset
 *  \brief The function used to reset DHCP client
 *  \param IfIdx
 *  \param fOwnTask
 *  \return none
 *
 */
void DHCPReset( uint IfIdx, uint fOwnTask )
{
    CI_SERVICE_DHCPC dhcpc;
    HANDLE h;
    int    rc,tmp;
    uint   idx;

    // If we were called from a newly created task thread, allow
    // the entity that created us to complete
    if( fOwnTask )
        TaskSleep(500);

    // Find DHCP on the supplied interface
    for(idx=1; ; idx++)
    {
        // Find a DHCP entry
        rc = CfgGetEntry( 0, CFGTAG_SERVICE, CFGITEM_SERVICE_DHCPCLIENT,
                          idx, &h );
        if( rc != 1 )
            goto RESET_EXIT;

        // Get DHCP entry data
        tmp = sizeof(dhcpc);
        rc = CfgEntryGetData( h, &tmp, (UINT8 *)&dhcpc );

        // If not the right entry, continue
        if( (rc<=0) || dhcpc.cisargs.IfIdx != IfIdx )
        {
            CfgEntryDeRef(h);
            h = 0;
            continue;
        }

        // This is the entry we want!

        // Remove the current DHCP service
        CfgRemoveEntry( 0, h );

        // Specify DHCP Service on specified IF
        bzero( &dhcpc, sizeof(dhcpc) );
        dhcpc.cisargs.Mode   = CIS_FLG_IFIDXVALID;
        dhcpc.cisargs.IfIdx  = IfIdx;
        dhcpc.cisargs.pCbSrv = &ServiceReport;
        CfgAddEntry( 0, CFGTAG_SERVICE, CFGITEM_SERVICE_DHCPCLIENT, 0,
                     sizeof(dhcpc), (UINT8 *)&dhcpc, 0 );
        break;
    }

RESET_EXIT:
    // If we are a function, return, otherwise, call TaskExit()
    if( fOwnTask )
        TaskExit();
}



void CheckDHCPOptions()
{
	 CI_ROUTE RT;
    char IPString[16];
    IPN  IPAddr;
    int  i, rc;

    // Now scan for DHCPOPT_ROUTER via the configuration
    for(i=1;;i++)
    {
        // Try and get a DNS server
        rc = CfgGetImmediate( 0, CFGTAG_SYSINFO, DHCPOPT_ROUTER,
                              i, 4, (UINT8 *)&IPAddr );
        if( rc != 4 )
            break;

        // We got something

        // Convert IP to a string:
        NtIPN2Str( IPAddr, IPString );

		bzero(&RT, sizeof(RT));
		RT.IPDestAddr = 0;
		RT.IPDestMask = 0;
		RT.IPGateAddr = inet_addr(IPString);


		CfgAddEntry(hCfg, CFGTAG_ROUTE, 1, 0,
					sizeof(CI_ROUTE), (UINT8 *)&RT, &cfgGateway);
    }
}

/**
 *  \name ServiceReport
 *  \brief The function is called whenever there is a status change
 *  \param Item
 *  \param Status
 *  \param Report
 *  \param h
 *  \return none
 *
 */
static void ServiceReport( uint Item, uint Status, uint Report, HANDLE h )
{

	//TaskName[Item-1];
	//ReportStr[Report/256];
	//Report&0xFF;
	//StatusStr[Status];

    //
    // Example of adding to the DHCP configuration space
    //
    // When using the DHCP client, the client has full control over access
    // to the first 256 entries in the CFGTAG_SYSINFO space.
    //
    // Note that the DHCP client will erase all CFGTAG_SYSINFO tags except
    // CFGITEM_DHCP_HOSTNAME. If the application needs to keep manual
    // entries in the DHCP tag range, then the code to maintain them should
    // be placed here.
    //
    // Here, we want to manually add a DNS server to the configuration, but
    // we can only do it once DHCP has finished its programming.
    //
    if( Item == CFGITEM_SERVICE_DHCPCLIENT &&
        Status == CIS_SRV_STATUS_ENABLED &&
        (Report == (NETTOOLS_STAT_RUNNING|DHCPCODE_IPADD) ||
         Report == (NETTOOLS_STAT_RUNNING|DHCPCODE_IPRENEW)) )
    {
        IPN IPTmp;

        // Manually add the DNS server when specified
        IPTmp = inet_addr(DNSServer);
        if( IPTmp )
            CfgAddEntry( 0, CFGTAG_SYSINFO, CFGITEM_DHCP_DOMAINNAMESERVER,
                         0, sizeof(IPTmp), (UINT8 *)&IPTmp, 0 );
        // We can now check on what the DHCP server supplied in
        // response to our DHCP option tags.
        CheckDHCPOptions();


    }
    // Reset DHCP client service on failure
    if( Item==CFGITEM_SERVICE_DHCPCLIENT && (Report&~0xFF)==NETTOOLS_STAT_FAULT )
    {
        CI_SERVICE_DHCPC dhcpc;
        int tmp;
        // Get DHCP entry data (for index to pass to DHCP_reset).
        tmp = sizeof(dhcpc);
        CfgEntryGetData( h, &tmp, (UINT8 *)&dhcpc );

        // Create the task to reset DHCP on its designated IF
        // We must use TaskCreate instead of just calling the function as
        // we are in a callback function.
        TaskCreate( DHCPReset, "DHCPreset", OS_TASKPRINORM, 0x1000,
                    dhcpc.cisargs.IfIdx, 1, 0 );
    }
}


static void registerIPCfg(HANDLE hCfg)
{
    CI_IPNET NA;
    CI_ROUTE RT;
    uint tmp;
	if((eipDevInitConfig.tcpipConfig.startupType==EIP_STARTUP_DHCP) && (!acdCablePull))//Outofbox Config
	{
		startupMode =EIP_STARTUP_DHCP;
		CI_SERVICE_DHCPC dhcpc;
		bzero( &dhcpc, sizeof(dhcpc) );
		dhcpc.cisargs.Mode   = CIS_FLG_IFIDXVALID;
		dhcpc.cisargs.IfIdx  = 1;
		dhcpc.cisargs.pCbSrv = &ServiceReport;
		dhcpc.param.pOptions = DHCP_OPTIONS;
		dhcpc.param.len = 2;
		CfgAddEntry( hCfg, CFGTAG_SERVICE, CFGITEM_SERVICE_DHCPCLIENT, 0,
					 sizeof(dhcpc), (UINT8 *)&dhcpc, &dhcphandle );
	}
	else
	{

		startupMode =EIP_STARTUP_STATICIP;
		/* Add our global hostname to hCfg (to be claimed in all connected domains) */
		CfgAddEntry(hCfg, CFGTAG_SYSINFO, CFGITEM_DHCP_HOSTNAME, 0,
					 strlen(ti_ndk_config_Global_HostName),
					 (UINT8 *)ti_ndk_config_Global_HostName, &cfgHostname);

		/* Configure IP address manually on interface 1 */
		bzero(&NA, sizeof(NA));

		NA.IPAddr  = htonl(eipDevInitConfig.tcpipConfig.deviceIP);
		NA.IPMask  = htonl(eipDevInitConfig.tcpipConfig.networkMask);
		strcpy(NA.Domain, DomainName);
		NA.NetType = 0;
		CfgAddEntry(hCfg, CFGTAG_IPNET, 1, 0,
					sizeof(CI_IPNET), (UINT8 *)&NA, &cfgAddress);

		/*
		 *  Add the default gateway. Since it is the default, the
		 *  destination address and mask are both zero (we go ahead
		 *  and show the assignment for clarity).
		 */
		bzero(&RT, sizeof(RT));
		RT.IPDestAddr = 0;
		RT.IPDestMask = 0;
		RT.IPGateAddr = htonl(eipDevInitConfig.tcpipConfig.gatewayAddress);

		CfgAddEntry(hCfg, CFGTAG_ROUTE, 1, 0,
					sizeof(CI_ROUTE), (UINT8 *)&RT, &cfgGateway);
	}
	/*Remove Debug prints from NDK*/
    tmp = DBG_INFO;	// EN era DBG_NONE
    CfgAddEntry( hCfg, CFGTAG_OS, CFGITEM_OS_DBGPRINTLEVEL,
                 CFG_ADDMODE_UNIQUE, sizeof(uint), (UINT8 *)&tmp, 0 );
	/*Set NDK Kernel Task Priority to 11*/
    tmp = 11;
    CfgAddEntry(hCfg, CFGTAG_OS, CFGITEM_OS_TASKPRIKERN,
                 CFG_ADDMODE_UNIQUE, sizeof(uint), (UINT8 *)&tmp, 0 );

	acdCablePull=0;
}


void GetTCPIPConfigDetails(ST_TCPIP_CONFIGURATION* tcpIPDetails)
{
	CI_ROUTE CAT;
	CI_IPNET NAT;

	CfgGetImmediate(hCfg, CFGTAG_ROUTE, 1, 1, sizeof(CI_ROUTE), (UINT8 *)&CAT);
	CfgGetImmediate(hCfg, CFGTAG_IPNET, 1, 1, sizeof(NAT), (UINT8 *)&NAT);
	tcpIPDetails->IP_Address = (EIP_DWORD)NAT.IPAddr;
	tcpIPDetails->Network_Mask = (EIP_DWORD)NAT.IPMask;
	tcpIPDetails->Gateway_Address = (EIP_DWORD)CAT.IPGateAddr;

	ConvertIPEndianness(&tcpIPDetails->IP_Address);
	ConvertIPEndianness(&tcpIPDetails->Network_Mask);
	ConvertIPEndianness(&tcpIPDetails->Gateway_Address);

}

static void deregisterIPCfg(HANDLE hCfg)
{

	if(startupMode == EIP_STARTUP_DHCP)
	{
		CfgRemoveEntry(hCfg, dhcphandle);
	}
	else
	{

		CfgRemoveEntry(hCfg, cfgGateway);
	    CfgRemoveEntry(hCfg, cfgAddress);
	    CfgRemoveEntry(hCfg, cfgHostname);
	}


}

/*
 *  ti_ndk_config_Global_NetworkOpen
 *
 *  This function is called after the configuration has booted
 */
static void ti_ndk_config_Global_NetworkOpen()
{
    registerIPCfg(hCfg);
}

/*
 *  ti_ndk_config_Global_NetworkClose
 *
 *  This function is called when the network is shutting down,
 *  or when it no longer has any IP addresses assigned to it.
 */
static void ti_ndk_config_Global_NetworkClose()
{
    deregisterIPCfg(hCfg);
}


/*
 *  ti_ndk_config_Global_NetworkIPAddr
 *
 *  This function is called whenever an IP address binding is
 *  added or removed from the system.
 */
static void ti_ndk_config_Global_NetworkIPAddr(IPN IPAddr, uint IfIdx, uint fAdd)
{
    IPN IPTmp;
    IPTmp = ntohl(IPAddr);
    EIPIPAddressChange(IPTmp,fAdd);

    System_flush();
}


/**
 *  \name NDKACD_stackThread
 *  \brief Main NDK Thread
 *  \param arg0 Argument if any
 *  \return none
 *
*/
void NDKACD_stackThread(UArg arg0, UArg arg1)
{
    Clock_Params clockParams;
    int rc;

    /* Create the NDK heart beat */
    Clock_Params_init(&clockParams);
    clockParams.startFlag = TRUE;
    clockParams.period = 200; /*Period set to 200 as we have 500microsec tick period*/
    Clock_create(&llTimerTick, clockParams.period, &clockParams, NULL);

    /* THIS MUST BE THE ABSOLUTE FIRST THING DONE IN AN APPLICATION!! */
    rc = NC_SystemOpen(NC_PRIORITY_HIGH, NC_OPMODE_INTERRUPT);
    if (rc) {

    }

    /* Create and build the system configuration from scratch. */
    hCfg = CfgNew();
    if (!hCfg) {
        goto main_exit;
    }

    /* add the Tcp module configuration settings. */
    ti_ndk_config_tcp_init(hCfg);

    /* Do EIP Application specific Init*/
    EIPACDInit();

    /*
     *  Boot the system using this configuration
     *
     *  We keep booting until the function returns 0. This allows
     *  us to have a "reboot" command.
    */
    do {

    	EIPACDStart();
        rc = NC_NetStart(hCfg, ti_ndk_config_Global_NetworkOpen, 
                         ti_ndk_config_Global_NetworkClose, 
                         ti_ndk_config_Global_NetworkIPAddr);
        EIPACDStop();
    } while (rc > 0 || rc == ACD_LINKDOWN);

    /*EIP Application Exit*/
    EIPACDExit();

    /* Delete Configuration */
    CfgFree(hCfg);
    /* Close the OS */
main_exit:
    NC_SystemClose();
    System_flush();
}
