#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <newt.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "devices.h"
#include "hd.h"
#include "install.h"
#include "log.h"
#include "scsi.h"

static int scsiChoicePanel(int * addSCSI);

static int scsiChoicePanel(int * addSCSI) {
    newtComponent label, f, answer;
    newtComponent yes, no;

    newtOpenWindow(21, 7, 35, 9, "SCSI Configuration");

    label = newtLabel(2, 2, "Do you have any SCSI adapters?");
 
    yes = newtButton(5, 5, "Yes");
    no = newtButton(22, 5, "No");

    f = newtForm(NULL, NULL, 0);
    newtFormAddComponents(f, label, yes, no, NULL);
    newtFormSetCurrent(f, no);
    
    answer = newtRunForm(f);
    if (answer == f) 
	answer = newtFormGetCurrent(f);

    newtFormDestroy(f);
    newtPopWindow();

    if (answer == no) 
	*addSCSI = 0;
    else
	*addSCSI = 1;
 
    return 0;
}


int setupSCSIInterfaces(int forceConfig, struct driversLoaded ** dl) {
    int rc;
    int hasscsi;

    if (scsiDeviceAvailable()) return 0;

    do {
	if (forceConfig)
	    forceConfig = 0;
	else {
	    scsiChoicePanel(&hasscsi);
	    if (!hasscsi) return 0;
	}

    	rc = loadDeviceDriver(DRIVER_SCSI, dl);
	if (rc == INST_ERROR) return INST_ERROR;
    } while (rc);

    return 0;
}

int scsiDeviceAvailable(void) {
    int fd;
    char buf[80];
    int i;

    fd = open("/proc/scsi/scsi", O_RDONLY);
    if (fd < 0) {
	logMessage("failed to open /proc/scsi/scsi: %s", strerror(errno));
	return 0;
    }
    
    i = read(fd, buf, sizeof(buf) - 1);
    if (i < 1) {
	logMessage("failed to read /proc/scsi/scsi: %s", strerror(errno));
	return 0;
    }
    close(fd);
    buf[i] = '\0';

    logMessage("/proc/scsi/scsi: %s", buf);

    if (strstr(buf, "devices: none")) {
	logMessage("no scsi devices are available");
	return 0;
    }

    logMessage("scsi devices are available");
    return 1;
}

#define SCSISCSI_TOP	0
#define SCSISCSI_HOST 	1
#define SCSISCSI_VENDOR 2
#define SCSISCSI_TYPE 	3

int scsiGetDevices(struct scsiDeviceInfo ** sdiPtr) {
    int fd;
    char buf[16384];
    char linebuf[80];
    char typebuf[10];
    int i, state = SCSISCSI_TOP;
    char * start, * chptr, * next;
    char driveName = 'a';
    char cdromNum = '0';
    char tapeNum = '0';
    int numMatches = 0;
    struct scsiDeviceInfo * sdi;
    int id = 0;

    /*sdi = malloc(sizeof(*sdi));*/
    sdi = malloc(16384);

    fd = open("/proc/scsi/scsi", O_RDONLY);
    if (fd < 0) {
	logMessage("failed to open /proc/scsi/scsi: %s", strerror(errno));
	return 1;
    }
    
    i = read(fd, buf, sizeof(buf) - 1);
    if (i < 1) {
	logMessage("failed to read /proc/scsi/scsi: %s", strerror(errno));
	return 1;
    }
    close(fd);
    buf[i] = '\0';

    start = buf;
    while (*start) {
	chptr = start;
 	while (*chptr != '\n') chptr++;
	*chptr = '\0';
	next = chptr + 1;

	switch (state) {
	  case SCSISCSI_TOP:
	    if (strcmp("Attached devices: ", start)) {
		logMessage("unexpected line in /proc/scsi/scsi: %s", start);
		free(sdi);
		return INST_ERROR;
	    }
	    state = SCSISCSI_HOST;
	    break;

	  case SCSISCSI_HOST:
	    if (strncmp("Host: ", start, 6)) {
		logMessage("unexpected line in /proc/scsi/scsi: %s", start);
		free(sdi);
		return INST_ERROR;
	    }

	    start = strstr(start, "Id: ");
	    if (!start) {
		logMessage("Id: missing in /proc/scsi/scsi");
		return INST_ERROR;
	    }
	    start += 4;

	    id = strtol(start, NULL, 10);

	    state = SCSISCSI_VENDOR;
	    break;

	  case SCSISCSI_VENDOR:
	    if (strncmp("  Vendor: ", start, 10)) {
		logMessage("unexpected line in /proc/scsi/scsi: %s", start);
		free(sdi);
		return INST_ERROR;
	    }

	    start += 10;
	    chptr = strstr(start, "Model:");
	    if (!chptr) {
		logMessage("Model missing in /proc/scsi/scsi");
		free(sdi);
		return INST_ERROR;
	    }

	    chptr--;
	    while (*chptr == ' ') chptr--;
	    *(chptr + 1) = '\0';

	    strcpy(linebuf, start);
	    *linebuf = toupper(*linebuf);
	    chptr = linebuf + 1;
	    while (*chptr) {
		*chptr = tolower(*chptr);
		chptr++;
	    }

	    start = strstr(start + strlen(linebuf) + 1, "Model:");
	    start += 7;
		
	    chptr = strstr(start, "Rev:");
	    if (!chptr) {
		logMessage("Rev missing in /proc/scsi/scsi");
		free(sdi);
		return INST_ERROR;
	    }
	   
	    chptr--;
	    while (*chptr == ' ') chptr--;
	    *(chptr + 1) = '\0';

	    strcat(linebuf, " ");
	    strcat(linebuf, start);

	    state = SCSISCSI_TYPE;

	    break;

	  case SCSISCSI_TYPE:
	    if (strncmp("  Type:", start, 7)) {
		logMessage("unexpected line in /proc/scsi/scsi: %s", start);
		free(sdi);
		return INST_ERROR;
	    }
	    *typebuf = '\0';
	    if (strstr(start, "Direct-Access")) 
		sprintf(typebuf, "sd%c", driveName++);
	    else if (strstr(start, "Sequential-Access")) 
		sprintf(typebuf, "st%c", tapeNum++);
	    else if (strstr(start, "CD-ROM")) 
		sprintf(typebuf, "scd%c", cdromNum++);

	    if (*typebuf) {
		/*sdi = realloc(sdi, sizeof(*sdi) * (numMatches + 2));*/
		sdi[numMatches].deviceName = strdup(typebuf);
		sdi[numMatches].info = strdup(linebuf);
		sdi[numMatches].bus = 0;
		sdi[numMatches++].id = id;
	    }

	    state = SCSISCSI_HOST;
	}

	start = next;
    }

    sdi[numMatches].deviceName = NULL;
    sdi[numMatches].info = NULL;

    *sdiPtr = sdi;

    return 0;
}
