/* Copyright 2000 Red Hat, Inc.
 *
 * This software may be freely redistributed under the terms of the GNU
 * public license.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "Python.h"
#include "kudzu.h"
#include "device.h"
#include "ddc.h"
#include "ide.h"
#include "isapnp.h"
#include "keyboard.h"
#include "parallel.h"
#include "pci.h"
#include "psaux.h"
#include "sbus.h"
#include "scsi.h"
#include "serial.h"
#include "usb.h"

typedef struct {
    char * name;
    int value;
} TableEntry;

static TableEntry classTable[] = {
    { "CLASS_UNSPEC",	CLASS_UNSPEC },
    { "CLASS_OTHER",	CLASS_OTHER },
    { "CLASS_NETWORK",	CLASS_NETWORK },
    { "CLASS_SCSI",	CLASS_SCSI },
    { "CLASS_MOUSE",	CLASS_MOUSE },    
    { "CLASS_AUDIO",	CLASS_AUDIO },
    { "CLASS_CDROM",	CLASS_CDROM },    
    { "CLASS_MODEM",	CLASS_MODEM },    
    { "CLASS_VIDEO",	CLASS_VIDEO },
    { "CLASS_TAPE",	CLASS_TAPE },    
    { "CLASS_FLOPPY",	CLASS_FLOPPY },    
    { "CLASS_SCANNER",	CLASS_SCANNER },    
    { "CLASS_HD",	CLASS_HD },    
    { "CLASS_RAID",	CLASS_RAID },    
    { "CLASS_PRINTER",	CLASS_PRINTER },
    { "CLASS_CAPTURE",	CLASS_CAPTURE },
    { "CLASS_KEYBOARD",	CLASS_KEYBOARD },
    { "CLASS_SOCKET",	CLASS_SOCKET },
    { "CLASS_MONITOR",	CLASS_MONITOR },
    { "CLASS_USB", CLASS_USB },
    { NULL },
} ;

static int numClassEntries = sizeof (classTable) / sizeof (TableEntry);

static TableEntry busTable[] = {
    { "BUS_UNSPEC",	BUS_UNSPEC },
    { "BUS_OTHER",	BUS_OTHER },
    { "BUS_PCI",	BUS_PCI },
    { "BUS_SBUS",	BUS_SBUS },
    { "BUS_SERIAL",	BUS_SERIAL },
    { "BUS_PSAUX",	BUS_PSAUX },
    { "BUS_PARALLEL",	BUS_PARALLEL },
    { "BUS_SCSI",	BUS_SCSI },
    { "BUS_IDE",	BUS_IDE },
    { "BUS_KEYBOARD",	BUS_KEYBOARD },
    { "BUS_DDC",	BUS_DDC },
    { "BUS_USB",	BUS_USB },
    { "BUS_ISAPNP",	BUS_ISAPNP },
    { "BUS_MISC",	BUS_MISC },
    { NULL }
};

static int numBusEntries = sizeof (busTable) / sizeof (TableEntry);

static TableEntry modeTable[] = {
    { "PROBE_ALL",	PROBE_ALL },
    { "PROBE_SAFE",	PROBE_SAFE },
    { "PROBE_ONE",	PROBE_ONE },
    { NULL },
} ;
static int numModeEntries = sizeof (modeTable) / sizeof (TableEntry);


void addDDCInfo(PyObject *dict,struct ddcDevice * device)
{
    PyObject *o;

    if(device->id) {
        PyDict_SetItemString(dict,"id",
			     o=PyString_FromString(device->id));
	Py_DECREF(o);
    } else {
		PyDict_SetItemString(dict,"id",Py_None); 
	}
    PyDict_SetItemString(dict,"horizSyncMin",
			 o=PyInt_FromLong(device->horizSyncMin));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"horizSyncMax",
			 o=PyInt_FromLong(device->horizSyncMax));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"vertRefreshMin",
			 o=PyInt_FromLong(device->vertRefreshMin));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"vertRefreshMax",
			 o=PyInt_FromLong(device->vertRefreshMax));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"mem",o=PyInt_FromLong(device->mem));
    Py_DECREF(o);
}

void addIDEInfo(PyObject *dict,struct ideDevice * device)
{
    char *str;
    PyObject *o;

    str = (device->physical) ? device->physical : strdup("");
    PyDict_SetItemString(dict,"physical", o=PyString_FromString(str));
    str = (device->logical) ? device->logical : strdup("");
    PyDict_SetItemString(dict,"logical", o=PyString_FromString(str));
}

void addParallelInfo(PyObject *dict,struct parallelDevice * device)
{
    PyObject *o;
    
    PyDict_SetItemString(dict,"pnpmodel",
			 o=PyString_FromString(device->pnpmodel));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"pnpmfr",o=PyString_FromString(device->pnpmfr));
    Py_DECREF(o);    
    PyDict_SetItemString(dict,"pnpmodes",
			 o=PyString_FromString(device->pnpmodes));
    Py_DECREF(o);    
    PyDict_SetItemString(dict,"pnpdesc",
			 o=PyString_FromString(device->pnpdesc));
    Py_DECREF(o);    
    if(device->pinfo!=NULL){
        PyDict_SetItemString(dict,"xres",
			     o=PyInt_FromLong(device->pinfo->xres));
	Py_DECREF(o);
        PyDict_SetItemString(dict,"yres",
			     o=PyInt_FromLong(device->pinfo->yres));
	Py_DECREF(o);
        PyDict_SetItemString(dict,"color",
			     o=PyInt_FromLong(device->pinfo->color));
	Py_DECREF(o);
        PyDict_SetItemString(dict,"ascii",
			     o=PyInt_FromLong(device->pinfo->ascii));
	Py_DECREF(o);
        PyDict_SetItemString(dict,"uniprint",
			     o=PyString_FromString(device->pinfo->uniprint));
	Py_DECREF(o);
    }
}

void addPCIInfo(PyObject *dict,struct pciDevice * device)
{
    PyObject *o;
    
    PyDict_SetItemString(dict,"vendorId", o=PyInt_FromLong(device->vendorId));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"deviceId",o=PyInt_FromLong(device->deviceId));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"subVendorId",
			 o=PyInt_FromLong(device->subVendorId));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"subDeviceId",
			 o=PyInt_FromLong(device->subDeviceId));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"pciType",
			 o=PyInt_FromLong(device->pciType));
    Py_DECREF(o);
}

void addSbusInfo(PyObject *dict,struct sbusDevice * device)
{
    PyObject *o;
    
    PyDict_SetItemString(dict,"width",o=PyInt_FromLong(device->width));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"height",o=PyInt_FromLong(device->height));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"freq",o=PyInt_FromLong(device->freq));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"monitor",o=PyInt_FromLong(device->monitor));
    Py_DECREF(o);
}

void addScsiInfo(PyObject *dict,struct scsiDevice * device)
{
    PyObject *o;
    
    PyDict_SetItemString(dict,"host",o=PyInt_FromLong(device->host));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"channel",o=PyInt_FromLong(device->channel));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"id",o=PyInt_FromLong(device->id));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"lun",o=PyInt_FromLong(device->lun));
    Py_DECREF(o);
}

void addSerialInfo(PyObject *dict,struct serialDevice * device)
{
    PyObject *o;
    
    if(device->pnpmfr) {
        PyDict_SetItemString(dict,"pnpmfr",
			     o=PyString_FromString(device->pnpmfr));
	Py_DECREF(o);
    } else {
        PyDict_SetItemString(dict,"pnpmfr",o=PyString_FromString("none"));
	Py_DECREF(o);
    }
    if(device->pnpmodel){
        PyDict_SetItemString(dict,"pnpmodel",o=PyString_FromString(device->pnpmodel));
	Py_DECREF(o);
    } else {
        PyDict_SetItemString(dict,"pnpmodel",o=PyString_FromString("none"));
	Py_DECREF(o);
    }
    if(device->pnpcompat) {
        PyDict_SetItemString(dict,"pnpcompat",o=PyString_FromString(device->pnpcompat));
	Py_DECREF(o);
    } else {
        PyDict_SetItemString(dict,"pnpcompat",o=PyString_FromString("none"));
	Py_DECREF(o);
    }
    if(device->pnpdesc) {
        PyDict_SetItemString(dict,"pnpdesc",o=PyString_FromString(device->pnpdesc));
	Py_DECREF(o);
    } else {
        PyDict_SetItemString(dict,"pnpdesc",o=PyString_FromString("none"));
	Py_DECREF(o);
    }
}

void addUsbInfo(PyObject *dict,struct usbDevice * device)
{
    PyObject *o;
    
    PyDict_SetItemString(dict,"usbclass",o=PyInt_FromLong(device->usbclass));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"usbsubclass",o=PyInt_FromLong(device->usbsubclass));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"usbprotocol",o=PyInt_FromLong(device->usbprotocol));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"usbbus",o=PyInt_FromLong(device->usbbus));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"usblevel",o=PyInt_FromLong(device->usblevel));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"usbport",o=PyInt_FromLong(device->usbport));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"vendorid",o=PyInt_FromLong(device->vendorId));
    Py_DECREF(o);
    PyDict_SetItemString(dict,"deviceid",o=PyInt_FromLong(device->deviceId));
    Py_DECREF(o);
    if (device->productrevision)
	  PyDict_SetItemString(dict,"productrevision",o=PyString_FromString(device->productrevision));
    else
	  PyDict_SetItemString(dict,"productrevision",o=PyString_FromString("none"));
    Py_DECREF(o);
}

/*
 * These are empty, and just used for consistency and possible
 * future expansion
 */

void addKbdInfo(PyObject *dict,struct keyboardDevice * device)
{
    return;
}

void addPsauxInfo(PyObject *dict,struct psauxDevice * device)
{
    return;
}


PyObject * createDict(struct device * probedDevice)
{
    PyObject *dict;
    PyObject *o;

    dict = PyDict_New();

    if (probedDevice->desc) {
	o = PyString_FromString(probedDevice->desc);
	PyDict_SetItemString(dict,"desc",o);
	Py_DECREF(o);
    } else
	PyDict_SetItemString(dict,"desc",Py_None);
    if (probedDevice->driver) {
	o = PyString_FromString(probedDevice->driver);
	PyDict_SetItemString(dict,"driver",o);
	Py_DECREF(o);
    } else
	PyDict_SetItemString(dict,"driver",Py_None);
    if (probedDevice->device) {
	o = PyString_FromString(probedDevice->device);
	PyDict_SetItemString(dict,"device",o);
	Py_DECREF(o);
    } else
	PyDict_SetItemString(dict,"device",Py_None);
    PyDict_SetItemString(dict,"detached",o=PyInt_FromLong(probedDevice->detached)); Py_DECREF(o);
    PyDict_SetItemString(dict,"class",o=PyInt_FromLong(probedDevice->class)); Py_DECREF(o);
    PyDict_SetItemString(dict,"bus",o=PyInt_FromLong(probedDevice->bus)); Py_DECREF(o);
    PyDict_SetItemString(dict,"index",o=PyInt_FromLong(probedDevice->index)); Py_DECREF(o);
    switch(probedDevice->bus){
    case BUS_DDC:
        addDDCInfo(dict,(struct ddcDevice*)probedDevice);
        break;
    case BUS_IDE:
        addIDEInfo(dict,(struct ideDevice*)probedDevice);
        break;
    case BUS_KEYBOARD:
        addKbdInfo(dict,(struct keyboardDevice*)probedDevice);
        break;
    case BUS_PARALLEL:
        addParallelInfo(dict,(struct parallelDevice*)probedDevice);
        break;
    case BUS_PCI:
        addPCIInfo(dict,(struct pciDevice*)probedDevice);
        break;
    case BUS_PSAUX:
        addPsauxInfo(dict,(struct psauxDevice*)probedDevice);
        break;
    case BUS_SBUS:
        addSbusInfo(dict,(struct sbusDevice*)probedDevice);
        break;
    case BUS_SCSI:
        addScsiInfo(dict,(struct scsiDevice*)probedDevice);
        break;
    case BUS_SERIAL:
        addSerialInfo(dict,(struct serialDevice*)probedDevice);
        break;
    case BUS_USB:
        addUsbInfo(dict,(struct usbDevice*)probedDevice);
        break;
    default:
        break;
    }
    return dict;
}

static PyObject * doProbe (PyObject * self, PyObject * args) {
    int class, bus, mode;
    struct device ** devices, ** tmp;
    PyObject * list;
    
    if (!PyArg_ParseTuple(args, "iii", &class, &bus, &mode))
	return NULL;

    devices = probeDevices(class, bus, mode);

    list = PyList_New(0);

    if (!devices)
	return list;
    
    tmp = devices;
    while (*tmp) {
	PyList_Append (list, createDict(*tmp));
	tmp++;
    }

    tmp = devices;
    while (*tmp) {
	(*tmp)->freeDevice (*tmp);
	tmp++;
    }

    free(devices);

    return list;
}


static PyMethodDef kudzuMethods[] = {
    { "probe", (PyCFunction) doProbe, METH_VARARGS, NULL },
    { NULL }
} ;

static void registerTable (PyObject * dict, TableEntry * table) {
    int i;
    PyObject *o;
    
    i = 0;
    while (table[i].name) {
	PyDict_SetItemString(dict, table[i].name,
			     o=PyInt_FromLong (table[i].value));
	Py_DECREF(o);
	i++;
    }
}

void init_kudzu (void) {
    PyObject * m, * dict;
    
    m = Py_InitModule("_kudzu", kudzuMethods);
    dict = PyModule_GetDict(m);

    registerTable (dict, classTable);
    registerTable (dict, busTable);
    registerTable (dict, modeTable);
	
    initializeDeviceList();
}
