/*

                                   _JNJ`                        
                               .JNMH`                           
                              JMMF`       `;.                   
                            .NMM)          `MN.                 
                            MMM)            (MML                
                           (MMM`             MMML               
                     M  I  D  N  I  G  H  T    C  o  D  E       
                           (NMMF             MHNH               
                            NMML            .MMM                
                             NMML          .NMH                 
                              4MMNL       .#F                   
                               `4HNNL_   `                      
                                   `"""`                        

                          Copyright (C) 2004-2007
       "Ian (Larry) Latter" <ian dot latter at midnightcode dot org>

    Midnight Code is a registered trademark of Ian Latter.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, and mirrored at the Midnight Code web
    site; as at version 2 of the License only.
  
    This program 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
    General Public License for more details.
  
    You should have received a copy of the GNU General Public License
    (version 2) along with this program; if not, write to the Free
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA, or see http://midnightcode.org/gplv2.txt

*/


/* midnightcode-socket.h :: Midnight Code socket library		*/


#ifndef __MIDNIGHTCODE_SOCKET_H
#define __MIDNIGHTCODE_SOCKET_H 1


#define MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_START "ctl|"
#define MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_END "|ltc"
#define MIDNIGHTCODE_SOCKET_DATA_BLOCK_START "dat|"
#define MIDNIGHTCODE_SOCKET_DATA_BLOCK_END "|dat"

#define MIDNIGHTCODE_SOCKET_INBOUND_MAX_Q 32
#define MIDNIGHTCODE_SOCKET_RECEIVE_BLOCK 65536
#define MIDNIGHTCODE_SOCKET_SEND_BLOCK 65536

#define MIDNIGHTCODE_SOCKET_USER_LIST_MAX 16
#define MIDNIGHTCODE_SOCKET_WIRE_LIST_MAX 16
#define MIDNIGHTCODE_SOCKET_WIRE_BLOCK_MAX 30000

#define MIDNIGHTCODE_SOCKET_POLL_TIME 500
#define MIDNIGHTCODE_SOCKET_POLL_TIME_HANDLER 750

/* TODO: put this in a better/any structure */
#define MIDNIGHTCODE_SOCKET_CONTROL_CMD_LEN 1
#define MIDNIGHTCODE_SOCKET_CONTROL_CMD_VERSREQ_LEN 0
#define MIDNIGHTCODE_SOCKET_CONTROL_CMD_VERSRSP_LEN 5
#define MIDNIGHTCODE_SOCKET_CONTROL_CMD_ECHOREQ_LEN 32
#define MIDNIGHTCODE_SOCKET_CONTROL_CMD_ECHORSP_LEN 32

#define MIDNIGHTCODE_SOCKET_VERSION_INTERVAL 1
#define MIDNIGHTCODE_SOCKET_DPD_INTERVAL 3

enum midnightcode_socket_coded_t
{
  MIDNIGHTCODE_SOCKET_CODED_UNKNOWN = 0,
  MIDNIGHTCODE_SOCKET_CODED_ENCODE = 1,
  MIDNIGHTCODE_SOCKET_CODED_DECODE = 2
};

enum midnightcode_socket_version_t
{
  MIDNIGHTCODE_SOCKET_VERSION_EQUAL = 1,
  MIDNIGHTCODE_SOCKET_VERSION_COMPATIBLE = 2,
  MIDNIGHTCODE_SOCKET_VERSION_UNKNOWN = 3,
  MIDNIGHTCODE_SOCKET_VERSION_MISMATCH = 4,
  MIDNIGHTCODE_SOCKET_VERSION_ERROR = 5
};

enum midnightcode_socket_command_t
{
  MIDNIGHTCODE_SOCKET_COMMAND_UNKNOWN = 0,
  MIDNIGHTCODE_SOCKET_COMMAND_VERSREQ = 1,
  MIDNIGHTCODE_SOCKET_COMMAND_VERSRSP = 2,
  MIDNIGHTCODE_SOCKET_COMMAND_ECHOREQ = 3,
  MIDNIGHTCODE_SOCKET_COMMAND_ECHORSP = 4,
  MIDNIGHTCODE_SOCKET_COMMAND_INVALID = 5
};

enum midnightcode_socket_control_t
{
  MIDNIGHTCODE_SOCKET_CONTROL_UNKNOWN = 0,
  MIDNIGHTCODE_SOCKET_CONTROL_RAW = 1,
  MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE = 2
};

enum midnightcode_socket_packet_t
{
  MIDNIGHTCODE_SOCKET_PACKET_UNKNOWN = 0,
  MIDNIGHTCODE_SOCKET_PACKET_NONE = 1,
  MIDNIGHTCODE_SOCKET_PACKET_INVALID = 2,
  MIDNIGHTCODE_SOCKET_PACKET_RAW = 3,
  MIDNIGHTCODE_SOCKET_PACKET_CONTROL = 4,
  MIDNIGHTCODE_SOCKET_PACKET_DATA = 5
};

enum midnightcode_socket_direction_t
{
  MIDNIGHTCODE_SOCKET_DIR_UNKNOWN = 0,
  MIDNIGHTCODE_SOCKET_DIR_INITIATE = 1,
  MIDNIGHTCODE_SOCKET_DIR_LISTEN = 2,
  MIDNIGHTCODE_SOCKET_DIR_RECEIVE = 3
};

enum midnightcode_socket_protocol_t
{
  MIDNIGHTCODE_SOCKET_PROTOCOL_UNKNOWN = 0,
  MIDNIGHTCODE_SOCKET_PROTOCOL_TCP = 1,
  MIDNIGHTCODE_SOCKET_PROTOCOL_UDP = 2
};

enum midnightcode_socket_state_t
{
  MIDNIGHTCODE_SOCKET_STATE_UNKNOWN = 0,
  MIDNIGHTCODE_SOCKET_STATE_INITIALISE = 1,
  MIDNIGHTCODE_SOCKET_STATE_OPERATIONAL = 2,
  MIDNIGHTCODE_SOCKET_STATE_CLOSE = 3,
  MIDNIGHTCODE_SOCKET_STATE_USER_SHUT = 4,
  MIDNIGHTCODE_SOCKET_STATE_DESTROY = 5
};

enum midnightcode_socket_dpd_t
{
  MIDNIGHTCODE_SOCKET_DPD_DISABLE = 0,
  MIDNIGHTCODE_SOCKET_DPD_ENABLE = 1
};

enum midnightcode_socket_enclosure_t
{
  MIDNIGHTCODE_SOCKET_ENCLOSURE_UNKNOWN = 0,
  MIDNIGHTCODE_SOCKET_ENCLOSURE_NATURAL = 1,
  MIDNIGHTCODE_SOCKET_ENCLOSURE_ANY = 2
};

enum midnightcode_socket_tagged_t
{
  MIDNIGHTCODE_SOCKET_TAGGED_UNKNOWN = 0,
  MIDNIGHTCODE_SOCKET_TAGGED_NATURAL = 1,
  MIDNIGHTCODE_SOCKET_TAGGED_FULL = 2
};

enum midnightcode_socket_extract_t
{
  MIDNIGHTCODE_SOCKET_EXTRACT_UNKNOWN = 0,
  MIDNIGHTCODE_SOCKET_EXTRACT_START_TAG = 1,
  MIDNIGHTCODE_SOCKET_EXTRACT_DATA = 2, 
  MIDNIGHTCODE_SOCKET_EXTRACT_END_TAG = 4,
  MIDNIGHTCODE_SOCKET_EXTRACT_COMPLETE = 8 
};

enum midnightcode_socket_io_t
{
  MIDNIGHTCODE_SOCKET_IO_BLOCKING = 0,
  MIDNIGHTCODE_SOCKET_IO_NONBLOCKING = 1
};

struct midnightcode_socket_queue_element_t
{
  char * buffer;
  int buffer_size;
  midnightcode_list_entry(midnightcode_socket_queue_element_t)
};

midnightcode_list_head(midnightcode_socket_queue_head_t,
                       midnightcode_socket_queue_element_t)

struct midnightcode_socket_element_t
{
  int socket;
  int control;
  int direction;
  int protocol;
  int state;
  void * user_buffer;
  int user_buffer_size;
  struct in_addr ipv4_local;
  struct in_addr ipv4_remote;
  struct sockaddr_in ipv4_peer;
  int port_local;
  int port_remote;
  char ipv4_local_s[32];
  char ipv4_remote_s[32];
  int dpd_enabled;
  int dpd_interval;
  int dpd_fails;
  int dpd_max_fails;
  int dpd_max_interval;
  int max_connections;
  int version;
  int ver_interval;
  int ver_requests;
  int ver_max_interval;
  int ver_max_requests;
  int invalid_notifies;
  int invalid_blocks;
  struct midnightcode_socket_queue_head_t * receive_chunks;
  struct midnightcode_socket_queue_head_t * send_control_queue;
  struct midnightcode_socket_queue_head_t * receive_control_queue;
  struct midnightcode_socket_queue_head_t * send_data_queue;
  struct midnightcode_socket_queue_head_t * receive_data_queue;
  midnightcode_list_entry(midnightcode_socket_element_t)
};

midnightcode_list_head(midnightcode_socket_head_t,
                       midnightcode_socket_element_t)

struct midnightcode_socket_tag_t
{
  int open_tag_chunk;
  int open_tag_offset;
  int close_tag_chunk;
  int close_tag_offset;
};

int midnightcode_socket_open(
                       struct midnightcode_socket_element_t ** socket_u,
                       int control, int dead_peer_detection,
                       int max_connections, int direction,
                       int protocol, char * host, int port);
int midnightcode_socket_read(
                       struct midnightcode_socket_element_t * socket,
                       char ** buffer, int * buffer_len, int io);
int midnightcode_socket_read_block(
                       struct midnightcode_socket_element_t * socket,
                       char ** buffer, int * buffer_len, int block_count,
                       int io);
int midnightcode_socket_write(
                       struct midnightcode_socket_element_t * socket,
                       char * buffer, int buffer_len, int io);
int midnightcode_socket_write_block(
                       struct midnightcode_socket_element_t * socket,
                       char * buffer, int buffer_len, int io);
int midnightcode_socket_close(struct midnightcode_socket_element_t * socket);
int midnightcode_socket_closeshut(
                       struct midnightcode_socket_head_t * sockets,
                       struct midnightcode_socket_element_t * socket,
                       int state);
int midnightcode_socket_accept(struct midnightcode_socket_head_t * sockets,
                       struct midnightcode_socket_element_t * parent);
int midnightcode_socket_membrane_size_est(int encode, int block_type,
                       int size);
int midnightcode_socket_membrane(int encode, int block_type,
                       char * dst_buffer, char * src_buffer,
                       int dst_buffer_len, int src_buffer_len);
int midnightcode_socket_control_dispatch(
                       struct midnightcode_socket_element_t * socket,
                       unsigned int command_i,
                       char * buffer, int buffer_len);
int midnightcode_socket_control_processor(
                       struct midnightcode_socket_head_t * sockets,
                       struct midnightcode_socket_element_t * socket);
int midnightcode_socket_receive_extract(
                       struct midnightcode_socket_head_t * sockets,
                       struct midnightcode_socket_element_t * socket,
                       struct midnightcode_socket_tag_t * open_tag,
                       struct midnightcode_socket_tag_t * close_tag,
                       int enclosed_by, int store_tags, int block_type,
                       char ** buffer, int * buffer_len);
int midnightcode_socket_receive_extract(
                       struct midnightcode_socket_head_t * sockets,
                       struct midnightcode_socket_element_t * socket,
                       struct midnightcode_socket_tag_t * open_tag,
                       struct midnightcode_socket_tag_t * close_tag,
                       int enclosed_by, int store_tags, int block_type,
                       char ** buffer, int * buffer_len);
int midnightcode_socket_receive_seek(
                       struct midnightcode_socket_head_t * sockets,
                       struct midnightcode_socket_element_t * socket,
                       char * key, int key_len,
                       int least_start_chunk, int least_start_offset,
                       int max_start_chunk, int max_start_offset,
                       struct midnightcode_socket_tag_t ** tag);
int midnightcode_socket_receive_chunk(
                       struct midnightcode_socket_head_t * sockets,
                       struct midnightcode_socket_element_t * socket,
                       int enclosure_option, int include_tags,
                       char ** buffer, int * buffer_len);
int midnightcode_socket_receive_processor(
                       struct midnightcode_socket_head_t * sockets,
                       struct midnightcode_socket_element_t * socket);
void * midnightcode_socket_handler(void *arg);
int midnightcode_socket_initialise(
                       struct midnightcode_socket_head_t * sockets);
int midnightcode_socket_shutdown(
                       struct midnightcode_socket_head_t * sockets);


#endif
