#ifndef CORE_H
#define CORE_H  1


/* ========================================================================== */
/* Include files */

#include <sys/types.h>
#include <limits.h>
#include "nntp.h"


/*! \addtogroup CORE */
/*! @{ */


/* ========================================================================== */
/* Data types */

/*! \brief Article number data type (value zero is always reserved)
 *
 * This renames the data type \ref nntp_anum_t .
 */
#define core_anum_t  nntp_anum_t

/*! \brief Group descriptor structure
 *
 * This renames the structure \ref nntp_groupdesc .
 */
#define core_groupdesc  nntp_groupdesc

/*! \brief Group label structure
 *
 * This renames the structure \ref nntp_grouplabel .
 */
#define core_grouplabel  nntp_grouplabel

/*! \brief Time in seconds since the epoche (in terms of POSIX.1)
 *
 * This is the core representation for POSIX \c time_t which is usually signed
 * and even allowed to be a floating point type (what we don't want for
 * performance reasons).
 * Old compilers don't support 64 bit integer data types, therefore we use the
 * largest mandatory (in terms of C90) unsigned integer type available.
 * This gives at least additional 68 years beyond 2038 until "end of time" is
 * reached for the cost that we can't use timestamps before the POSIX epoche
 * (at this time Usenet was not invented yet and therefore this causes no
 * problems).
 *
 * In the case Usenet would still exist at that time, the limit can be
 * increased by redefining this to \c uint64_t at the cost of losing
 * compatibility to historical systems.
 */
typedef unsigned long int  core_time_t;

/*! \brief Structure to transfer data between core and UI threads
 *
 * This structure is moved between the UI and the core threads. Before every
 * access to it, a mutex must be locked with \ref core_mutex_lock() .
 *
 * After a command was completed that was delegated to the core thread, the core
 * calls \ref ui_wakeup() and the UI uses the \ref cookie field of this
 * structure to queue the corresponding callback for the command.
 */
struct core_data
{
   unsigned int  cookie;     /*!< UI callback cookie */
   int  result;              /*!< Result (zero on success, negative on error) */
   size_t  size;             /*!< Size of data */
   void*  data;              /*!< Pointer to data */
};

/*! \brief Article range linked list element */
struct core_range
{
   core_anum_t  first;             /*!< First article in range */
   core_anum_t  last;              /*!< Last article in range */
   struct core_range*  next;       /*!< Pointer to next range in list */
};

/*! \brief Group description */
struct core_groupstate
{
   const char*  name;              /*!< Group name */
   struct core_range*  info;       /*!< Linked list of read article ranges */
   core_anum_t  last_viewed;       /*!< Last viewed article */
};

/*! \brief Article header fields supported by core
 *
 * All fields marked with [M] are mandatory for Usenet article headers.
 * All fields marked with [U] are MIME decoded to UTF-8 and normalized to NFC.
 *
 * All fields are initialized by the header object constructor. All mandatory
 * fields are always initialized with content (even if the article header is
 * invalid), all optional fields with NULL (if there is no such field in the
 * article header).
 *
 * The field \ref date is a numerical value, not a string.
 * The fields \ref groups and \ref refs point to NULL terminated arrays of
 * strings.
 */
/*
 * If you add or remove fields, don't forget to modify the constructor and
 * destructor functions for the header object accordingly.
 */
struct core_article_header
{
   const char*  msgid;        /*!< [M] Message ID of the article */
   const char**  groups;      /*!< [M] Groups the article is posted to */
   const char*  from;         /*!< [M][U] Sender of the article */
   const char*  subject;      /*!< [M][U] Subject of the article */
   core_time_t  date;         /*!< [M] POSIX timestamp (seconds since epoch) */
   const char*  supers;       /*!< Supersedes article with this Message ID */
   const char*  fup2;         /*!< Follow up to this group */
   const char*  reply2;       /*!< [U] Recipient of replies instead of sender */
   const char*  uagent;       /*!< [U] Client program which created article */
   const char*  org;          /*!< [U] Organization of sender */
   const char**  refs;        /*!< List with references */
   const char*  dist;         /*!< Distribution */
   const char*  mime_v;       /*!< MIME version */
   const char*  mime_ct;      /*!< MIME content type */
   const char*  mime_cte;     /*!< MIME content transfer encoding */
   const char*  mime_cd;      /*!< MIME content disposition */
   const char*  x_newsr;      /*!< [U] Nonstandard equivalent to 'uagent' */
   const char*  x_mailer;     /*!< [U] Nonstandard equivalent to 'uagent' */
   const char*  x_pagent;     /*!< [U] Nonstandard equivalent to 'uagent' */
   /* The following fields are marked obsolete by RFC 5536 */
   unsigned long int  lines;  /*!< Number of lines in body */
};

/*! \brief Node in article hierarchy
 *
 * The array pointed to by the \ref child field has \ref children entries.
 */
struct core_hierarchy_element
{
   /*! Article number (only valid for the current server) */
   core_anum_t  anum;
   /*! Flags (use \c CORE_HE_FLAG_xxx constants) */
   unsigned int  flags;
   /*! Pointer to article header */
   struct core_article_header*  header;
   /*! Pointer to parent article */
   struct core_hierarchy_element*  parent;
   /*! Number of child articles in array \ref child */
   size_t  children;
   /*! Pointer to array of pointers to child articles */
   struct core_hierarchy_element**  child;
};

/*! \brief Actions that the article hierarchy manager can handle
 *
 * \ref CORE_HIERARCHY_INIT must be the first action that is executed.
 */
enum core_hierarchy_action
{
   CORE_HIERARCHY_INIT,      /*!< Delete hierarchy and create a new empty one */
   CORE_HIERARCHY_GETROOT,   /*!< Get root node of article hierarchy */
   CORE_HIERARCHY_ADD,       /*!< Add article to hierarchy */
   CORE_HIERARCHY_ADD_OVER,  /*!< Add incomplete data (overview) to hierarchy */
   CORE_HIERARCHY_UPDATE     /*!< Update element of hierarchy */
};


/* ========================================================================== */
/* Constants */

/*! \brief Article number limit
 *
 * This renames the limit from the NNTP transport subsystem
 * \ref NNTP_ANUM_T_MAX .
 *
 * \attention
 * This value must always be smaller or equal to \c ULONG_MAX because many older
 * compilers do not provide a larger data type and many older stdio
 * implementations can't handle larger data types. This means on all platforms
 * where \c unsigned \c long is 32 bit wide this value is limited to 2^32 - 1.
 */
#define CORE_ANUM_T_MAX  NNTP_ANUM_T_MAX

/*! \brief Article number limit */
#define CORE_TIME_T_MAX  ULONG_MAX

/*! \name Group flag bits */
/*! @{ */
/*! Name contains only ASCII characters */
#define CORE_GROUP_FLAG_ASCII  NNTP_GROUP_FLAG_ASCII
/*! @} */

/*! \name Hierarchy elements flag bits */
/*! @{ */
/*! Node contains incomplete data from NNTP overview */
#define CORE_HE_FLAG_OVER  1U
/*! @} */

/*! \name Signature warnings */
/*! @{ */
#define CORE_SIG_FLAG_SEPARATOR   0x01U  /*!< Separator "-- " is missing */
#define CORE_SIG_FLAG_INVALID     0x02U  /*!< Character set is not UTF-8 */
#define CORE_SIG_FLAG_NOTASCII    0x04U  /*!< Character set is not US-ASCII */
#define CORE_SIG_FLAG_LENGTH      0x08U  /*!< Length is too large */
/*! @} */

/*! \name Algorithms for Cancel-Lock scheme */
/*! @{ */
#define CORE_CL_INVALID  0U  /*!< Invalid scheme */
#define CORE_CL_SHA1     1U  /*!< SHA1 */
#define CORE_CL_SHA256   2U  /*!< SHA2-256 */
/*! @} */


/*! @} */


/* ========================================================================== */
/* Variables */

extern struct core_data  data;


/* ========================================================================== */
/* Function prototypes */

const char**  core_extract_groups(const char*);
int  core_get_group_list(unsigned int);
int  core_get_group_labels(unsigned int);
int  core_sort_group_list(void);
int  core_subscribe_group(const char*);
int  core_unsubscribe_group(size_t*,  struct core_groupstate**, size_t*);
int  core_reset_group_states(unsigned int);
int  core_export_group_states(size_t, struct core_groupstate*);
int  core_update_subscribed_group_states(size_t*, struct core_groupstate**,
                                         size_t*);
void  core_destroy_subscribed_group_states(size_t*, struct core_groupstate**);
int  core_get_subscribed_group_info(const size_t*, struct core_groupstate**,
                                    unsigned int);
void  core_destroy_subscribed_group_info(struct core_groupdesc**);
int  core_set_group(const char*, unsigned int);
int  core_get_motd(unsigned int);
int  core_get_distribution(const char**, const char**);
int  core_get_subscription_proposals(unsigned int);
int  core_get_overview(struct core_range*, unsigned int);
int  core_get_article_by_mid(const char*, unsigned int);
int  core_get_article(const core_anum_t*, unsigned int);
int  core_get_article_header(const core_anum_t*, unsigned int);
int  core_get_article_body(const core_anum_t*, unsigned int);
int  core_post_article(const char*, unsigned int);
int  core_check_already_read(struct core_groupstate*,
                             struct core_hierarchy_element*);
void  core_mark_as_read(struct core_groupstate*, core_anum_t);
void  core_mark_as_unread(struct core_groupstate*, core_anum_t);
const char*  core_convert_canonical_to_posix(const char*, int, int);
const char*  core_convert_posix_to_canonical(const char*);
int  core_hierarchy_manager(struct core_hierarchy_element**,
                            enum core_hierarchy_action, core_anum_t, ...);
void  core_create_hierarchy_from_overview(struct core_groupstate*,
                                          struct core_range*,
                                          const char*);
const char*  core_entity_parser(const char*, size_t,
                                struct core_article_header**, size_t*);
void  core_destroy_entity_header(struct core_article_header**);
const char*  core_get_homedir(void);
const char*  core_suggest_pathname(void);
const char*  core_get_datetime(int);
const char*  core_get_msgid(const char*);
const char*  core_get_cancel_key(unsigned int, const char*);
const char*  core_get_cancel_lock(unsigned int, const char*);
const char*  core_get_signature(unsigned int*);
const char*  core_get_introduction(const char*, const char*);
const char*  core_convert_pathname_to_locale(const char*);
int  core_check_file_exist(const char*);
int  core_save_to_file(const char*, const char*);
const char*  core_tmpfile_create(void);
void  core_tmpfile_delete(const char*);
void  core_disconnect(void);
void  core_mutex_lock(void);
void  core_mutex_unlock(void);
int  core_check_thread_ui(void);
void  core_free(void*);
int  core_init(void);
void  core_exit(void);


#endif  /* CORE_H */

/* EOF */
