#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;
}