#include <stdio.h>
#include <string.h>
#include "MicroApp.h"
#include "vxUtils.h"
#include "DmErrno.h"

extern "C" 
{
# include "microcmd.h"
# include "usrTime.h"
};



MicroApp::MicroApp(const char *systemName,
		   const char *appName,
		   DeviceInterface *deviceInterface)
{
  MBool debug = FALSE;
  dprintf("MicroApp::MicroApp()\n");
  
  _systemName = strdup(systemName);
  _appName = strdup(appName);

  char dmPrefix[MaxDmNameLen];
  sprintf(dmPrefix, "%s.%s.", _systemName, _appName);

  microDm = new MicroDm("microDm", dmPrefix);

  Errno err;
  char errorBuf[512];

  if ((err = microDm->startProviding(DM_STATIC)) != SUCCESS) {
    sprintDmError(err, "MicroApp::MicroApp() - microDm->startProviding", 
		  errorBuf);

    logMsg(errorBuf);
  }

  device = deviceInterface;
  _serialChanNo = device->channelNo();
  _microSampler = 0;
  _dmMonitor = 0;
  taskSync = 0;

  // Initialize task params to something reasonable
  _dmMonitorParams.priority = 66;
  _dmMonitorParams.floatingPoint = TRUE;
  _dmMonitorParams.stackSize = 5500;

  _microSamplerParams.priority = 65;
  _microSamplerParams.floatingPoint = TRUE;
  _microSamplerParams.stackSize = 5500;

}


MicroApp::~MicroApp()
{
  free((void *)_appName);
  free((void *)_systemName);

  delete taskSync;
  delete _microSampler;
  delete _dmMonitor;
}


int MicroApp::run()
{
  MBool debug = TRUE;
  dprintf("MicroApp::run()\n");

  if (initialize() == -1)
  {
    logMsg("MicroApp::run() - initialize() failed");
    return -1;
  }

  // DEBUG DEBUG DEBUG
  // taskSuspend(0);
  
  // Create and run child tasks
  if (spawnTasks() == -1)
  {
    logMsg("MicroApp::run() - spawnTasks() failed");
    return -1;
  }

  // When task is first starting, pretend there has been a micro reset
  if (device->powerSwitchStatus() == SWITCH_ON)
    processMicroReset();
  
  while (TRUE)
  {
    switchStatus devicePower = device->powerSwitchStatus() ;

    Byte srqPacket[SRQ_PACKET_LEN];
    int nBytes;
    
    if ((nBytes = device->readSRQ(srqPacket, SRQ_PACKET_LEN, WAIT_FOREVER)) 
	== ERROR)
    {
      logMsg("MicroApp::run() - device->readSRQ() failed");
      continue;
    }
    
    Word srqCode = device->getWord(&srqPacket, 0);
    
    dprintf("MicroApp::run() - got SRQ %x\n", srqCode);
    
    if (srqCode == MICRO_RESET_SRQ)
    {
      if (processMicroReset() == 0)
	taskSync->setDeviceLinkState(DeviceInterface::LinkUp);
    }
    
    // Application-defined processing
    processSrq(srqPacket, nBytes);
  }
}


DeviceInterface::LinkState MicroApp::processMicroReset()
{
  MBool debug = TRUE;
  dprintf("MicroApp::processMicroReset()\n");
  
  if (taskSync->deviceLinkState() == DeviceInterface::LinkUp)
  {
    DM_Time t;
    gettimeofday(&t, NULL);
    microDm->resetEvent->write(&t);
  }  

  taskSync->setDeviceLinkState(DeviceInterface::LinkDown);

  if (taskSync->handleDeviceReset() == 0)
    // Successfully handled reset; wakeup DmMonitor task
    taskSync->semGive(DmMonitorTask);
  
  return taskSync->deviceLinkState();
}


int MicroApp::spawnTasks()
{
  MBool debug = TRUE;
  
  dprintf("MicroApp::spawnTasks()\n");
  
  // Set priority, stacksize, etc for tasks
  setTaskParams();
  
  Nat32 taskNo;
  char taskName[512];
  int options;
  
  sprintf(taskName, "%sMicroSampler", _appName);

  if (_microSamplerParams.floatingPoint)
    options = VX_FP_TASK;
  else
    options = 0;
  
  if ((taskNo = taskSpawn(taskName, 
			 _microSamplerParams.priority,
			 options,
			 _microSamplerParams.stackSize,
			 (FUNCPTR )&MicroApp::runMicroSampler, 
			 (int )this, 0, 0, 0, 0, 0, 0, 0, 0, 0)) != ERROR)
  {
    taskSync->setTaskId(MicroSamplerTask, taskNo);
  }    
  else
  {
    delete taskSync;
    return ERROR;
  } 

  sprintf(taskName, "%sDmMonitor", _appName);

  if (_dmMonitorParams.floatingPoint)
    options = VX_FP_TASK;
  else
    options = 0;

  if ((taskNo = taskSpawn(taskName, 
			  _dmMonitorParams.priority,
			  options,
			  _dmMonitorParams.stackSize,
			  (FUNCPTR )&MicroApp::runDmMonitor,
			  (int )this, 0, 0, 0, 0, 0, 0, 0, 0, 0)) != ERROR)
  {
    taskSync->setTaskId(DmMonitorTask, taskNo);
  }    
  else
  {
    delete taskSync;
    return ERROR;
  }
  return 0;
}


int MicroApp::runMicroSampler(MicroApp *app,
			      int dummy1,
			      int dummy2,
			      int dummy3,
			      int dummy4,
			      int dummy5,
			      int dummy6,
			      int dummy7,
			      int dummy8,
			      int dummy9)
{

  int ret;
  char errorBuf[100];

  MBool debug = TRUE;
  
  dprintf("MicroApp::runDmMonitor()\n");

  app->_microSampler = app->createMicroSampler();
  if (app->_microSampler->error())
  {
    logMsg("MicroApp::runMicroSampler() - MicroSampler constructor failed");
    return -1;
  }
  
  app->_microSampler->run();

  return 0;
}


int MicroApp::runDmMonitor(MicroApp *app,
			   int dummy1,
			   int dummy2,
			   int dummy3,
			   int dummy4,
			   int dummy5,
			   int dummy6,
			   int dummy7,
			   int dummy8,
			   int dummy9)
{
  int ret;
  char errorBuf[100];
  MBool debug = TRUE;
  
  dprintf("MicroApp::runDmMonitor()\n");
  
  app->_dmMonitor = app->createDmMonitor();
  if (app->_dmMonitor->error())
  {
    logMsg("MicroApp::runDmMonitor() - DmMonitorTask constructor error.\n");
    return -1;
  }
  
  app->_dmMonitor->run();
}


int MicroApp::initialize()
{
  if (!microDm)
  {
    logMsg("MicroApp::initialize() - microDm not created by application");
    return -1;
  }
  
  const int nTasks = 3;
  
  taskSync = new TaskSynchronizer(_appName, nTasks, microDm, device);
  if (taskSync->error())
  {
    logMsg("MicroApp::initialize() - taskSync constructor failed");
    return -1;
  }

  return 0;
}


STATUS MicroApp::enableMicro(int maxTries)
{
  // Try to enable IO channel
  int tries;
  for (tries = 0; tries < maxTries; tries++)
  {
    if (device->enable() != ERROR)
      return OK;

    taskDelay(sysClkRateGet() / 5);
  }

  return ERROR;
}



STATUS MicroApp::resetMicroDmItems()
{
  DM_Time t;
  gettimeofday(&t, NULL);
  
  Char str[256];
  
  // Update Datamanager items
  device->id(str);
  microDm->id->write((char *)str, &t);

  device->serialNo(str);
  microDm->microSerialNo->write((char *)str, &t);

  device->softwareRev(str);
  microDm->softwareRev->write((char *)str, &t);

  MBool result;
  device->ramTest(&result);
  
  microDm->ramTestStatus->write(result, &t);

  return OK;
}




