/*******************************************************************************
 * scriptel-easyscript-private.h - Copyright 2014 - Scriptel Corporation
 * ----
 * This header contains definitions for functions used by the library that
 * should be considered private for calling applications as the
 * implementations of these functions could change over time and the
 * functions may be altogether removed.
 ******************************************************************************/
#ifndef SCRIPTEL_EASYSCRIPT_PRIVATE_H
#define SCRIPTEL_EASYSCRIPT_PRIVATE_H

#define BOOL int
#define FALSE (0)
#define TRUE (1)

#define REPEAT_HALF_CODE (59)
#define REPEAT_HALF_SWAP_CODE (3004)
#define NEW_STROKE_CODE (60)
#define NEW_STROKE_SWAP_CODE (3005)
#define REVERSE_X_CODE (61)
#define REVERSE_X_SWAP_CODE (3001)
#define REVERSE_Y_CODE (62)
#define REVERSE_Y_SWAP_CODE (3002)
#define REVERSE_X_AND_Y_CODE (63)
#define REVERSE_X_AND_Y_SWAP_CODE (3003)
#define CANCEL_CODE (3007)

#define VK_BACK_QUOTE		0x29
#define VK_1				0x02
#define VK_2				0x03
#define VK_3				0x04
#define VK_4				0x05
#define VK_5				0x06
#define VK_6				0x07
#define VK_7				0x08
#define VK_8				0x09
#define VK_9				0x0a
#define VK_0				0x0b
#define VK_MINUS			0x0c
#define VK_EQUALS			0x0d

#define VK_Q				0x10
#define VK_W				0x11
#define VK_E				0x12
#define VK_R				0x13
#define VK_T				0x14
#define VK_Y				0x15
#define VK_U				0x16
#define VK_I				0x17
#define VK_O				0x18
#define VK_P				0x19
#define VK_OPEN_BRACKET		0x1a
#define VK_CLOSE_BRACKET	0x1b
#define VK_BACK_SLASH		0x2b
#define VK_A				0x1e
#define VK_S				0x1f
#define VK_D				0x20
#define VK_F				0x21
#define VK_G				0x22
#define VK_H				0x23
#define VK_J				0x24
#define VK_K				0x25
#define VK_L				0x26
#define VK_SEMICOLON		0x27
#define VK_QUOTE			0x28

#define VK_Z				0x2c
#define VK_X				0x2d
#define VK_C				0x2e
#define VK_V				0x2f
#define VK_B				0x30
#define VK_N				0x31
#define VK_M				0x32
#define VK_COMMA			0x33
#define VK_PERIOD			0x34
#define VK_SLASH			0x35
#define VK_SPACE			0x39
#define VK_LSHIFT			0x2a
#define VK_RSHIFT			0x36
#define VK_ENTER			0x1c

#ifndef NULL
    //This is a work-around for compilers that don't include NULL as a
    //preprocessor macro.
	#define NULL 0
#endif

//For the VC compiler certain functions are flagged as unsafe. These macros let
//us prefer the safe variant while falling back to the unsafe variant if they
//don't exist (GCC, LLVM, etc).
#ifdef _MSC_VER
	#define SPRINTF(str, fmt, len, ...) sprintf_s(str, len, fmt, ##__VA_ARGS__)
	#define SPRINTF_ERR(fmt, ...) sprintf_s(last_error, SCRIPTEL_ERROR_MAX_LEN, fmt, ##__VA_ARGS__)
	#define CTIME(time, buffer, size) ctime_s(buffer, size, time)
	#define STRNCPY(dest, src, size) strcpy_s(dest, size, src)
#else
	#define SPRINTF(str, fmt, len, ...) snprintf(str, len, fmt, ##__VA_ARGS__)
	#define SPRINTF_ERR(fmt, ...) snprintf(last_error, SCRIPTEL_ERROR_MAX_LEN, fmt, ##__VA_ARGS__)
	#define CTIME(time, buffer, size) ctime_r(time, buffer)
	#define STRNCPY(dest, src, size) strncpy(dest, src, size)
#endif

typedef struct signature_linked_list {
	streaming_event_data streaming_event;
	struct signature_linked_list* next;			// link to the next node
} signature_linkedlist_node;

typedef enum {UNUSED, MSB_SHIFTED, LSB_SHIFTED, MSB_UNSHIFTED, LSB_UNSHIFTED} grouping;
typedef struct {
	grouping group;
	int msb_value;
	int lsb_value;
} keystroke_groupings_struct;

typedef enum {
	CS_CARD_DATA,			///< We're expecting card data.
	CS_CARD_PROCESSED,		///< Card data was successfully parsed.
	CS_NOT_A_CARD,			///< Not a card.
	CS_UNKNOWN				///< Unknown.
} cardState_t;

/**
 *	Initializes all of the static variables needed to start decoding a
 * streaming signature whetherthe signature will be uncompressed or compressed.
 */
void scriptel_private_initialize_streaming();

/**
 * This function checks a character buffer to see if the buffer matches the
 * pattern of a signature buffer.
 * @param sig_protocol Signature protocol to use to check the signature buffer.
 * @param buffer Buffer to check.
 * @param len Length of the buffer, used for range checking.
 * @return SCRIPTEL_RETR_SUCCESS on a postive match, SCRIPTEL_RETR_ERROR on a
 * parsing problem.
 */
scriptel_return_status scriptel_private_check_signature_buffer(scriptel_signature_protocol* sig_protocol, char* buffer, int len);

/**
 * This function checks a character buffer to see if the buffer matches the
 * pattern of a card swipe buffer.
 * @param swipe_protocol Card swipe protocol to use to check the signature
 * buffer.
 * @param buffer Buffer to check.
 * @param len Length of the buffer, used for range checking.
 * @return SCRIPTEL_RETR_SUCCESS on a positive match, SCRIPTEL_RETR_ERROR on a
 * parsing problem.
 */
scriptel_return_status scriptel_private_check_cardswipe_buffer(const scriptel_cardswipe_protocol* swipe_protocol, char* buffer, int len);

/**
 * This function decodes a single four-character coordinate.
 * @param sig_protocol Protocol to use to decode the point.
 * @param scriptel_coordinate Pointer to a coordinate to fill in.
 * @param point Pointer to the four characters to read from.
 * @return SCRIPTEL_RETR_SUCCESS on a successful parse, SCRIPTEL_RETR_ERROR
 * otherwise.
 */
scriptel_return_status scriptel_private_decode_point(const scriptel_signature_protocol* sig_protocol, scriptel_coordinate* coordinate, char* point);

/**
 * Compares two strings character by character while ignoring case.
 * @param str1 First string to compare.
 * @param str2 Second string to compare.
 * @param len Length of the strings to compare.
 * @return Returns 1 if the strings are identical up to the length, 0 otherwise.
 */
int scriptel_private_equals_ignore_case(char* str1, char* str2, int len);

/**
 * This function checks a credit card number with Luhn's algorithm to determine
 * if the card number is likely invalid.
 * @param card_number Card number to check. Make sure this is null terminated!!
 */
int scriptel_private_check_card_checksum(char* card_number);

/**
 * This function compares a credit card number against known patterns to
 * determine the likely issuer for a card.
 * @param card_number Card number to check. Make sure this is null terminated!!
 */
scriptel_financial_card_vendor scriptel_private_check_card_type(char* card_number);

/**
 * This function attempts to parse a financial card.
 * @param card Card swipe structure to fill in.
 * @param buffer Buffer to parse.
 * @param len Length of the buffer to parse.
 * @return SCRIPTEL_RETR_SUCCESS on success, otherwise SCRIPTEL_RETR_ERROR.
 */
scriptel_return_status scriptel_private_parse_financial_card(scriptel_cardswipe* card, char* buffer, int len);

/**
 * This function attempts to parse the first track of a financial card.
 * @param track Track one structure to fill in.
 * @param buffer Buffer to parse.
 * @param len Length of the buffer to parse.
 * @return SCRIPTEL_RETR_SUCCESS on success, otherwise SCRIPTEL_RETR_ERROR.
 */
scriptel_return_status scriptel_private_parse_financial_track_one(scriptel_financial_track_one* track, char* buffer, int len);

/**
 * This function attempts to parse the second track of a financial card.
 * @param track Track two structure to fill in.
 * @param buffer Buffer to parse.
 * @param len Length of the buffer to parse.
 * @return SCRIPTEL_RETR_SUCCESS on success, otherwise SCRIPTEL_RETR_ERROR.
 */
scriptel_return_status scriptel_private_parse_financial_track_two(scriptel_financial_track_two* track, char* buffer, int len);

/**
 * This function attempts to parse an identification card.
 * @param card Structure to fill in.
 * @param buffer Buffer containing identification card information.
 * @param len Length of the buffer to parse.
 * @return SCRIPTEL_RETR_SUCCESS on success, otherwise SCRIPTEL_RETR_ERROR.
 */
scriptel_return_status scriptel_private_parse_identification_card(scriptel_cardswipe* card, char* buffer, int len);

/**
 * This function attempts to parse the first track of an identification card.
 * @param track Track one structure to fill in.
 * @param buffer Buffer containing identification card information.
 * @param len Length of the buffer to parse.
 * @return SCRIPTEL_RETR_SUCCESS on success, otherwise SCRIPTEL_RETR_ERROR.
 */
scriptel_return_status scriptel_private_parse_identification_track_one(scriptel_identification_track_one* track, char* buffer, int len);

/**
 * This function attempts to parse the second track of an identification card.
 * @param track Track two structure to fill in.
 * @param buffer Buffer containing identification card information.
 * @param len Length of the buffer to parse.
 * @return SCRIPTEL_RETR_SUCCESS on success, otherwise SCRIPTEL_RETR_ERROR.
 */
scriptel_return_status scriptel_private_parse_identification_track_two(scriptel_identification_track_two* track, char* buffer, int len);

/**
 * This function attempts to parse the third track of an identification card.
 * @param track Track three structure to fill in.
 * @param buffer Buffer containing identification card information.
 * @param len Length of the buffer to parse.
 * @return SCRIPTEL_RETR_SUCCESS on success, otherwise SCRIPTEL_RETR_ERROR.
 */
scriptel_return_status scriptel_private_parse_identification_track_three(scriptel_identification_track_three* track, char* buffer, int len);

/**
 * This function attempts to locate the first instance of the specified
 * character in the passed in character buffer.
 * @param str1 String to search.
 * @param chr Character to search for.
 * @param start Where to start searching for the string, relative to the start
 * of the buffer.
 * @param max Maximum index to search relative to the start of the buffer.
 * @return Position of the character in the buffer, -1 if not found.
 */
int scriptel_private_chrpos(char* str1, char chr, int start, int max);

/**
 * This function attempts to locate the last instance of the specified
 * character in the passed in character buffer.
 * @param str1 String to search.
 * @param chr Character to search for.
 * @param start Where to start searching for the string, relative to the start
 * of the buffer.
 * @param max Maximum index to search relative to the start of the buffer.
 * @return Position of the character in the buffer, -1 if not found.
 */
int scriptel_private_rchrpos(char* str1, char chr, int start, int max);

/**
 * This function takes a substring from a character buffer and then copies it
 * into its own memory space. Any substrings taken from this function must be
 * free'd when you're done using them. All strings will be automatically null
 * terminated.
 * @param str1 Character buffer to take substring from.
 * @param idx Index to start from.
 * @param len Number of characters to take.
 * @return New string copied from the string buffer.
 */
char* scriptel_private_substr(char* str1, int idx, int len);

/**
 * This function parses the expiration date in the format present on financial
 * and identification cards.
 * @param buffer Buffer of at least four characters to parse the expiration
 * date from.
 * @return Expiration date.
 */
time_t scriptel_private_parse_expiration(char* buffer);

/**
 * This function trims a string on the right side with nulls.
 * @param str String to trim on the right.
 */
void scriptel_private_rtrim(char* str);

/**
* This function parses the signature portion of a compressed signature one
* character at a time while sending events as they become available.
* @param sig_protocol the protocol used for the signature
* @param c the next character in the signature stream
* @param current_position the position in stream starting from the start_stream character. Used for error reporting.
*/
scriptel_return_status parse_compressed_signature(const scriptel_signature_protocol *sig_protocol, char c, int current_position);

typedef struct huffman_node_struct {
	struct huffman_node_struct *left;
	struct huffman_node_struct *right;
	int value;
} huffman_node;
#define NODE (-1)

/**
 * This function returns a pointer to the huffman binary tree.
 * @param 0 = low res, 1 = high res
 */
SCRIPTEL_SYMBOL_LINKAGE
huffman_node* scriptel_private_load_compression_tree();

/**
 * Free the binary huffman tree from memory
 * @param pointer to the head of the tree
 */
void scriptel_private_free_huffman_tree(huffman_node *head);

/**
 * Reads a number of bits from a bit stream and converts them into a value
 * @param a pointer to a pointer to the current byte in the stream. The pointer is updated after the read.
 * @param a pointer to the current bit in the current byte. Counts from 7 to 0 and is updated after the read.
 * @param the number of bits to read to form the value
 * @return the value of the bits read.
 */
int scriptel_private_read_bits(const unsigned char **cur_byte, int *cur_bit, int num_bits);

/**
 * Reads the bit stream and returns the head of the binary tree that it represents.
 * The bottom of this page describes the format:
 * https://www.siggraph.org/education/materials/HyperGraph/video/mpeg/mpegfaq/huffman_tutorial.html
 * In a nutshell, the value of the first 6 bits is the number of bits all values will have. 
 * The remainder of the stream will be composed of left-right tags. a bit value of zero is a
 * node, and a bit value of 1 is a leaf. Leaf tags are followed by the bits that represent a value.
 * A node tag is followed by a right and left tag, which may be nodes themselves or leafs. Therefore
 * it is recursive: A node's left tag can be a node, who's left can be a leaf with a value and right tag
 * another node, and so on. The bit stream must describe a complete tree with only leafs at the bottom.
 * @param the initial head, which should be NULL if this is a completely new tree.
 * @param a pointer to a pointer to the current byte in the tream. The pointer is updated after the read.
 * @param a pointer to the current bit in the current byte. Counts from 7 to 0 and is updated after the read.
 * @param the number of bits that make up values. This will normally be the value of the first 6 bits of the stream.
 * @return a pointer to the head of the new portion of the binary tree. The tree should not be freed. This function
 *         may free it on subsequent calls if the table needs to be replaced, but otherwise remains static until
           the program terminates.
 */
huffman_node* scriptel_private_create_huffman_tree(huffman_node *head, const unsigned char **current_byte, int *cur_bit, int bit_depth);

/**
 * Loads the compression tree into memory if necessary and returns the top (head) node.
 * @return the head of the tree.
 */
huffman_node* scriptel_private_get_huffman_table();

/**
 * Packs the signature stored as a list of events into a structure.
 * @param sig_protocol the protocol used for the signature
 * @param pointer to the structure to be filled. Assumes that pointers are not valid to start with and mallocs anything it needs to.
 * @param where to start processing the linked list from, usually the list's head
 */
void scriptel_private_pack_signature_linked_list(const scriptel_signature_protocol *sig_protocol, scriptel_signature* signature, signature_linkedlist_node *head);

/**
 * Adds a copy of the the streaming event to a linked list
 */
void scriptel_private_linkedlist_add_to_tail(streaming_event_data *sd);

/**
 * Free the linked list of events
 */
void scriptel_private_free_linked_list();

/**
 * Notify all event listeners of the event
 * @param streaming_data the event
 */
void scriptel_private_notify(streaming_event_data *sd);

/**
 * Takes a string of 1 to 3 characters from the signiture coming from the
 * signature pad and converts them to a value
 * of between 0 and 511. A minimum of 2 is required and occationally 3.
 * @param sig_protocol the protocol used to convert values.
 * @sig_chars a portion of the string coming from the signature pad
 * @return either the value represented by the string; or -1 means there were not enough characters; -2 means the value could not be calculated because the signature fragment had an error
 */
int scriptel_private_convert_bodnar_chars_to_value(const scriptel_signature_protocol *sig_protocol, char *sig_chars);

/**
 * Take 9 bits at a time of the compressed signature stream and decompress it, sending events as they occur.
 * @param sig_protocol the protocol used to convert values.
 * @param a value between 0-511 representing the bit stream to be decompressed. High bit first.
 */
void scripte_private_decompress_bits(const scriptel_signature_protocol *sig_protocol, int bits);

/**
 * Given one character at a time of the signature data portion of an uncompressed signature,
 * this function sends the appropriate events to the listeners.
 * @param sig_protocol the signature protocol to use to interpret the data
 * @param c each character, one per call, of the signature data
 * @param the position in the stream of the character, used for logging
 * @return success of failure, fails if the data makes no sense.
 */
scriptel_return_status parse_uncompressed_signature(const scriptel_signature_protocol *sig_protocol, char c, int current_position);

/**
 * Given the decompressed values of the compressed data stream,
 * this function sends the appropriate events.
 * @param sig_protocol the signature protocol to use to interpret the data
 * @param the next uncompressed value of the data stream
 */
void scriptel_private_data_decode(const scriptel_signature_protocol *sig_protocol, int value);

/**
 * This function is the card portion of the state machine that process signatures.
 * It works hand-in-hand with the state machine in scriptel_keyboard_parse_char.B
 * @param chr the next character in the card stream
 * @param card_state the current state-machine state
 * @param card_buffer a buffer area in which to build up the card string
 */
void parse_card(char chr, cardState_t *card_state, char *card_buffer);

#endif