/****************************************************************************/
/* Copyright 2010-2016 MBARI												*/
/****************************************************************************/
/* Summary	: Routines to save new config file for OASIS5					*/
/* Filename : cfgsave.c														*/
/* Author	: Robert Herlien (rah)											*/
/* Project	: Respirometer Upgrade											*/
/* Revision : $Revision: 1.1 $												*/
/* Created	: 05/14/2010													*/
/*																			*/
/* MBARI provides this documentation and code "as is", with no warranty,	*/
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium	*/
/* Research Institute to assist in its use, correction, modification, or	*/
/* enhancement. This information should not be published or distributed to	*/
/* third parties without specific written permission from MBARI.			*/
/*																			*/
/****************************************************************************/
/* Modification History:													*/
/* 14may2010 rah - created													*/
/* 23aug2016 rah - OASIS5 version created from OASIS3 cfgsave.c				*/
/*
/* $Log$
*/
/****************************************************************************/

#include <mbariTypes.h>			/* MBARI type definitions				*/
#include <mbariConst.h>			/* MBARI constants						*/
#include <oasis.h>				/* OASIS controller definitions			*/
#include <olist.h>				/* OASIS Linked List library			*/
#include <serial.h>				/* OASIS Serial I/O						*/		
#include <drvr.h>				/* OASIS driver functions				*/
#include <config.h>				/* OASIS Config file parser definitions */
#include <cfgsave.h>			/* OASIS Config file save routines		*/
#include <parm.h>				/* Parameter table definitions			*/
#include <file.h>				/* Oasis5 file I/O						*/
#include <utils.h>				/* OASIS utility functions				*/
#include <modbus.h>				/* Modbus protocol library				*/
#include <modbusio.h>			/* Modbus I/O							*/
#include <remote.h>				/* Remote I/O library					*/
#include <istdio.h>				/* Our (integer) standard I/O library	*/
#include <fatFs/ff.h>			/* FatFs file system defns				*/

#include <stdio.h>				/* Standard I/O library					*/
#include <stdlib.h>				/* Standard C library					*/
#include <stdarg.h>
#include <string.h>				/* Standard string library				*/
#include <ctype.h>				/* Standard ctypes						*/
#include <time.h>


/********************************/
/*		External Data	*/
/********************************/

Extern Parm_t			cfgSaves;		/* Number of OASISnnn.CFG files to save */
Extern const DrvDesc	*drvDescs[];	/* Driver descriptors					*/
Extern int				numDrvDescs;	/* Size of drvDescs array				*/
Extern LstHead			drv_list;		/* Driver list							*/
Extern char				*logTypes[];	/* Log type identifiers					*/
Extern int				numLogTypes;	/* Size of the log type array			*/
Extern RemSerPort		*serPorts[];	/* Remote serial ports					*/
Extern RemIOPort		*pwrPorts[];	/* Remote power ports					*/
Extern RemIOPort		*outPorts[];	/* Remote output ports					*/
Extern const Param		parmtbl[];		/* Parameter table						*/
Extern Nat32			numParmTbl;		/* Size of paramter table				*/
Extern const ParamArray parmArrayTbl[]; /* Parameter Array table				*/
Extern Nat32			numParmArrayTbl; /* Size of Paramter Array table		*/


/********************************/
/*		Module Local Data		*/
/********************************/

MLocal const char * const cfgFileName = "OASIS.CFG";
MLocal const char * const cfgTmpName = "OASISCFG.TMP";
MLocal const char * const cfgBackName = "OASIS%03u.CFG";
MLocal const char * const comment =
	"################################################################\r\n";


/************************************************************************/
/* Function	   : cserror												*/
/* Purpose	   : Error return function for this module					*/
/* Inputs	   : printf format and args									*/
/* Outputs	   : ERROR													*/
/************************************************************************/
MLocal CmdRtn cserror( const char *format, ... )
{
	va_list		ap;

	va_start(ap, format);
	giveFileSem();
	ivprintf(format, ap);
	va_end(ap);
	return(ERROR);

} /* cserror() */


/************************************************************************/
/* Function	   : csferror												*/
/* Purpose	   : Error return from a file function						*/
/* Inputs	   : FIL ptr												*/
/* Outputs	   : ERROR													*/
/************************************************************************/
MLocal Int16 csferror(FIL *fp)
{
	f_close(fp);
	giveFileSem();
	xputs("Error in f_printf, exiting\n");
	return(ERROR);

} /* csferror() */


/************************************************************************/
/* Function	   : writeHeader											*/
/* Purpose	   : Write the header (comments) to the new oasis.cfg file	*/
/* Inputs	   : File ptr												*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal Int16 writeHeader(FIL *fp)
{
	time_t		now;

	if (f_printf(fp, "#\r\n## OASIS5 Configuration File\r\n") < 0)
		return(csferror(fp));

	if (f_printf(fp, "##\r\n## Automatically generated due to User request\r\n## ") < 0)
		return(csferror(fp));

	now = clkTime();
	if (f_printf(fp, asctime(gmtime(&now))) < 0)
		return(csferror(fp));

	if (f_printf(fp, "#\r\n\r\n") < 0)
		return(csferror(fp));

	return(OK);

} /* writeHeader() */


/************************************************************************/
/* Function	   : findDrvrDesc											*/
/* Purpose	   : Find the DrvDesc matching an existing Driver			*/
/* Inputs	   : Driver Pointer											*/
/* Outputs	   : DrvDesc ptr or NULL									*/
/************************************************************************/
MLocal const DrvDesc *findDrvrDesc(Driver *dp)
{
	int			i;
	const DrvDesc **ddp;

	for (i = 0, ddp = &drvDescs[0] ; i < numDrvDescs; i++, ddp++)
		if (dp->drv_task == (*ddp)->dd_task)
			return(*ddp);

	return(NULL);
}

/************************************************************************/
/* Function	   : writeDrivers											*/
/* Purpose	   : Write the drivers to the new oasis.cfg file			*/
/* Inputs	   : File ptr												*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal CmdRtn writeDrivers(FIL *fp)
{
	Driver				*dp;
	const DrvDesc		*ddp;
	char				*logType;
	char				logTypeBuf[32];

	if (f_printf(fp, "%s#	Drivers\r\n%s", comment, comment) < 0)
		return(csferror(fp));

	for (dp = (Driver *)drv_list.lst_head; dp != DRV_NULL; dp = dp->drv_next)
		if ((ddp = findDrvrDesc(dp)) != NULL)
		{
			if (dp->drv_parms[SAMPLE_TYPE] < numLogTypes)
				logType = logTypes[dp->drv_parms[SAMPLE_TYPE]];
			else
			{			
				sprintf(logTypeBuf, "%u", dp->drv_parms[SAMPLE_TYPE]);
				logType = logTypeBuf;
			}
			if (f_printf(fp, "driver = %s,%s,%d,%d,%d,0x%x,%d,%s,0x%x,%d,%d,%d,%d\r\n\r\n",
						 ddp->dd_name, dp->drv_name, dp->drv_parms[INTERVAL],
						 dp->drv_parms[SER_PORT], dp->drv_parms[SER_BAUD], 
						 dp->drv_parms[SER_SETUP], dp->drv_parms[FLAGS],
						 logType, dp->drv_parms[TOD_MASK],
						 dp->drv_parms[TIMEOUT], dp->drv_parms[PARM0],
						 dp->drv_parms[PARM1], dp->drv_parms[PARM2]) < 0)
				return(csferror(fp));
		}

	f_printf(fp, "\r\n");
	return(OK);

} /* writeDrivers() */


/************************************************************************/
/* Function	   : writeModbus											*/
/* Purpose	   : Write the ModBus configuration to the new oasis.cfg file*/
/* Inputs	   : File ptr												*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal CmdRtn writeModbus(FIL *fp)
{
	Int16		i;
	ModBusStruct *mbp;

	if (f_printf(fp, "%s#	ModBus\r\n%s", comment, comment) < 0)
		return(csferror(fp));

	for (i = 1, mbp = getSerModStruct() + 1; i < PIC_UARTS; i++, mbp++)
		if (mbp->ident == IDENT)
			if (f_printf(fp, "modbus = %u,%u,0x%x\r\n",
						 i, mbp->baud, mbp->sermode) < 0)
				return(csferror(fp));
	
	f_printf(fp, "\r\n\r\n");
	return(OK);

} /* writeModBus() */


/************************************************************************/
/* Function	   : writeRemote											*/
/* Purpose	   : Write the Remote configuration to the new oasis.cfg file*/
/* Inputs	   : File ptr												*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal CmdRtn writeRemote(FIL *fp)
{
	Int16		i;
	RemSerPort	*rsp;
	RemIOPort	*rp;

	if (f_printf(fp, "%s#	Virtual I/O Ports\r\n%s", comment, comment) < 0)
		return(csferror(fp));

	for (i = 0; i < VIRT_SER_PORTS; i++)
	{
		rsp = serPorts[i];
		if (rsp && rsp->id && (rsp->id->busType != MB_UNASSIGNED))
			if (f_printf(fp, "serial = %u,%u,%u,%u\r\n",
						 i+PIC_UARTS, rsp->id->bus, rsp->id->slaveID, rsp->port) < 0)
				return(csferror(fp));
	}

	f_printf(fp, "\r\n");

	for (i = 0; i < VIRT_PWR_PORTS; i++)
	{
		rp = pwrPorts[i];
		if (rp && rp->id && (rp->id->busType != MB_UNASSIGNED))
			if (f_printf(fp, "power = %u,%u,%u,%u\r\n",
						 i+PHYS_PWR_PORTS, rp->id->bus, rp->id->slaveID, rp->coil) < 0)
				return(csferror(fp));
	}

	f_printf(fp, "\r\n");

	for (i = 0; i < VIRT_OUT_PORTS; i++)
	{
		rp = outPorts[i];
		if (rp && rp->id && (rp->id->busType != MB_UNASSIGNED))
			if (f_printf(fp, "output = %u,%u,%u,%u\r\n",
						 i, rp->id->bus, rp->id->slaveID, rp->coil) < 0)
				return(csferror(fp));
	}

	f_printf(fp, "\r\n\r\n");
	return(OK);

} /* writeRemote() */


/************************************************************************/
/* Function	   : writeParms												*/
/* Purpose	   : Write the global parameters to the new oasis.cfg file	*/
/* Inputs	   : File ptr												*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal CmdRtn writeParms(FIL *fp)
{
	Int16		i, j;
	int			rtn;
	Parm_t		*p;
	const Param *paramP;
	const ParamArray	*paramAP;

	if (f_printf(fp, "%s#	Global Parameters\r\n%s", comment, comment) < 0)
		return(csferror(fp));

	for (i = 0, paramP = parmtbl; i < numParmTbl; i++, paramP++)
		if ((paramP->pt_flags & PARM_IMMUTABLE) == 0)
		{
			switch(paramP->pt_type)
			{
			  case PARM_DEC:
				  rtn = f_printf(fp, "%s = %d\r\n", paramP->pt_name,
								 *(Parm_t *)(paramP->pt_parm));
				  break;

			  case PARM_HEX:
				  rtn = f_printf(fp, "%s = 0x%x\r\n", paramP->pt_name,
								 *(Parm_t *)(paramP->pt_parm));
				  break;

			  case PARM_STRING:
				  if ((*(char **)(paramP->pt_parm)) != NULL)
					  rtn = f_printf(fp, "%s = %s\r\n",  paramP->pt_name, 
									 *(char **)(paramP->pt_parm));
				  break;

			  case PARM_BOOL:
				  rtn = f_printf(fp, "%s = %s\r\n",  paramP->pt_name,
								 (*(MBool *)(paramP->pt_parm)) ? "TRUE" : "FALSE");
				  break;
			}

			if (rtn < 0)
				return(csferror(fp));
		}

	f_printf(fp, "\r\n");

	for (i = 0, paramAP = parmArrayTbl; i < numParmArrayTbl; i++, paramAP++)
	{
		f_printf(fp, "array = %s", paramAP->pt_name);
		p = (Parm_t *)(paramAP->pt_array);
		for (j = 0; j < paramAP->pt_size; j++)
			f_printf(fp, ",%d", *p++);
		f_printf(fp, "\r\n");
	}

	f_printf(fp, "\r\n\r\n");
	return(OK);

} /* writeParms() */


/************************************************************************/
/* Function	   : renameFiles											*/
/* Purpose	   : Rename backup files									*/
/* Inputs	   : None													*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal Int16 renameFiles(Void)
{
	char		nameBuf[64], oldName[64];
	Int32		i;

	sprintf(nameBuf, cfgBackName, cfgSaves);
	f_unlink(nameBuf);

	for (i = cfgSaves-1; i > 0; i--)
	{
		sprintf(oldName, cfgBackName, i);
		sprintf(nameBuf, cfgBackName, i+1);
		f_unlink(nameBuf);
		f_rename(oldName, nameBuf);
	}

	sprintf(nameBuf, cfgBackName, 1);
	if (f_rename(cfgFileName, nameBuf) != FR_OK)
		return(cserror("Could not rename %s\n", cfgFileName));

	if (f_rename(cfgTmpName, cfgFileName) != FR_OK)
		return(cserror("Could not rename %s\n", cfgTmpName));

	return(OK);

} /* renameFiles() */


/************************************************************************/
/* Function	   : usrSaveCfg												*/
/* Purpose	   : User command to save the config file					*/
/* Inputs	   : None													*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
CmdRtn usrSaveCfg(void)
{
	FIL		cfgFp;
	CmdRtn	rtn;

	takeFileSem();

	if (f_open(&cfgFp, cfgTmpName, (BYTE)(FA_WRITE | FA_CREATE_ALWAYS)) != FR_OK)
		return(cserror("Cannot create %s\n", cfgTmpName));

	if (writeHeader(&cfgFp) != OK)
		return(csferror(&cfgFp));

	writeModbus(&cfgFp);
	writeRemote(&cfgFp);
	writeDrivers(&cfgFp);
	writeParms(&cfgFp);

	if (f_close(&cfgFp) != FR_OK)
		return(cserror("Error on file close\n"));

	rtn = renameFiles();
	giveFileSem();
	return(rtn);

} /* usrSaveCfg() */
