#include <EDK.h>
#include "General.h"
#include "CHarddisk.h"

void CHarddisk::SetError(const char* pcError) {
  strncpy(acError, pcError, sizeof acError);
  acError[sizeof acError - 1] = 0;
  iOut = 0;
}

DeviceType CHarddisk::GetType() {
  return eHarddisk;
}

CString CHarddisk::GetDisk() {
  return Dir;
}

void CHarddisk::SetDisk(const CString& NewDisk) {
  if (!SetCurrentDirectory(NewDisk)) {
    error("invalid directory \"%s\"", (LPCSTR)NewDisk);
  }
  int iSize = GetCurrentDirectory(0, NULL);
  GetCurrentDirectory(iSize, Dir.GetBuffer(iSize));
  Dir.ReleaseBuffer();
}

void CHarddisk::Command() {


}

static int Listen() {
  CHarddisk* p;
  __asm mov p,ECX
  p->eState = eListen;
  return 0;
}

static int ListenCh() {
  CHarddisk* p;
  __asm mov p,ECX
  int iChannel;
  __asm mov iChannel,EAX
  if (p->eState != eListen) {
    return TIMEOUT_RECEIVE;
  }
  p->iCh = iChannel & 15;
  if (p->iCh == 15) {
    p->eState = eCommand;
    p->iIn = 0;
    return 0;
  }
  if (iChannel & 128) {
    if (iChannel & 16) {
      p->eState = eOpen;
    } else {
      p->eState = eClose;
    }
    p->iIn = 0;
  } else {
    p->eState = eReceive;
  }
  return 0;
}

static int Receive() {
  CHarddisk* p;
  __asm mov p,ECX
  int iData;
  __asm mov iData,EAX
  switch (p->eState) {
  case eOpen:
  case eClose:
  case eCommand:
    if (p->iIn >= 80) {
      return TIMEOUT_RECEIVE;
    }
    p->acCommand[p->iIn++] = (char)iData;
    return 0;
  case eReceive:
    return p->aCh[p->iCh].Put(iData);
  }
  return TIMEOUT_RECEIVE;
}

static int Unlisten() {
  CHarddisk* p;
  __asm mov p,ECX
  switch (p->eState) {
  case eOpen:
    try {
      p->aCh[p->iCh].Open(p->Dir, p->acCommand, p->iIn, p->iCh);
    } catch (char* pcError) {
      p->SetError(pcError);
    }
    break;
  case eClose:
    try {
      p->aCh[p->iCh].Close();
    } catch (char* pcError) {
      p->SetError(pcError);
    }
    break;
  case eCommand:
    try {
      p->Command();
    } catch (char* pcError) {
      p->SetError(pcError);
    }
    break;
  }
  p->eState = eIdle;
  return 0;
}

int Talk() {
  CHarddisk* p;
  __asm mov p,ECX
  p->eState = eTalk;
  return 0;
}

static int TalkCh() {
  CHarddisk* p;
  __asm mov p,ECX
  int iChannel;
  __asm mov iChannel,EAX
  if (p->eState != eTalk) {
    return TIMEOUT_SEND;
  }
  p->iCh = iChannel & 15;
  if (p->iCh == 15) {
    p->eState = eError;
    return 0;
  }
  p->eState = eSend;
  return 0;
}

static int Send() {
  CHarddisk* p;
  __asm mov p,ECX
  int iReturn;
  switch (p->eState) {
  case eError:
    iReturn = (byte)p->acError[p->iOut++];
    if (iReturn == 0) {
      p->SetError("00, OK,00,00");
      iReturn = 13 | END_OF_FILE;
    }
    return iReturn;
  case eSend:
    return p->aCh[p->iCh].Get();
  }
  return TIMEOUT_SEND;
}

static int Untalk() {
  CHarddisk* p;
  __asm mov p,ECX
  p->eState = eIdle;
  return 0;
}


////////////////////////////////////////////////////////////////////////////////
// fast loading file into buffer

int CHarddisk::Load(CString Name, byte* pbBuffer, int iSize) {
  try {
    aCh[0].Open(Dir, Name, Name.GetLength(), 0);
    iSize = aCh[0].Read(pbBuffer, iSize);
    aCh[0].Close();
    return iSize;
  } catch (...) {
  }
  return 0;
}


/*
  int iCh;
  EState eState;
  char acCommand[80];
  int iIn;
  char acError[80];
  int iOut;
  CString Dir;
  CC64File aCh[15];
*/

CHarddisk::CHarddisk() {
  pfnListen = (pfnv)Listen;
  pfnListenCh = (pfnv)ListenCh;
  pfnReceive = (pfnv)Receive;
  pfnUnlisten = (pfnv)Unlisten;
  pfnTalk = (pfnv)Talk;
  pfnTalkCh = (pfnv)TalkCh;
  pfnSend = (pfnv)Send;
  pfnUntalk = (pfnv)Untalk;
  iCh = 15;
  eState = eIdle;
  iIn = 0;
  SetError("73,PC64 HARDDISK,00,00");
  Dir = GetDefaultDirectory();
};

CHarddisk::~CHarddisk() {
}
