/*
 * Copyright 2001 Mike McCormack
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "config.h"

#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#include "winbase.h"
#include "winreg.h"
#include "wingdi.h"
#include "winuser.h"
#include "wine/debug.h"
#include "winerror.h"
#include "nb30.h"

#ifdef HAVE_SYS_FILE_H
# include <sys/file.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_SYS_SOCKIO_H
# include <sys/sockio.h>
#endif
#ifdef HAVE_NET_IF_H
# include <net/if.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif

#ifdef HAVE_SOCKADDR_SA_LEN
#  ifndef max
#   define max(a,b) ((a) > (b) ? (a) : (b))
#  endif
#  define ifreq_size(i) max(sizeof(struct ifreq),\
sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
# else
#  define ifreq_size(i) sizeof(struct ifreq)
# endif /* defined(HAVE_SOCKADDR_SA_LEN) */

WINE_DEFAULT_DEBUG_CHANNEL(netbios);

HMODULE NETAPI32_hModule = 0;

struct NetBiosAdapter
{
    int valid;
    unsigned char address[6];
};

static struct NetBiosAdapter NETBIOS_Adapter[MAX_LANA];

# ifdef SIOCGIFHWADDR
int get_hw_address(int sd, struct ifreq *ifr, unsigned char *address)
{
    if (ioctl(sd, SIOCGIFHWADDR, ifr) < 0)
        return -1;
    memcpy(address, (unsigned char *)&ifr->ifr_hwaddr.sa_data, 6);
    return 0;
}
# else
#  ifdef SIOCGENADDR
int get_hw_address(int sd, struct ifreq *ifr, unsigned char *address)
{
    if (ioctl(sd, SIOCGENADDR, ifr) < 0)
        return -1;
    memcpy(address, (unsigned char *) ifr->ifr_enaddr, 6);
    return 0;
}
#   else
int get_hw_address(int sd, struct ifreq *ifr, unsigned char *address)
{
    return -1;
}
#  endif /* SIOCGENADDR */
# endif /* SIOCGIFHWADDR */

static UCHAR NETBIOS_Enum(PNCB ncb)
{
#ifdef HAVE_NET_IF_H
    int             sd;
    struct ifreq    ifr, *ifrp;
    struct ifconf   ifc;
    unsigned char   buf[1024];
    int             i, ofs;
#endif
    LANA_ENUM *lanas = (PLANA_ENUM) ncb->ncb_buffer;

    TRACE("NCBENUM\n");

    lanas->length = 0;

#ifdef HAVE_NET_IF_H
    /* BSD 4.4 defines the size of an ifreq to be
     * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
     * However, under earlier systems, sa_len isn't present, so
     *  the size is just sizeof(struct ifreq)
     */

    sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    if (sd < 0)
        return NRC_OPENERROR;

    memset(buf, 0, sizeof(buf));
    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = buf;
    /* get the ifconf interface */
    if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0)
    {
        close(sd);
        return NRC_OPENERROR;
    }

    /* loop through the interfaces, looking for a valid one */
    /* n = ifc.ifc_len; */
    ofs = 0;
    for (i = 0; i < ifc.ifc_len; i++)
    {
        unsigned char *a = NETBIOS_Adapter[i].address;

        ifrp = (struct ifreq *)((char *)ifc.ifc_buf+ofs);
        strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ);

        /* try to get the address for this interface */
        if(get_hw_address(sd, &ifr, a)==0)
        {
            /* make sure it's not blank */
            /* if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
	        continue; */

            TRACE("Found valid adapter %d at %02x:%02x:%02x:%02x:%02x:%02x\n", i,
                        a[0],a[1],a[2],a[3],a[4],a[5]);

            NETBIOS_Adapter[i].valid = TRUE;
            lanas->lana[lanas->length] = i;
            lanas->length++;
        }
        ofs += ifreq_size(ifr);
    }
    close(sd);
#endif /* HAVE_NET_IF_H */
    return NRC_GOODRET;
}


static UCHAR NETBIOS_Astat(PNCB ncb)
{
    struct NetBiosAdapter *nad = &NETBIOS_Adapter[ncb->ncb_lana_num];
    PADAPTER_STATUS astat = (PADAPTER_STATUS) ncb->ncb_buffer;

    TRACE("NCBASTAT (Adapter %d)\n", ncb->ncb_lana_num);

    if(!nad->valid)
        return NRC_INVADDRESS;

    memset(astat, 0, sizeof astat);
    memcpy(astat->adapter_address, nad->address, sizeof astat->adapter_address);

    return NRC_GOODRET;
}

BOOL WINAPI
NETAPI32_LibMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    TRACE("%x,%lx,%p\n", hinstDLL, fdwReason, lpvReserved);

    switch (fdwReason) {
	case DLL_PROCESS_ATTACH:
            NETAPI32_hModule = hinstDLL;
	    break;
	case DLL_PROCESS_DETACH:
	    break;
    }

    return TRUE;
}

BOOL WINAPI Netbios(PNCB pncb)
{
    UCHAR ret = NRC_ILLCMD;

    TRACE("ncb = %p\n",pncb);

    if(!pncb)
        return NRC_INVADDRESS;

    switch(pncb->ncb_command&0x7f)
    {
    case NCBRESET:
        FIXME("NCBRESET adapter %d\n",pncb->ncb_lana_num);
        if( (pncb->ncb_lana_num < MAX_LANA ) &&
             NETBIOS_Adapter[pncb->ncb_lana_num].valid)
            ret = NRC_GOODRET;
        else
            ret = NRC_ILLCMD; /* NetBIOS emulator not found */
        break;

    case NCBADDNAME:
        FIXME("NCBADDNAME\n");
        break;

    case NCBADDGRNAME:
        FIXME("NCBADDGRNAME\n");
        break;

    case NCBDELNAME:
        FIXME("NCBDELNAME\n");
        break;

    case NCBSEND:
        FIXME("NCBSEND\n");
        break;

    case NCBRECV:
        FIXME("NCBRECV\n");
        break;

    case NCBHANGUP:
        FIXME("NCBHANGUP\n");
        break;

    case NCBCANCEL:
        FIXME("NCBCANCEL\n");
        break;

    case NCBLISTEN:
        FIXME("NCBLISTEN\n");
        break;

    case NCBASTAT:
        ret = NETBIOS_Astat(pncb);
        break;

    case NCBENUM:
        ret = NETBIOS_Enum(pncb);
        break;

    default:
        FIXME("(%p): command code %02x\n", pncb, pncb->ncb_command);

        ret = NRC_ILLCMD; /* NetBIOS emulator not found */
    }
    pncb->ncb_retcode = ret;
    return ret;
}

