/*
 * Copyright (c) 2019 Clementine Computing LLC.
 *
 * This file is part of PopuFare.
 * 
 * PopuFare is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * PopuFare is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with PopuFare.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

#include "common_config.h"

#ifndef _COMMON_DEFS
#define _COMMON_DEFS


//------------------------------- PROCESS DEATH MANAGEMENT ----------------------

#ifdef USE_WATCHDOG_ALARM
  
//    #define WATCHDOG_DEBUG

    #ifdef WATCHDOG_DEBUG
        #define WD_DEBUG_MSG(cmd, x)		printf("WD %s (%d seconds left)\n", (cmd), (x))
    #else
        #define WD_DEBUG_MSG(cmd, x)		(x)
    #endif
  
#endif

//------------------- Watchdog Timer Related
#ifdef USE_WATCHDOG_ALARM
  #define RESET_WATCHDOG()		{WD_DEBUG_MSG("reset", alarm(MODULE_WATCHDOG_VALUE));}
#else
  #define RESET_WATCHDOG()		{/*nop*/}
#endif
//-------------------
#ifdef USE_WATCHDOG_ALARM
  #define RESET_WATCHDOG_LONG(x)		{WD_DEBUG_MSG("long_reset", alarm(MODULE_WATCHDOG_VALUE * (x)));}
#else
  #define RESET_WATCHDOG_LONG(x)		{/*nop*/}
#endif
//---------
#ifdef USE_WATCHDOG_ALARM
  #define STOP_WATCHDOG()		{WD_DEBUG_MSG("stopped",alarm(0));}
#else
  #define STOP_WATCHDOG()		{/*nop*/}
#endif
//----------


#define EXIT_REQUEST_NONE		(0)	//no exit is requested
#define EXIT_REQUEST_POLITE		(1)	//we have an IPC message asking us to wind down
#define EXIT_REQUEST_INT_TERM		(2)	//we caught a SIGINT or SIGTERM
#define EXIT_REQUEST_CRASH		(4)	//the watchdog timer has overflowed, or a SEGV has occurred

//This variable is populated by the signal handlers, and should be checked in main while loops.
extern volatile int exit_request_status;

//This flag says there is a pending HUP request
extern volatile int hup_request_status;

//This function requests a config reload / HUP request, debug-logging its arguments printf-style
void request_hup(char *fmt, ...);

//This function requests a polite exit (OR-ing its reason into exit_status_request).
//It also takes printf-style arguments to log the reason for the exit if it has to force
//exit.
void request_polite_exit(int reason, char *fmt, ...);

//This function sets up signal handlers to log any occurances of the serious "crash" type errors
//like a watchdog expiration, or the mean four (SIGSEGV, SIGILL, SIGFPE, SIGBUS).
//
//It also sets up signal handlers for SIGINT and SIGTERM to politely request an exit
//
//  You should always call this function from main() with argv[0] as its argument so
//that it can register with syslog so any future messages will come with the correct
//module name attached.
//
void configure_signal_handlers(char *procname);

//------------------- database operation returns
#define OKAY_MIN		(0)

#define	FAIL_PARAM		(-1)
#define	FAIL_DATABASE		(-2)
#define	FAIL_DUPKEY		(-3)
#define	FAIL_FULL		(-4)
#define FAIL_MEM		(-5)

#define FAIL_MAX		(-1)
#define FAIL_MIN		(-99)

#define WARN_NOTFOUND		(-100)
#define WARN_NOCHANGE		(-101)

#define WARN_MAX		(-100)
#define WARN_MIN		(-199)

#define DB_OKAY(x)		( (x) >= 0 )
#define DB_FAIL(x)		( ((x) <= FAIL_MAX) && ((x) > FAIL_MIN) )
#define DB_WARN(x)		( ((x) <= WARN_MAX) && ((x) > WARN_MIN) )

//----------------- common utility functions

//Extracts the first tab delimited field from src and puts it in dest,
//returning the number of characters consumed and optionally filling
//*eol_flag with the end-of-line state of the returned field.
int get_field(char *dest, char *src, int dest_len, int *eol_flag);

//Returns an unsigned long long hash value for a string.
unsigned long long stringhash(char *string);

//This function takes a string and removes any CR or LF characters from it
//(deleting them out of the middle if needed).  Note, it DOES mangle its input.
int strip_crlf(char *buffer);

//This function opens the TTY device named by devname at the speed
//specified by custon_baud (see sys/termios.h for values).  Passing 0
//to custom_baud uses the system default of 115200bps.
//The linemode flag determines whether the port will be treated as a raw
//serial strem (linemode = 0) or a line-buffered terminal (linemode = 1).
//
//    When linemode = 1 read() will return a whole line at a time, and
//  poll() and select() will not unblock on input from this device until
//  either A) a whole line is available, or B) it's been ACCUM_SECONDS 
//  seconds since the last character has been received.
//
int open_rs232_device(char *devname, int custom_baud, int linemode);

//Defines for the above

#define USE_DEFAULT_BAUD	(0)

#define RS232_RAW		(0)
#define RS232_LINE		(1)


typedef struct device_test_vector_struct
{
    char 	*dev_id;		//Expected Device ID returned in the "/?: ?=device_id" message from device
    char 	*init_string;		//Initialization String to send to device (Example "/0:Foo Bar\n/m:09 00 03 42 5A 44 56\r")
    int 	n_reply_lines;		//Number of expected lines coming back from device...
    char 	**reply_strings;	//An optional list of n_reply_lines where NULLs mean "don't care" and non-null 
                                        //means reply line N much match supplied string (sans pre '/' garbage and 
                                        //trailing '\r' and '\n' characters. Example:
                                        // {NULL, "/OK:m:09 00 03 42 5A 44 56", "/M:^"} means line 0 == Don't care, lines 1 and 2 must match their spec
                                        
    int 	init_tries;		//Number of times to try getting device ID		(0 == use default)
    int		init_timeout;		//Number of milliseconds to wait for device ID reply.	(0 == use default)

} device_test_vector;


/*
device_test_vector sample_dev_test_vector = 
{
    .dev_id 		= "rider_ui",
    .init_string	= "/0:\r/1:\r/m:09 00 03 42 5A 44 56\r",
    .n_reply_lines	= 4, 
    .reply_strings	= (char *[]){NULL, NULL, "/OK:m:09 00 03 42 5A 44 56", "/M:^"},
};

*/

#define DEV_INIT_BUFFER_SIZE	(256)
#define DIAG_BUFFER_SIZE	(DEV_INIT_BUFFER_SIZE * 4)

//This function takes a file descriptor, a pointer to a test vector structure,
//and an optional diagnostic output string (this string must be at least DIAG_BUFFER_SIZE
//characters in size).
//
// The routine first sends a "\r" to the device to prompt it to send a device ID line.
// This is retried until either the maximum number of retries is reached or a reply is received.
//
// The device ID is then checked again the expected ID from the test vector.  If it is incorrect,
// failure is returned.  If it is correct, the device is then flushed and the initialization specified
// in the test vector is performed.
//
// If this initialization succeeds, the call returns 0, wheras a negative return value is returned in the
// case of failure.
//
int test_and_init_device(int fd, device_test_vector *vec, char *diag);


#define PKG_STRING_SIZE (64)
#define PKG_STRING_FORMAT "%63s"	//for use in sscanf format string.  Eeew...

typedef struct package_signature_struct
{
    char pkgname[PKG_STRING_SIZE];
    char pkgver[PKG_STRING_SIZE];
    char checksum[PKG_STRING_SIZE];
    time_t installed;

} package_signature;
                

//    This function fills up to n package_signature structures with the name, version, md5 checksum, and install time of
//installed packages as gleaned from the package management dropfiles in the configuration directory.  This is useful for
//status screens and diagnostic purposes.
int find_packages(package_signature *pkgs, int n);

//This function checks the dropfile to see if the tunnel is up...
int tunnel_is_up();

//This function checks the dropfile to see if the GPRS modem is up...
int gprs_is_up();

//This function gets the equipment number from the appropriate config file
//If it cannot be gotten, it returns -1
int get_equip_num();

//This function sets the equipment number in the correct configuration file
int set_equip_num(int num);


#define SERVER_DESC_LEN	(64)
#define MAX_SERVER_LIST	(16)

//This ges the current server description into the supplied buffer up to N characters
int get_server_desc(char *desc, int len);

#endif

