#include "IbcCardManager.h"
#include "IbcMicroSampler.h"
#include "vxUtils.h"
#include "DmErrno.h"
#include "ibcItemNames.h"

IbcCardManager::IbcCardManager(const char *systemName,
			       const char *appName,
			       sio32Chan *sio32Channel,
			       Word cpuAddress)
{
  _appName = (const char *)strdup(appName);
  _systemName = strdup(systemName);
  
  IbcCard::setSystemName(systemName);
  IbcCard::setAppName(appName);  
    
  sprintf(_appDmPrefix, "%s.%s", systemName, appName);
  sprintf(_powerDmPrefix, "%s.%s.%s", systemName, PowerMgrAppName, appName);
  
  _sio32Chan = sio32Channel;

  _nLpsCards = _nHpsCards = _nVicorCards = 
    _nGf5vCards = _nIsolatedIoCards = _nQuadSerialCards = 0;

  _cardConfig.numCards = 0;
  
  cpuCard = new CpuCard(CpuName, cpuAddress, _sio32Chan, &_cardConfig);
  if (cpuCard->error())
  {
    char errorBuf[errorMsgSize];
    cpuCard->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  cards.add((void **)&cpuCard);
  
  addCardAddress(cpuAddress);

  _ibcSwitchChannel = NULL;

  _powerNameGenerator = 0;

  return;
}


IbcCardManager::~IbcCardManager()
{
  free((void *)_appName);
  free((void *)_systemName);
  delete _powerNameGenerator;
}


ExternalSwitch *IbcCardManager::addExternalSwitch(const char *cardName,
						  const char *switchReqName,
						  const char *switchStatusName,
						  MBool isIbcPowerSwitch)
{
  ExternalSwitch *externalSwitch = 
    new ExternalSwitch(cardName, switchReqName, switchStatusName, 
		       _sio32Chan);

  if (externalSwitch->error())
  {
    char errorBuf[errorMsgSize];
    externalSwitch->errorMsg(errorBuf);
    setError(errorBuf);
  }
  
  if (isIbcPowerSwitch)
  {
    if (!_ibcSwitchChannel)
      _ibcSwitchChannel = externalSwitch->switchChannel();
    else
    {
      setError("IbcCardManager::addExternalSwitch() - "
	       "IBC power switch already defined");

#if 0
      throw IbcAppError(IbcAppError::General, cardName,
			"IbcCardManager::addExternalSwitch() - "
			"IBC power switch already defined");
#endif

    }
  }
  cards.add((void **)&externalSwitch);
  return externalSwitch;
}


QuadSerialCard *IbcCardManager::addQuadSerial(const char *cardName, 
					      Word cardAddr)
{
  addCardAddress(cardAddr);

  QuadSerialCard *card = 
    new QuadSerialCard(cardName, cardAddr, _sio32Chan, _nQuadSerialCards++);

  if (card->error())
  {
    char errorBuf[errorMsgSize];
    card->errorMsg(errorBuf);
    setError(errorBuf);
  }

  cards.add((void **)&card);
  return card;
}


QuadAtoDCard *IbcCardManager::addQuadAtoD(const char *cardName, 
					  Word cardAddr,
					  Word getAtoDCmd) 
{
  MBool debug = FALSE;
  
  addCardAddress(cardAddr);
  QuadAtoDCard *card = new QuadAtoDCard(cardName, cardAddr, _sio32Chan,
					getAtoDCmd);

  char errorBuf[errorMsgSize];
  
  if (card->error())
  {
    card->errorMsg(errorBuf);
    setError(errorBuf);
    dprintf("IbcCardManager::addGf5v() - %s", errorBuf);
  }

  cards.add((void **)&card);
  return card;  
}


Gf5vCard *IbcCardManager::addGf5v(const char *cardName, Word cardAddr)
{
  MBool debug = FALSE;
  
  addCardAddress(cardAddr);
  
  Gf5vCard *card = new Gf5vCard(cardName, cardAddr, _sio32Chan, _nGf5vCards++);

  char errorBuf[errorMsgSize];
  
  if (card->error())
  {
    card->errorMsg(errorBuf);
    setError(errorBuf);
    dprintf("IbcCardManager::addGf5v() - %s", errorBuf);
  }

  cards.add((void **)&card);
  return card;
}



IsolatedIoCard *IbcCardManager::addIsolatedIo(const char *cardName, 
					      Word cardAddr,
					      SwitchChannel *parent,
					      SystemBus bus,
					      LoadDivision division,
					      MBool positiveLogic)
{
  addCardAddress(cardAddr);
  
  IsolatedIoCard *isolatedIo = 
    new IsolatedIoCard(cardName, cardAddr, _sio32Chan, _nIsolatedIoCards,
		       parent, bus, division, positiveLogic);

  char errorBuf[errorMsgSize];
  
  if (isolatedIo->error())
  {
    isolatedIo->errorMsg(errorBuf);
    setError(errorBuf);
  }

  cards.add((void **)&isolatedIo);
  return isolatedIo;
}



VicorLpsCard *IbcCardManager::addVicorLps(const char *cardName,
					  Word cardAddr,
					  SwitchChannel *parent,
					  SystemBus bus,
					  LoadDivision division,
					  Nat16 vicorPower,
					  SwitchMethod vicorSwitchMethod,
					  SwitchStatus defaultVicorStatus)

{
  addCardAddress(cardAddr);

  VicorLpsCard *card = 
    new VicorLpsCard(cardName, cardAddr, _sio32Chan, _nLpsCards++, 
		     parent, bus, division, vicorPower,
		     vicorSwitchMethod, defaultVicorStatus); 
  
  if (card->error())
  {
    char errorBuf[errorMsgSize];
    card->errorMsg(errorBuf);
    setError(errorBuf);
  }
  
  
  cards.add((void **)&card);
  return card;
}


VicorCard *IbcCardManager::addVicor(const char *cardName,
				    Word cardAddr,
				    SwitchChannel *parent,
				    SystemBus bus,
				    LoadDivision division)
{
  addCardAddress(cardAddr);

  VicorCard *card = 
    new VicorCard(cardName, cardAddr, _sio32Chan, _nVicorCards++, 
		  parent, bus, division); 

  if (card->error())
  {
    char errorBuf[errorMsgSize];
    card->errorMsg(errorBuf);
    setError(errorBuf);
  }
  
  cards.add((void **)&card);
  return card;
}


LpsCard *IbcCardManager::addLps(const char *cardName,
				Word cardAddr,
				SwitchChannel *parent,
				SystemBus bus,
				LoadDivision division)

{
  addCardAddress(cardAddr);

  LpsCard *card = new LpsCard(cardName, cardAddr, _sio32Chan, _nLpsCards++, 
			      parent, bus, division);
  
  if (card->error())
  {
    char errorBuf[errorMsgSize];
    card->errorMsg(errorBuf);
    setError(errorBuf);
  }
  
  cards.add((void **)&card);

  return card;
}


HpsCard *IbcCardManager::addHps(const char *cardName,
				Word cardAddr,
				SwitchChannel *parent,
				Nat16 power,
				SystemBus bus,
				LoadDivision division,
				SwitchMethod switchMethod)
{
  MBool debug = FALSE;
  
  addCardAddress(cardAddr);

  HpsCard *card = new HpsCard(cardName, cardAddr, _sio32Chan, _nHpsCards++, 
			      parent, power, bus, division, switchMethod);

  if (card->error())
  {
    char errorBuf[errorMsgSize];
    card->errorMsg(errorBuf);
    setError(errorBuf);
  }

  cards.add((void **)&card);
  return card;
}



STATUS IbcCardManager::startMonitoring(DmMonitor *dmMonitor)
{
  STATUS status;
  IbcCard *card;

  for (int i = 0; i < cards.size(); i++)
  {
    cards.get(i, (void **)&card);

    if ((status = card->startMonitoring(dmMonitor)) != SUCCESS)
    {
      char errorBuf[errorMsgSize];
      sprintf(errorBuf, "startMonitoring() - error from card %s", 
	      card->name());
      
      setError(errorBuf);
    }
  }
  return OK;
}


MBool IbcCardManager::hasExternalSwitch()
{
  if (_ibcSwitchChannel)
    return TRUE;
  else
    return FALSE;
}


switchStatus IbcCardManager::powerSwitchStatus()
{
  if (_ibcSwitchChannel)
  {
    switchStatus status;
    Errno err;
    
    // Check IBC power switch status
    if ((err = dm_read(_ibcSwitchChannel->entry()->switchStatusDm,
		       &status, sizeof(switchStatus), 
		       (DM_Time *)NULL)) != SUCCESS)
    {
      static char errorBuf[errorMsgSize];
      sprintDmError(err, "IbcCardManager::powerSwitchStatus(), dm_read()", 
		    errorBuf);

      logMsg(errorBuf);
      return SWITCH_ON;
    }
    else
      return status;
  }
  else
    return SWITCH_ON;
}


STATUS IbcCardManager::processDmChanges(DmGroup *dmGroup)
{
  IbcCard *card;
  STATUS status;
  
  for (int i = 0; i < cards.size(); i++)
  {
    cards.get(i, (void **)&card);
    card->processDmChanges(dmGroup);
  }

  return OK;
}


STATUS IbcCardManager::resetSwitches()
{
  IbcCard *card;
  STATUS status;
  
  for (int i = 0; i < cards.size(); i++)
  {
    cards.get(i, (void **)&card);

    if (card->isSwitchCard())
    {
      SwitchCard *switchCard = (SwitchCard *)card;
      switchCard->setDefaultStatus();
    }      
  }
  return OK;
}


int IbcCardManager::buildSwitches(SEM_ID dmUpdateSem, SEM_ID powerStatusSem)
{
  MBool debug = FALSE;

  for (int i = 0; i < cards.size(); i++)
  {
    IbcCard *card;
    cards.get(i, (void **)&card);
    
    if (card->isSwitchCard())
    {
      dprintf("IbcCardManager::buildSwitches() - build card %s\n", 
	      card->name());
      
      ((SwitchCard *)card)->buildSwitch(dmUpdateSem, powerStatusSem,
					_powerNameGenerator);
      
      dprintf("IbcCardManager::buildSwitches() - cards.size() = %d\n", 
	      cards.size());
    }
  }

  // Log DataManager item names to file
  if (_powerNameGenerator)
    _powerNameGenerator->printNames();

  return 0;
}


MBool IbcCardManager::handleSrq(Word requestCode, Byte *packet)
{
  MBool debug = FALSE;

  dprintf("IbcCardManager::handleSrq(); code=0x%x\n", requestCode);

  for (int i = 0; i < cards.size(); i++)
  {
    IbcCard *card;
    cards.get(i, (void **)&card);
    dprintf("IbcCardManager::handleSrq() - call handleSrq() for card %s\n", 
	    card->name());

    if (card->handleSrq(requestCode, packet)) {

      dprintf("IbcCardManager::handleSrq() - handled by card \"%s\"\n",
	      card->name());

      // return TRUE;
    }
  }
  return FALSE;
}


STATUS IbcCardManager::handleResetSrq(TaskSynchronizer *taskSync)
{
  MBool debug = FALSE;
  if (cpuCard->handleResetSrq() == OK)
  {
    // CpuCard::handleResetSrq() worked - so process remaining cards
    for (int i = 1; i < cards.size(); i++)
    {
      IbcCard *card;
      cards.get(i, (void **)&card);
      dprintf("handleResetSrq() for card \"%s\"...", card->name());
      card->handleResetSrq();
      dprintf("done.\n");
    }
  }
  else
  {
    taskSync->setDeviceLinkState(DeviceInterface::LinkDown);
  }

  return OK;
}


STATUS IbcCardManager::handleResetSemaphore()
{
  IbcCard *card;
  
  for (int i = 0; i < cards.size(); i++)
  {
    cards.get(i, (void **)&card);

    if (card->isSwitchCard())
    {
      // Set switch to default state
      ((SwitchCard *)card)->setDefaultStatus();
    }

    card->handleResetEvent();
  }
}


STATUS IbcCardManager::sampleMicro(int millisec)
{
  IbcCard *card;

  MBool debug = FALSE;
  
  for (int i = 0; i < cards.size(); i++)
  {
    cards.get(i, (void **)&card);
    if (card->sampleInterval() == millisec)
    {
      dprintf("IbcCardManager::sampleMicro() - sample card \"%s\"\n", 
	      card->name()); 

      if (card->sampleMicro() != OK)
      {
	logMsg("IbcCardManager::sampleMicro() - error sampling card \"%s\"\n",
	       card->name());
      }
    }
  }  
}



int IbcCardManager::addCardAddress(Word address)
{
  MBool debug = FALSE;
  
  dprintf("IbcCardManager::addCardAddress() - numCards=%d\n", 
	  _cardConfig.numCards);
  
  if (_cardConfig.numCards == IBC_MAX_CARDS)
    return -1;
  
  _cardConfig.address[_cardConfig.numCards++] = address;
}


void IbcCardManager::logPowerMgrNames(const char *fileName)
{
  _powerNameGenerator = new PowerNameGenerator(_systemName, _appName, 
					       fileName);
}
