/*
 * 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/>.
 *
 */


//Check to make sure we haven't already defined these things
#ifndef _TCAT_COMMHUB_API
#define _TCAN_COMMHUB_API	(1)

#define MSG_NO_ACTION	(0)
#define MSG_ERROR	(-1)

#define MSG_SEND	(POLLOUT)
#define MSG_RECV	(POLLIN)


#define TOTAL_MESSAGE_SIZE	(0x1000)	//Each message can be a maximum of 4K including header

//This is the maximum lenght of a mailbox name (excluding the terminating NUL)
//The mailbox name field will always be one longer than this value to accomodate the NUL.
#define MAILBOX_NAME_MAX	(31)



//The message_header_record structure contains the timestamp, routing, and data size for a message
//being passed either client->server or server->client
struct message_header_record
{
    long long int usec_time;				//When the message was sent
    char mailbox_name[MAILBOX_NAME_MAX + 1];		//What mailbox was it sent to?
    unsigned int payload_length;			//How many octets of the payload are populated?
    pid_t sender;					//The PID of the sender
    int from_fd;					//The file descriptor of the IPC socket the message came in on
};

//This is convenient because it allows the total message length to be something sane including the header.
#define MAX_PAYLOAD_LENGTH	((TOTAL_MESSAGE_SIZE - sizeof(char)) - sizeof(struct message_header_record))

#define MAX_MODULE_NAME_LENGTH	(1024)


//The message_record structure is what is passed from client to server to send a message out to a mailbox
//The same structure is passed from the server to each client that has a subscription to the mailbox
struct message_record
{
    struct message_header_record header;
    unsigned char payload[MAX_PAYLOAD_LENGTH];
    unsigned char nul;
};

typedef struct message_record message;

//This defines the number of supported client sockets.  We will have to replace our flat lists with hashes or trees
//long before we hit this limit.  We would also need a hell of a lot more RAM than exists on a viper to have this
//many meaningful processes
#define NUM_SUPPORTED_CLIENTS	(128)

//The subscriber_record structure is a linear linked list of file descriptors represinting all of the
//clients who have subscribed to any given mailbox
struct subscriber_record
{
    int clientfd;
    pid_t pid;

    char progname[MAX_MODULE_NAME_LENGTH];

    struct subscriber_record *next;
};

typedef struct subscriber_record subscriber;

//The mailbox_record structure holds a mailbox name and a list of subscribers
struct mailbox_record
{
    char mailbox_name[MAILBOX_NAME_MAX + 1];
    struct subscriber_record *clients;
    
    struct mailbox_record *next;
};


//This function creates a connection to the message server and returns a file descriptor
//the progname parameter should be passed argv[0] to make it easier for anybody listening
//in on the server side to identify which module corresponds to which PID, but it is acceptable
//to pass NULL if this information is not available or this functionality not desired.
int connect_to_message_server(char *progname);

//This function gets the number of microseconds since epoch for storing in message headers and
//comparison to determine how timely a message is.
long long int get_usec_time();

//This function does a non-blocking poll to determine if the message passing socket is
//ready for input or output (depending on
//since it is much more efficient to block on the entire list of your I/O file descriptors
//and service any that cause you to unblock.
//
//      Returns:  MSG_NO_ACTION = no message
//                MSG_ERROR     = fatal error (passed fd is no good)
//
//                If the socket is ready, the return value will be
//                whatever combination of events specified in mask (MSG_SEND, MSG_RECV)
//                that is actually ready.
//
int message_socket_status(int message_socket_fd, int mask);

//this function will pack a mailbox address and a payload into the supplied message structure and
//set the correct timestamp.
int prepare_message(struct message_record *target, char *mailbox, void *payload, unsigned int payload_len);

//the send_message function will transmit a message and its header (trimmed to the specified payload length).
//zero will be returned on success, -1 on failure.
int send_message(int message_socket_fd, struct message_record *message);

//the get_message function will receive a message from the opposite end of the socket and place it in the
//supplied message structure incliding its header.  -1 is returned on failure, 0 on success.
int get_message(int message_socket_fd, struct message_record *message);

//This is a utility function to get the number of microseconds since the epoch
long long int get_usec_time();

//------------------------------------------------ SPECIAL MAILBOXES

#define MAILBOX_HELLO		"_HELLO"
#define MAILBOX_SUBSCRIBE	"_SUBSCRIBE"
#define MAILBOX_UNSUBSCRIBE	"_UNSUBSCRIBE"

#define MAILBOX_BROADCAST	"_BROADCAST"
#define MAILBOX_WIRETAP		"_WIRETAP"
#define MAILBOX_ERROR		"_ERROR"

//------------------------------------------------ MAILBOXES USED FOR COMMUNICATION BETWEEN MODULES

//--------- PROCESS STATUS RELATED
#define MAILBOX_PING		"_PING"			//A process should ALWAYS respond to this message.  The payload will be echoed back
                                                        //in the PONG message, so sticking a unique identifier (or sequence number) in there may be useful

#define MAILBOX_PONG		"_PONG"			//A PONG message is the appropriate respoonse to the above PING message.

#define MAILBOX_EXIT		"_EXIT"			//A process should catch this message and make a graceful exit

#define MAILBOX_HUP		"_HUP"			//A process should catch this and reload its config file

//--------- SYSTEM STATUS RELATED
#define MAILBOX_STATUS_REQUEST	"STATUS_REQ"		//Requests all modules report their status
#define MAILBOX_PASS_STATUS     "PASS_STATUS"		//This message indicates the current passdb status.  Payload is struct pass_status_struct
#define MAILBOX_BILL_STATUS     "BILL_STATUS"		//This message indicates the current billdb status.  Payload is struct bill_status_struct

#define MAILBOX_DRIVER_STATUS	"DRIVER_STATUS"		//This message indicates the current driver login status.  Payload is struct driver_status_struct
#define MAILBOX_GPS_STATUS	"GPS_STATUS"		//This mailbox indicates that there is a new GPS datum available.  Payload is struct gps_status_struct
#define MAILBOX_STOP_STATUS	"STOP_STATUS"		//This mailbox indicates that there is a new bus stop arrival event.  Payload is stop_status_struct
                                              


//--------- ACTION MESSAGES
#define MAILBOX_TOKEN_MAG       "TOKEN_MAG"		//A Magstripe has been presented.  Parameter is the magstripe output
#define MAILBOX_TOKEN_RFID      "TOKEN_RFID"		//An RFID card has been presented.  Parameter is the RFID output
#define MAILBOX_FLUSH_PASSES    "FLUSH_PASSES"		//A request has been made to flush the pass database
#define MAILBOX_UPDATE_PASSES   "UPDATE_PASSES"		//A request has been made to try to update the pass database
#define MAILBOX_DRIVER_NOTIFY   "DRIVER_NOTIFY"		//A message is being transmitted to the user
#define MAILBOX_BILLING_LOG	"BILLING_LOG"		//A billing long entry is ready to dispatch

#define MAILBOX_PIU_MESSAGE	"PIU_MESSAGE"		//A message to display on line 0 of the PIU.  Payload is piu_message_struct

#define MAILBOX_SET_PADDLE	"SET_PADDLE"		//A message to request a paddle change.  Payload is set_paddle_req_struct
#define MAILBOX_PADDLE_ACK	"PADDLE_ACK"		//A reply to the above message.  Payload is the same, but with result field populated.

#define MAILBOX_NEXT_STOP	"NEXT_STOP"		//Request to advance to the next stop on the paddle
#define MAILBOX_PREV_STOP	"PREV_STOP"		//Request to advance to the previous stop on the paddle
#define MAILBOX_RULE_CALL	"RULE_CALL"		//Request the passdb module to execute a rule.  Payload is driver_rulecall_struct
#define MAILBOX_VAULT_DROP	"VAULT_DROP"		//Request the cash vault drop

//------------------------------------------------ MESSAGE BODY DATA STRUCTURES

typedef struct gps_status_struct
{
    //GPS location data
    double lat, lon, heading, velocity;
  
    //GPS Quality data  
    int num_sats;		//Number of sats in view
    int gps_good;		//GPS fix considered valid

    time_t stamp;		//System timestamp at last valid received fix
    time_t gpstime;		//GPS timestamp from last valid received GPS time
  
} gps_status;

#define STOP_NAME_LEN	(64)

typedef struct stop_status_struct
{
    //Paddle data
    int paddle;
    
    //Current stop data
    int route, trip, stop;
    double lat,lon;
    char stopname[STOP_NAME_LEN];

} stop_status;

#define DRIVER_NAME_LEN	(64)

typedef struct driver_status_struct
{
  //Information on the currently logged in driver
  int logged_in_driver;
  char driver_name[DRIVER_NAME_LEN];

  //Information on the current equipment number
  int equip_num;

} driver_status;


#define PIU_PRIORITY_NOTIFY	(100)
#define PIU_PRIORITY_FARE	(200)

#define PIU_MESSAGE_LEN	(64)	//this is optimistic since our display is only 20 characters wide...

typedef struct piu_message_struct
{
  //If this message is of greater or equal priority than the currently displayed one, it will clobber it
  int priority;
  
  //Which line do we want to display this on?
  int line;
  
  //And for how long?  0 seconds = set default for that line
  int seconds;
  
  //And now for the actual message...
  char message[PIU_MESSAGE_LEN];

} piu_message;


#define FLUSH_STATUS_NORMAL	(0)	//we are not in the process of a flush operation
#define FLUSH_STATUS_LEGACY	(1)	//we are in the process of an old-style flush, pass checking won't work
#define FLUSH_STATUS_DOWNLOAD	(2)	//we are in the process of downloading a ZFLUSH datablob, pass checking will work with old database
#define FLUSH_STATUS_APPLY	(3)	//we are in the process of applying a downloaded ZFLUSH datablob, pass checking won't work
#define FLUSH_STATUS_WRITE	(4)	//we are done applying, but waiting for the disk write to complete, pass checking won't work

#define PASSDB_OPERABLE(x)	( ((x) == FLUSH_STATUS_NORMAL) || ((x) == FLUSH_STATUS_DOWNLOAD) ) 

typedef struct pass_status_struct
{
    //This indicates what our flush status is
    int flush_status;
    
    //This is a generic progress bar counter, expected to be in the range of 0-100 (for percent).
    int progress_indicator;

    //Last server contact
    time_t last_sync_time;
    time_t last_ack_time;
    
} pass_status;


typedef struct bill_status_struct
{
    //Last server contact
    time_t last_sync_time;
    time_t last_ack_time;
    
    
    //Number of undelivered billing / log messages bound for the server
    int unsynced_messages;
    
} bill_status;


typedef struct set_paddle_req_struct
{
    int request;
    int result;

} set_paddle_req;

#define DRIVER_RULECALL_LEN	(24)

typedef struct driver_rulecall_struct
{
    char rulename[DRIVER_RULECALL_LEN];
    char ruleparam[DRIVER_RULECALL_LEN];    

} driver_rulecall;


#endif	//#ifdef _TRANSIT_COMMHUB_API

