/***************************************************************************
 *   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 _GNU_SOURCE
# define _GNU_SOURCE
#endif
#ifndef _REENTRANT
# define _REENTRANT
#endif

#include <smspdu.h>
#include "smscoding.h"
#include "smsudh.h"

#include <charsets.h>
#include <helper.h>
#include <timeincl.h>
#include <gtincl.h>

#include <stdlib.h>
#include <string.h>
#include <ctype.h>

static
void sms_pdu_print_item_slot (FILE* fp, struct sms* sms) {
  struct sms_pdu* pdu;
  unsigned int i = 0;
  unsigned int p = 1;

  pdu = sms->decoded->pdu;
  if (pdu->parts > 1) p = pdu->parts;
  for (; i < p; ++i) {
    if (sms->encoded[i] != NULL && 
	sms->encoded[i]->slot >= 1) {
      fprintf(fp,"%s: %d\n",_("Slot"), sms->encoded[i]->slot);
    }
  }
}

static
void sms_pdu_print_item_address (FILE* fp, struct sms* sms) {
  struct sms_pdu* pdu;
  char* temp;

  pdu = sms->decoded->pdu;
  if (str_len(pdu->address.digits) > 0 ||
      ucs4len(pdu->address.text) > 0) {
    switch(pdu->options.type) {
    default:
    case SMS_TYPE_SUBMIT: temp = _("To"); break;
    case SMS_TYPE_DELIVER: temp =  _("From"); break;
    case SMS_TYPE_STATUS_REPORT: temp = _("Original To"); break;
    }
    fprintf(fp,"%s:",temp);
    if (str_len(temp) > 0) {
      temp = gsm_number_get(&pdu->address);
      if (temp != NULL) fprintf(fp," %s",temp);
      mem_realloc(temp,0);
    }
    if (ucs4len(pdu->address.text)) {
      temp = convert_to_system(pdu->address.text,REPMODE_QUESTIONMARK);
      fprintf(fp," (%s)",temp);
      mem_realloc(temp,0);      
    }
    fprintf(fp,"\n");
  }
}

static
void sms_pdu_print_item_time (FILE* fp, enum sms_pdu_type type,
			      struct sms_pdu_time* t)
{
  unsigned short weeks;
  unsigned short days;
  unsigned short hours;
  unsigned short minutes;
  unsigned short seconds;
  struct tm sct_tm;
  struct tm* sct_status;
  char* field;
  char temp[81];

  switch (t->format) {
  case SMS_PDU_TIME_NONE:
    break;
  case SMS_PDU_TIME_RELATIVE:
/*     fprintf(fp,"Valid for: %ld seconds\n",t->value); */
    weeks = t->value / (7*24*3600);
    days = (t->value / (24*3600))%7;
    hours = (t->value / 3600)%24;
    minutes = (t->value / 60)%60;
    seconds = t->value%60;
    fprintf(fp,"%s:",_("Valid for"));
    if (weeks) fprintf(fp," %d %s", weeks, ngettext("week","weeks",weeks));
    if (days) fprintf(fp," %d %s", days, ngettext("day","days",days));
    if (hours) fprintf(fp," %d %s", hours, ngettext("hour","hours",hours));
    if (minutes) fprintf(fp," %d %s", minutes, ngettext("minute","minutes",minutes));
    if (seconds) fprintf(fp," %d %s", seconds, ngettext("second","seconds",seconds));
    fprintf(fp,"\n");
    break;
  case SMS_PDU_TIME_ABSOLUTE:
    field = NULL;
    sct_status = localtime(&t->value);
    if (sct_status != NULL) {
      memcpy(&sct_tm,sct_status,sizeof(struct tm));
      if (strftime(temp,sizeof(temp)-1,"%c",&sct_tm) > 0) {
	switch(type) {
	case SMS_TYPE_SUBMIT: field = _("Valid until"); break;
	case SMS_TYPE_DELIVER: field =  _("Date"); break;
	case SMS_TYPE_STATUS_REPORT: field = _("Message status time"); break;
	default: break;
	}
	fprintf(fp,"%s: %s\n",field,temp);
      }
    }
    break;
  }
}

static
void sms_pdu_print_item_smsc (FILE* fp, struct sms* sms) {
  char* temp = gsm_number_get(&(sms->decoded->sca));
  if (str_len(temp) > 0)
    fprintf(fp,"%s: %s\n",_("SMSC number"),temp);
  mem_realloc(temp,0);
}

char* sms_pdu_type_string (enum sms_pdu_type t) {
  switch (t) {
  case SMS_TYPE_DELIVER: return "SMS-DELIVER";
  case SMS_TYPE_SUBMIT: return "SMS-SUBMIT";
  case SMS_TYPE_STATUS_REPORT: return "SMS-STATUS-REPORT";
  case SMS_TYPE_SUBMIT_REPORT: return "SMS-SUBMIT-REPORT";
  case SMS_TYPE_DELIVER_REPORT: return "SMS-DELIVER-REPORT";
  case SMS_TYPE_COMMAND: return "SMS-COMMAND";
  }
  return "";
}

static
void sms_pdu_print_item_options (FILE* fp, struct sms* sms) {
  struct sms_pdu* pdu;

  pdu = sms->decoded->pdu;
  fprintf(fp,"%s: %s\n",_("PDU type"),sms_pdu_type_string(pdu->options.type));
  /* since this is a union and deliver is the biggest... */
  if (pdu->options.type != SMS_TYPE_SUBMIT_REPORT &&
      pdu->options.type != SMS_TYPE_DELIVER_REPORT &&
      (pdu->options.u.deliver.sr || pdu->options.u.deliver.mms ||
       pdu->options.u.deliver.rp)) {
    fprintf(fp,"%s:",_("PDU flags"));
    if (pdu->options.u.deliver.sr) fprintf(fp," %s",_("StatusRequest"));
    if (pdu->options.type == SMS_TYPE_SUBMIT) {
      if (pdu->options.u.submit.rd) fprintf(fp," %s",_("RejectDuplicate"));
    } else if (pdu->options.type == SMS_TYPE_DELIVER ||
	       pdu->options.type == SMS_TYPE_STATUS_REPORT) {
      if (pdu->options.u.deliver.mms) fprintf(fp," %s",_("MoreMessagesToSend"));
    }
    if (pdu->options.type == SMS_TYPE_DELIVER ||
	pdu->options.type == SMS_TYPE_SUBMIT)
      if (pdu->options.u.deliver.rp) fprintf(fp," %s",_("ReplyPath"));
    fprintf(fp,"\n");
  }
}

static
void sms_pdu_print_item_pid (FILE* fp, struct sms* sms) {
  struct sms_pdu* pdu;

  pdu = sms->decoded->pdu;
  if (pdu->pid > 0 && (pdu->pid&0xC0) != 0x80) {
    fprintf(fp,"%s: 0x%02x (",_("Protocol ID"),pdu->pid);
    switch (pdu->pid&0xC0) {
    case 0xC0:
      fprintf(fp,"%s",_("service-center-specific use"));
      break;
    case 0x40:
      if (0 < (pdu->pid&0x3F) && (pdu->pid&0x3F) <= 9) {
	fprintf(fp,_("replace short message type %d"),pdu->pid&0x3F);
      }
      switch (pdu->pid&0x3F) {
      case 0x00: fprintf(fp,"%s",_("short message type 0")); break;
      case 0x1e: fprintf(fp,"%s",_("obsolete EMS mark")); break;
      case 0x1f: fprintf(fp,"%s",_("return call message")); break;
      case 0x3c: fprintf(fp,"%s","ANSI-136 R-DATA"); break;
      case 0x3d: fprintf(fp,"%s",_("ME data download")); break;
      case 0x3e: fprintf(fp,"%s",_("ME de-personalization short message")); break;
      case 0x3f: fprintf(fp,"%s",_("(U)SIM data download")); break;
      default: break;
      }
      break;
    case 0x00:
      if ((pdu->pid&0x20) == 0) {
	fprintf(fp,"%s",_("SMS-to-SME protocol"));
      } else {
	fprintf(fp,"%s",_("telematic interworking"));
      }
      break;
    default:
    case 0x80:
      //reserved value, ignored with if-statement above
      break;
    }
    fprintf(fp,")\n");
  }
}

static
void sms_pdu_print_item_dcs (FILE* fp, struct sms* sms) {
  struct sms_pdu* pdu;
  char* temp = NULL;

  pdu = sms->decoded->pdu;
  if (pdu->scheme.type == SMS_DCS_TYPE_NORMAL) {
    fprintf(fp,"%s:",_("Settings"));
    if (pdu->scheme.value.normal.compressed) fprintf(fp," %s",_("compressed"));
    switch (pdu->scheme.encoding) {
    case SMS_CHARSET_GSM: fprintf(fp," %s","7bit-GSM"); break;
    case SMS_CHARSET_UCS2: fprintf(fp," %s","UCS-2"); break;
    case SMS_CHARSET_8BIT: fprintf(fp," %s","8bit"); break;
    }
    if (pdu->scheme.value.normal.class != SMS_CLASS_NONE)
      fprintf(fp,_(" (class %d)"),pdu->scheme.value.normal.class);
    if (pdu->scheme.value.normal.autodel)
      fprintf(fp," %s",_("marked as auto-delete"));
  } else if (pdu->scheme.type == SMS_DCS_TYPE_IND) {
    fprintf(fp,"%s:",_("Settings"));
    switch (pdu->scheme.encoding) {
    case SMS_CHARSET_GSM: fprintf(fp," %s","7bit-GSM"); break;
    case SMS_CHARSET_UCS2: fprintf(fp," %s","UCS-2"); break;
    case SMS_CHARSET_8BIT: fprintf(fp," %s","8bit"); break;
    }
    fprintf(fp,"\n%s: ",_("Indication"));
    switch(pdu->scheme.value.ind.type) {
    case SMS_DCS_IND_VOICE: temp = _("voicemail"); break;
    case SMS_DCS_IND_FAX: temp = _("fax"); break;
    case SMS_DCS_IND_EMAIL: temp = _("e-mail"); break;
    case SMS_DCS_IND_OTHER: temp = _("misc."); break;
    }
    if (pdu->scheme.value.ind.sense)
      fprintf(fp,_("new %s message(s) waiting"),temp);
    else
      fprintf(fp,_("no more %s messages waiting"),temp);
  }
  fprintf(fp,"\n");
}

static
void sms_pdu_print_item_text (FILE* fp, struct sms* sms) {
  struct sms_pdu* pdu;
  uint16_t textlen = 0;
  char* temp;
  unsigned int i;

  pdu = sms->decoded->pdu;
  if (pdu->ud == NULL) return;
  for (i = 0; i < pdu->parts; ++i) {
    textlen += ucs4len(pdu->ud[i].text);
  }
  fprintf(fp,"%s: %d\n\n",_("Message length"),textlen);
  if (textlen == 0) return;

  for (i = 0; i < pdu->parts; ++i) {
    if (pdu->ud[i].text == NULL) {
      fprintf(fp,"[...]");
    } else {
      temp = convert_to_system(pdu->ud[i].text,REPMODE_QUESTIONMARK);
      fprintf(fp,"%s",temp);
      mem_realloc(temp,0);
    }
  }
  fprintf(fp,"\n");
}

static
void sms_pdu_print_item_status (FILE* fp, struct sms_pdu_message_status* m) {
  char* temp;
  char* temp2;

  if (m == NULL) return;
  switch (m->status&0xd0) {
  case 0x00:
  case 0x40:
    temp2 = _("transaction completed"); break;
  case 0x20:
  case 0x60:
  default:
    temp2 = _("SC still trying to transfer short message"); break;
  }
  switch (m->status) {
  case 0x00: temp = _("received by SME"); break;
  case 0x01: temp = _("forwarded to SME but delivery cannot be confirmed"); break;
  case 0x02: temp = _("replaced"); break;
  case 0x20:
  case 0x60: temp = _("congestion"); break; 
  case 0x21:
  case 0x61: temp = _("SME busy"); break; 
  case 0x22:
  case 0x62: temp = _("no response from SME"); break; 
  case 0x23:
  case 0x63: temp = _("service rejected"); break; 
  case 0x24: 
  case 0x44:
  case 0x64: temp = _("QoS not available"); break; 
  case 0x25:
  case 0x65: temp = _("error in SME"); break; 
  case 0x40: temp = _("remote procedure error"); break;
  case 0x41: temp = _("incompatible destination"); break;
  case 0x42: temp = _("connection rejected by SME"); break;
  case 0x43: temp = _("not obtainable"); break;
  case 0x45: temp = _("no interworking available"); break;
  case 0x46: temp = _("validity period expired"); break;
  case 0x47: temp = _("deleted by sender"); break;
  case 0x48: temp = _("deleted by service center"); break;
  case 0x49: temp = _("message does not exist"); break;
  default:
    temp = _("service rejected");
  }
  fprintf(fp,"%s: %s\n",_("Message status"),temp2);
  fprintf(fp,"%s: %s\n",_("Message status reason"),temp);
  sms_pdu_print_item_time(fp,SMS_TYPE_STATUS_REPORT,&m->time);
}

static
void sms_pdu_print_item_mr (FILE* fp, struct sms* sms) {
  struct sms_pdu* pdu;

  pdu = sms->decoded->pdu;
  switch (pdu->options.type) {
  case SMS_TYPE_SUBMIT:
    fprintf(fp,"%s: %d\n",_("Message reference"),pdu->extra.submit.mref);
    if (pdu->ud != NULL)
      sms_pdu_print_item_status(fp,pdu->ud[pdu->partnum].ack);
    break;
  case SMS_TYPE_STATUS_REPORT:
    fprintf(fp,"%s: %d\n",_("Message reference"),pdu->extra.statusreport.mref);
    sms_pdu_print_item_status(fp,pdu->extra.statusreport.status);
    break;
  default:
    break;
  }
}

static
void sms_pdu_print_deliver (FILE* fp, struct sms* sms, unsigned long flags) {
  struct sms_pdu* pdu;

  if (fp == NULL || sms == NULL ||
      sms->decoded == NULL || sms->decoded->pdu == NULL) {
    return;
  }
  pdu = sms->decoded->pdu;
  if (flags&SMS_PRINT_COMMON_HEADERS) {
    sms_pdu_print_item_slot(fp,sms);
    sms_pdu_print_item_address(fp,sms);
    sms_pdu_print_item_time(fp,pdu->options.type,&pdu->timedata);
  }
  if (flags&SMS_PRINT_EXTENDED_HEADERS) {
    sms_pdu_print_item_smsc(fp,sms);
    sms_pdu_print_item_mr(fp,sms);
    sms_pdu_print_item_options(fp,sms);
    sms_pdu_print_item_pid(fp,sms);
    sms_pdu_print_item_dcs(fp,sms);
  }
  if (flags&SMS_PRINT_USERDATA_HEADERS) {
    sms_udh_print(fp,sms->decoded->pdu->ud,
		  sms->decoded->pdu->parts);
  }
  sms_pdu_print_item_text(fp,sms);
  fprintf(fp,"\n");
}

static
void sms_pdu_print_statusreport (FILE* fp, struct sms* sms, unsigned long flags) {
  struct sms_pdu* pdu;

  if (fp == NULL || sms == NULL ||
      sms->decoded == NULL || sms->decoded->pdu == NULL) {
    return;
  }
  pdu = sms->decoded->pdu;
  if (pdu->extra.statusreport.status != NULL &&
      pdu->extra.statusreport.status->message_parent == NULL) {
    if (flags&SMS_PRINT_COMMON_HEADERS) {
      sms_pdu_print_item_slot(fp,sms);
      sms_pdu_print_item_address(fp,sms);
      sms_pdu_print_item_time(fp,SMS_TYPE_DELIVER,&pdu->timedata);
    }
    if (flags&SMS_PRINT_EXTENDED_HEADERS) {
      sms_pdu_print_item_smsc(fp,sms);
      sms_pdu_print_item_mr(fp,sms);
      sms_pdu_print_item_options(fp,sms);
      sms_pdu_print_item_pid(fp,sms);
      sms_pdu_print_item_dcs(fp,sms);
    }
    if (flags&SMS_PRINT_USERDATA_HEADERS) {
      sms_udh_print(fp,sms->decoded->pdu->ud,sms->decoded->pdu->parts);
    }
    sms_pdu_print_item_text(fp,sms);
    fprintf(fp,"\n");
  }
}

void sms_pdu_print (FILE* fp, struct sms* sms, unsigned long flags) {
  if (fp == NULL || sms == NULL ||
      sms->decoded == NULL || sms->decoded->pdu == NULL) {
    return;
  }
  switch(sms->decoded->pdu->options.type) {
  case SMS_TYPE_DELIVER:
  case SMS_TYPE_SUBMIT:
    sms_pdu_print_deliver(fp,sms,flags);
    break;
  case SMS_TYPE_STATUS_REPORT:
    sms_pdu_print_statusreport(fp,sms,flags);
    break;
  case SMS_TYPE_SUBMIT_REPORT:
  case SMS_TYPE_DELIVER_REPORT:
  case SMS_TYPE_COMMAND:
    fprintf(stderr,"%s: %s\n\n",_("Unsupported pdu type"),
	    sms_pdu_type_string(sms->decoded->pdu->options.type));
    break;
  }
}
