#include <pci/hd.h>
#include <sys/video.h>
#include <sys/drives.h>
#include <sys/io.h>
#include <lib/kmalloc.h>

struct driveInfo *hdd0;
struct driveInfo *hdd1;
struct driveInfo *hdd2;
struct driveInfo *hdd3;
  
void initHardDisk() {
  hdd0 = (struct driveInfo *)kmalloc(sizeof(struct driveInfo),-2);
  hdd1 = (struct driveInfo *)kmalloc(sizeof(struct driveInfo),-2);
  hdd2 = (struct driveInfo *)kmalloc(sizeof(struct driveInfo),-2);
  hdd3 = (struct driveInfo *)kmalloc(sizeof(struct driveInfo),-2);
  hdd0->hdPort = 0x1F7;
  hdd0->hdDev  = 0x40;
  hdd1->hdPort = 0x1F7;
  hdd1->hdDev  = 0x50;
  hdd2->hdPort = 0x177;
  hdd2->hdDev  = 0x40;
  hdd3->hdPort = 0x177;
  hdd3->hdDev  = 0x50;
  if (!initDrive(hdd0)) {
    addDrive(1,1,hdd0,hdRead,hdWrite,0x0);
    }
  if (!initDrive(hdd1)) {
    addDrive(2,1,hdd1,hdRead,hdWrite,0x0);
    }
  if (!initDrive(hdd2)) {
    addDrive(3,1,hdd2,hdRead,hdWrite,0x0);
    }
  if (!initDrive(hdd3)) {
    addDrive(4,1,hdd3,hdRead,hdWrite,0x0);
    }            
  return;
  }
  
int initDrive(struct driveInfo *hdd) {
  char retVal  = 0x0;
  long counter = 0x0;
  short *tmp    = 0x0;
  for (counter = 1000000;counter >= 0;counter--) {
    retVal = inportByte(hdd->hdPort) & 0x80;
    if (!retVal) goto ready;
    }
  kprintf("Error Initializing Drive\n");
  return(1);
  ready:
  hdd->hdPort--;
  outportByte(hdd->hdPort,hdd->hdDev);
  hdd->hdPort++;
  outportByte(hdd->hdPort,0xEC);
  for (counter = 1000000;counter >= 0;counter--) {
    retVal = inportByte(hdd->hdPort);
    if ((retVal & 1) != 0x0) {
      kprintf("Error Drive Not Available\n");
      return(1);
      }
    if ((retVal & 8) != 0x0) {
      goto go;
      }
    }
  kprintf("Time Out Waiting On Drive\n");
  return(1);
  go:
  hdd->hdPort -= 7;
  tmp = (short *)hdd->hdSector;
  for (counter = 0;counter < (512/2);counter++) {
    tmp[counter] = inportWord(hdd->hdPort);
    }
  retVal = hdd->hdSector[0x5E] & 127;
  switch (retVal) {
    case 0:
      hdd->hdShift = 3;
      hdd->hdMulti = 8;
    case 2:
      hdd->hdShift = 1;
      hdd->hdMulti = retVal;
      break;
    case 4:
      hdd->hdShift = 2;
      hdd->hdMulti = retVal;
      break;
    case 8:
      hdd->hdShift = 3;
      hdd->hdMulti = retVal;
      break;
    case 16:
      hdd->hdShift = 4;
      hdd->hdMulti = retVal;
      break;
    case 32:
      hdd->hdShift = 5;
      hdd->hdMulti = retVal;
      break;
    case 64:
      hdd->hdShift = 6;
      hdd->hdMulti = retVal;
      break;
    default:
      kprintf("Error BLOCK Mode Unavailable: [%i]\n",retVal);
      return(1);
    }
  hdd->hdPort += 2;
  outportByte(hdd->hdPort,retVal);
  hdd->hdPort += 4;
  outportByte(hdd->hdPort,hdd->hdDev);
  hdd->hdPort++;
  outportByte(hdd->hdPort,0xC6);
  //retVal--; //I'm Not Sure Why He Was Doing This
  hdd->hdMask = retVal;
  hdd->hdSize = (hdd->hdSector[0x7B] * 256 * 256 * 256) + (hdd->hdSector[0x7A] * 256 * 256) + (hdd->hdSector[0x79] * 256) + hdd->hdSector[0x78];
  hdd->hdEnable = 1;
  kprintf("Drive: [0x%X/0x%X], Size: [%iSectors/%iKBytes]\n",hdd->hdPort,hdd->hdDev,hdd->hdSize,((hdd->hdSize*512)/1024));
  return(0);
  }

void hdWrite(struct driveInfo *hdd,long startSector,long sectorCount,void *baseAddr) {
  long counter           = 0x0;
  long retVal            = 0x0;
  short transactionCount = 0x0;
  short *tmp             = (short *)baseAddr;
  if (hdd->hdEnable == 0x0) {
    kprintf("Invalid Drive\n");
    return;
    }
  if ((sectorCount >> hdd->hdShift) == 0x0) {
    hdd->hdCalc = sectorCount; //hdd->hdMask;
    transactionCount = 1;
    }
  else {
    hdd->hdCalc = hdd->hdMulti;
    transactionCount = sectorCount >> hdd->hdShift;
    }
  for (;transactionCount > 0;transactionCount--) {
    for (counter = 1000000;counter >= 0;counter--) {
      retVal = inportByte(hdd->hdPort) & 0x80;
      if (!retVal) goto ready;
      }
    kprintf("Time Out Waiting On Drive\n");
    return;
    ready:
    hdd->hdPort -= 5;
    outportByte(hdd->hdPort,hdd->hdCalc);
    hdd->hdPort++;
    outportByte(hdd->hdPort,(startSector & 0xFF));
    hdd->hdPort++;
    retVal = startSector >> 8;
    outportByte(hdd->hdPort,(retVal & 0xFF));
    hdd->hdPort++;
    retVal >>= 8;
    outportByte(hdd->hdPort,(retVal & 0xFF));
    hdd->hdPort++;
    retVal >>= 8;
    retVal &= 0x0F;
    retVal |= hdd->hdDev;
    outportByte(hdd->hdPort,(retVal & 0xFF));
    hdd->hdPort++;
    outportByte(hdd->hdPort,0xC5);
    for (counter = 1000000;counter >= 0;counter--) {
      retVal = inportByte(hdd->hdPort);
      if ((retVal & 1) != 0x0) {
        kprintf("HD Write Error\n");
        return;
        }
      if ((retVal & 8) != 0x0) {
        goto go;
        }
      }
    kprintf("Time Out Waiting On Drive\n");
    return;
    go:
    hdd->hdPort -= 7;
    for (counter = 0;counter < (hdd->hdCalc << 8);counter++) {
      outportWord(hdd->hdPort,tmp[counter]);
      }
    hdd->hdPort += 7;
    startSector += hdd->hdCalc;
    }
  return;
  }

void hdRead(struct driveInfo *hdd,long startSector,long sectorCount,void *baseAddr) {
  long counter           = 0x0;
  long retVal            = 0x0;
  short transactionCount = 0x0;
  short *tmp             = (short *)baseAddr;
  kprintf("Moo\n");
  if (hdd->hdEnable == 0x0) {
    kprintf("Invalid Drive\n");
    return;
    }
  if ((sectorCount >> hdd->hdShift) == 0x0) {
    hdd->hdCalc = sectorCount;//(hdd->hdMask);
    transactionCount = 1;
    }
  else {
    hdd->hdCalc = hdd->hdMulti;
    transactionCount = sectorCount >> hdd->hdShift;
    }
  for (;transactionCount > 0;transactionCount--) {
    for (counter = 1000000;counter >= 0;counter--) {
      retVal = inportByte(hdd->hdPort) & 0x80;
      if (!retVal) goto ready;
      }
    kprintf("Time Out Waiting On Drive\n");
    return;
    ready:
    hdd->hdPort -= 5;
    outportByte(hdd->hdPort,hdd->hdCalc);
    hdd->hdPort++;
    outportByte(hdd->hdPort,(startSector & 0xFF));
    hdd->hdPort++;
    retVal = startSector >> 8;
    outportByte(hdd->hdPort,(retVal & 0xFF));
    hdd->hdPort++;
    retVal >>= 8;
    outportByte(hdd->hdPort,(retVal & 0xFF));
    hdd->hdPort++;
    retVal >>= 8;
    retVal &= 0x0F;
    retVal |= hdd->hdDev;
    outportByte(hdd->hdPort,(retVal & 0xFF));
    hdd->hdPort++;
    outportByte(hdd->hdPort,0xC4);
    for (counter = 1000000;counter >= 0;counter--) {
      retVal = inportByte(hdd->hdPort);
      if ((retVal & 1) != 0x0) {
        kprintf("HD Write Error\n");
        return;
        }
      if ((retVal & 8) != 0x0) {
        goto go;
        }
      }
    kprintf("Time Out Waiting On Drive\n");
    return;
    go:
    hdd->hdPort -= 7;
    for (counter = 0;counter < (hdd->hdCalc << 8);counter++) {
      tmp[counter] = inportWord(hdd->hdPort);
      }
    hdd->hdPort += 7;
    startSector += hdd->hdCalc;
    }
  return;
  }  
