/***************************************************************************
 *   copyright           : (C) 2002 by Hendrik Sattler                     *
 *   mail                : post@hendrik-sattler.de                         *
 *                                                                         *
 *   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; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef SMSPDU_H
#define SMSPDU_H

#include "charsets.h"
#include "helper.h"
#include "timeincl.h"
#include "intincl.h"



/* SMS time
 */
struct sms_pdu_time {
  enum {
    SMS_PDU_TIME_NONE = 0, /* ignore value */
    SMS_PDU_TIME_RELATIVE, /* value is relative */
    SMS_PDU_TIME_ABSOLUTE  /* value is absolute(GMT) */
  } format;
  time_t value;
};
void sms_pdu_time_init (struct sms_pdu_time* s);
int sms_pdu_time_fill (struct sms_pdu_time* this, const char* value, int tz);


/* SMS PDU types
 */
enum sms_direction {
  SMS_INCOMING,
  SMS_OUTGOING
};
enum sms_pdu_type {
  SMS_TYPE_DELIVER, /* incoming */
  SMS_TYPE_SUBMIT_REPORT, /* incoming */
  SMS_TYPE_STATUS_REPORT, /* incoming */

  SMS_TYPE_DELIVER_REPORT, /* outgoing */
  SMS_TYPE_SUBMIT, /* outgoing */
  SMS_TYPE_COMMAND /* outgoing */
};
/* type is the full PDU type field but only the lowest two bits are really needed */
enum sms_pdu_type sms_pdu_type_get (enum sms_direction d, uint8_t type);
char* sms_pdu_type_string (enum sms_pdu_type t);


/* SMS PDU options
 */
struct sms_pdu_options_deliver { /* for SMS_TYPE_DELIVER */
  unsigned int sr :1; /* status report indication */  
  unsigned int mms :1; /* more messages to send indication */
  unsigned int rp :1; /* reply path indication */
};
struct sms_pdu_options_status { /* for SMS_TYPE_STATUS_REPORT */
  unsigned int sr :1; /* status report qualifier */  
  unsigned int mms :1; /* more messages to send indication */
};
struct sms_pdu_options_submit { /* for SMS_TYPE_SUBMIT */
  unsigned int sr :1; /* status report request */  
  unsigned int rd :1; /* reject duplicate request */
  unsigned int rp :1; /* reply path request */
};
struct sms_pdu_options_command { /* for SMS_TYPE_COMMAND */
  unsigned int sr :1; /* status report request */  
};
struct sms_pdu_options {
  enum sms_pdu_type type;
  unsigned int udh_present :1; /* user data header indication, needed by sms_udh_fill() */
  union {
    struct sms_pdu_options_deliver deliver;
    struct sms_pdu_options_status  status;
    struct sms_pdu_options_submit  submit;
    struct sms_pdu_options_command command;
  } u;
};
void sms_pdu_options_init (struct sms_pdu_options* s);


/* SMS Data Coding Scheme
 */
enum sms_pdu_dcs_type {
  SMS_DCS_TYPE_NORMAL,
  SMS_DCS_TYPE_IND, /* SMS_CHARSET_8BIT is not a valid choice for this type */
  SMS_DCS_TYPE_NONE /* needed for SMS-STATUS-REPORT (DCS is optional) */
};
enum sms_pdu_class {
  SMS_CLASS_FLASH = 0,
  SMS_CLASS_ME = 1,
  SMS_CLASS_SIM = 2,
  SMS_CLASS_TE = 3,
  SMS_CLASS_NONE
};
enum sms_encoding {
  SMS_CHARSET_GSM,
  SMS_CHARSET_UCS2,
  SMS_CHARSET_8BIT
};
struct sms_pdu_dcs {
  enum sms_pdu_dcs_type type;

  enum sms_encoding encoding;
  union {
    struct {
      unsigned int autodel :1; /* 0:normal mode, 1:marked as auto-delete */
      unsigned int compressed :1; /* 0:not compressed, 1:compressed (not supported, yet) */
      enum sms_pdu_class class; /* default is SMS_CLASS_NONE */
    } normal;

    struct {
      unsigned int sense :1; /* 0:inactive, 1:active */
      enum {
	SMS_DCS_IND_STORE = 0,
	SMS_DCS_IND_DISCARD
      } group;
      enum {
	SMS_DCS_IND_OTHER = 0,
	SMS_DCS_IND_VOICE,
	SMS_DCS_IND_FAX,
	SMS_DCS_IND_EMAIL
      } type;
    } ind; 
  } value;
};
void sms_pdu_dcs_init (struct sms_pdu_dcs* s);



/* SMS Status-Report addition
 */
struct sms_pdu_message_status {
  uint8_t status;
  struct sms_pdu_time time;
  struct sms_pdu_ud* message_parent;
  struct sms_pdu* status_parent;
};
void sms_pdu_message_status_init(struct sms_pdu_message_status* s);



/* SMS user data (with user data header)
 */
struct sms_pdu_ud_header {
  uint8_t type;
  uint8_t len;
  uint8_t data[137];
};
void sms_pdu_ud_header_init (struct sms_pdu_ud_header* s);

struct sms_pdu_ud {
  /* a NULL terminated list */
  struct sms_pdu_ud_header** header;

  /* the text in the message */
  ucs4char_t* text;

  /* status of the message if a SMS-STATUS-REPORT can be matched
   * If the message type is SMS_STATUS_REPORT or no
   * SMS-STATUS-REPORT matches, this shall be NULL.
   *
   * Never free this pointer from here, except that
   * ack->status_parent is NULL
   */
  struct sms_pdu_message_status* ack;
};
void sms_pdu_ud_init (struct sms_pdu_ud* s);
void sms_pdu_ud_delete (struct sms_pdu_ud* s);


/* SMS Protocol Data Unit
 */
struct sms_pdu_extra_submit {
  /* message reference */
  uint8_t mref;
};
struct sms_pdu_extra_statusreport {
  /* message reference */
  uint8_t mref;
  /* service center status of a previously sent message */
  struct sms_pdu_message_status* status;
};
struct sms_pdu {
  struct sms_pdu_options options;

  struct gsm_number address;

  uint8_t pid; /* protocol ID */
  struct sms_pdu_dcs scheme;

  struct sms_pdu_time timedata;

  uint16_t multipart_id; /* multipart short message id */
  uint8_t partnum; /* number of this part, count from 0 */

  /* ud is a list of size (parts*sizeof(*ud))
   * if parts is 0, ud should be NULL
   */  
  uint8_t parts;
  struct sms_pdu_ud* ud;

  union {
    struct sms_pdu_extra_submit submit;
    struct sms_pdu_extra_statusreport statusreport;
  } extra;
};
void sms_pdu_init (struct sms_pdu* s);
void sms_pdu_delete (struct sms_pdu* s);



/* SMS transport PDU: TPDU = SCA + PDU
 */
struct sms_tpdu {
  struct gsm_number sca; //service center address
  struct sms_pdu* pdu;
};
void sms_tpdu_init (struct sms_tpdu* s);
void sms_tpdu_delete (struct sms_tpdu* s);


/* Raw SMS PDU data
 */
#define SMS_PDU_SIZE_MAX 176
struct sms_pdu_raw {
  size_t size;
  uint8_t data[SMS_PDU_SIZE_MAX];
};
void sms_pdu_raw_init (struct sms_pdu_raw* this);
void sms_pdu_raw_append (struct sms_pdu_raw* this,
			 uint8_t* data, size_t len);
void sms_pdu_raw_from_hexstring (struct sms_pdu_raw* this,
				 char* hexstring);
char* sms_pdu_raw_to_hexstring (struct sms_pdu_raw* this);

/* Data as it may be present in a slot of a mobile phone
 */
#define SMS_SLOT_INVALID  0
struct sms_slot_data {
  unsigned int slot; /* != 0 for true values */
  enum sms_direction dir;
  struct sms_pdu_raw tpdu;
};
void sms_slot_data_init (struct sms_slot_data* this);



/* Unification for encoded and decoded data
 * see below how to get one from the other
 */
struct sms {
  struct sms_slot_data** encoded;
  struct sms_tpdu* decoded;
};
void sms_delete (struct sms* s);



/***************************
 * encoding short messages *
 ***************************/
/* create a new short message of type SMS_TYPE_SUBMIT
 * return value is a NULL-terminated array of TPDUs allocated with malloc
 */
#define SMS_CREATE_OPT_FLASH    0x01 /* set immediate display bit */
#define SMS_CREATE_OPT_REPORT   0x02 /* request a status report from the SMSC */
#define SMS_CREATE_OPT_AUTOCHAR 0x04 /* automatically select a proper character set */
#define SMS_CREATE_OPT_UNICODE  0x08 /* force use of UCS-2 instead of GSM charset */
struct sms_pdu_raw** sms_pdu_create_submit (char* text, char* number, unsigned long options);

/* TODO: more generic interface:
 * sms_tpdu* sms_pdu_create_submit (char* text, char* number, unsigned long options);
 * sms_slot_data** sms_pdu_encode (sms_tpdu* sms);
 */

/* both return length of SMS-PDU (pdu is part of tpdu)
 * This is needed as parameter for the at command for sending
 * or for verification on a received PDU.
 */
unsigned int sms_tpdu_len (enum sms_direction d,
			   struct sms_pdu_raw* raw); //for TPDU (means: with SMSC field)
unsigned int sms_pdu_len (enum sms_direction d,
			  struct sms_pdu_raw* raw); //for PDU


/***************************
 * decoding short messages *
 ***************************/
/* STEP 1:
 * decode sms which is of type type
 * return value allocated with malloc
 */
struct sms_tpdu* sms_pdu_decode (struct sms_slot_data* sms);

/* STEP 2:
 * merge encoded and decoded into struct sms
 */
void sms_init (struct sms* s,
	       struct sms_slot_data* encoded,
	       struct sms_tpdu* decoded);

/* STEP 3.1:
 * to match the status report messages to sent messages
 * This must be done BEFORE merging concatenated message,
 * otherwise not all can be matched.
 *
 * The number of elements in list does not change with this call.
 *
 * If you don't need or want this, just omit it.
 */
void sms_statusreport_match (struct sms** list);

/* STEP 3.2:
 * to merge concatenated short messages
 *
 * The list may have less elements after this call
 *
 * If you don't need or want this, just omit it.
 */
int sms_multipart_merge (struct sms** list);

/* STEP 3.3:
 * order can currently be "type", "slot" or "type,slot",
 * Concatenated messages (if merged before or after this step)
 * are assigned to the first slot that any part of it appears in.
 *
 * The number of elements in list does not change with this call.
 * This can be done at any time in STEP 3.
 *
 * If you don't need or want this, just omit it.
 */
void sms_pdu_sort (struct sms** list, const char* order);

/* STEP 4:
 * print sms as text to fp
 *
 * flags defines what to print, the text (if present) is always printed
 */
#include <stdio.h>
#define SMS_PRINT_COMMON_HEADERS   0x01
#define SMS_PRINT_EXTENDED_HEADERS 0x02
#define SMS_PRINT_USERDATA_HEADERS 0x04
#define SMS_PRINT_ALL_HEADERS      0x07
void sms_pdu_print (FILE* fp, struct sms* sms, unsigned long flags);

#endif
