/***************************************************************************
 *   copyright           : (C) 2005 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.                                   *
 *                                                                         *
 ***************************************************************************/


#include <versit.h>
#include <helper.h>
#include <stdio.h>
#include <string.h>
#include <locale.h>
#include <ctype.h>

struct versit_out_handle {
  FILE* fp;
  char* charset;
  unsigned int needs_charset :1;
  enum encoding_value encoding;
  enum vcard_version version;
  unsigned int begin;
};
#define HANDLE(h) ((struct versit_out_handle*)(h))

static void versit_out_begin (struct versit_out_handle* h) {
  if (h->begin) return;
  fprintf(h->fp,"BEGIN:VCARD"VERSIT_LINE_END);
  fprintf(h->fp,"VERSION:%s"VERSIT_LINE_END,(h->version==VCARD_VERSION_21)?"2.1":"3.0");
  h->begin = 1;
}

static void versit_out_end (struct versit_out_handle* h) {
  if (!h->begin) return;
  fprintf(h->fp,"END:VCARD"VERSIT_LINE_END);
}

struct versit_out_propopt {
  unsigned int needs_encoding :1;
  unsigned int needs_charset :1;
};


static void versit_out_prop (struct versit_out_handle* h,
			     char* property,
			     struct versit_out_propopt* p)
{
  fprintf(h->fp,"%s",property);
  if (h->version != VCARD_VERSION_30 && p != NULL) {
    if (p->needs_charset)
      fprintf(h->fp,";CHARSET=%s",h->charset);
    if (p->needs_encoding || h->encoding != ENC_SEVENBIT)
      fprintf(h->fp,";ENCODING=%s",encoding_string[h->encoding]);
  }
}

static void versit_out_prop_param (struct versit_out_handle* h,
				   char* type, char* value)
{
  char* p = str_dup(value);
  size_t i = 0;

  switch (h->version) {
  case VCARD_VERSION_21:
    for (; i < str_len(p); ++i) p[i] = (char)(toupper((int)p[i])&0x7F);
    break;
  case VCARD_VERSION_30:
    for (; i < str_len(p); ++i) p[i] = (char)(tolower((int)p[i])&0x7F);
    break;
  }
  if (strcasecmp(type,"TYPE") == 0) type = NULL;
  if (type) fprintf(h->fp,";%s=%s",type,p);
  else fprintf(h->fp,";%s",p);
  mem_realloc(p,0);
}

static char* versit_out_convert (struct versit_out_handle* h,
				 ucs4char_t* text,
				 struct versit_out_propopt* p)
{
  char* temp = convert_from_internal(h->charset,text,REPMODE_QUESTIONMARK);
  char* retval = (char*)encode_chars(h->encoding,(unsigned char*)temp);
  p->needs_charset = h->needs_charset;
  if (is_eightbit((uint8_t*)temp)) p->needs_encoding = 1;
  mem_realloc(temp,0);
  return retval;
}

void* versit_out_open (FILE* fp,
		       char* cset,
		       enum encoding_value enc,
		       enum vcard_version version)
{
  struct versit_out_handle* h = mem_alloc(sizeof(*h),0);
  char* l;

  h->fp = fp;
  h->encoding = enc;
  if (cset != NULL &&
      (strncmp(cset,"ANSI_X3",7) == 0 ||
       strcmp(cset,"ASCII") == 0 ||
       strcmp(cset,"646") == 0)) {
    l = str_dup(setlocale(LC_ALL,NULL));
    setlocale(LC_ALL,"C");
    h->charset = str_dup(get_system_charset_raw());
    setlocale(LC_ALL,l);
    mem_realloc(l,0);
    /* ASCII is the default for vCard-2.1 */
    h->needs_charset = 0;
    /* ASCII is always 7bit charset and 7bit is default for vCard-2.1 */
    if (h->encoding == ENC_EIGHTBIT) h->encoding = ENC_SEVENBIT;
  } else {
    h->charset = str_dup(cset);
    h->needs_charset = 1;
    /* It's better to assume that his need 8bit.
     * However, honour the 7bit user request.
     */
    if (h->encoding == ENC_SEVENBIT) h->encoding = ENC_QUOTEDPRINTABLE;
  }
  h->version = version;
  h->begin = 0;
  return (void*)h;
}

void versit_out_close (void* h) {
  
  versit_out_end(HANDLE(h));
  mem_realloc(HANDLE(h)->charset,0);
  mem_realloc(h,0);
}

void versit_out_number(void* h, struct gsm_number* num,
		       uint32_t type) {
  char* temp = gsm_number_get(num);

  if (str_len(temp)) {
    versit_out_begin(HANDLE(h));
    versit_out_prop(HANDLE(h),"TEL",NULL);
    if (type&VERSIT_NUMBER_HOME) versit_out_prop_param(HANDLE(h),"TYPE","HOME");
    if (type&VERSIT_NUMBER_WORK) versit_out_prop_param(HANDLE(h),"TYPE","WORK");
    if (type&VERSIT_NUMBER_CELL) versit_out_prop_param(HANDLE(h),"TYPE","CELL");
    if (type&VERSIT_NUMBER_FAX) versit_out_prop_param(HANDLE(h),"TYPE","FAX");
    fprintf(HANDLE(h)->fp,":%s"VERSIT_LINE_END,temp);
  }
  mem_realloc(temp,0);
}


void versit_out_name (void* h,
		      ucs4char_t* first,
		      ucs4char_t* last)
{
  struct versit_out_propopt p = { 0, 0 };
  char* given_name = versit_out_convert(HANDLE(h),first,&p);
  char* family_name = versit_out_convert(HANDLE(h),last,&p);
  
  if (str_len(given_name) || str_len(family_name)) {
    versit_out_begin(HANDLE(h));
    versit_out_prop(HANDLE(h),"N",&p);
    fprintf(HANDLE(h)->fp,":%s;%s"VERSIT_LINE_END,
	    (given_name)?given_name:"",
	    (family_name)?family_name:"");
  }
  mem_realloc(given_name,0);
  mem_realloc(family_name,0);
}

void versit_out_address (void* h,
			 ucs4char_t* street, ucs4char_t* city,
			 ucs4char_t* country,ucs4char_t* postal)
{
  struct versit_out_propopt p = { 0, 0 };
  char* st = versit_out_convert(HANDLE(h),street,&p);
  char* ci = versit_out_convert(HANDLE(h),city,&p);
  char* co = versit_out_convert(HANDLE(h),country,&p);
  char* po = versit_out_convert(HANDLE(h),postal,&p);
  
  if (str_len(st) || str_len(ci) || str_len(co) || str_len(po)) {
    versit_out_begin(HANDLE(h));
    versit_out_prop(HANDLE(h),"ADR",&p);
    fprintf(HANDLE(h)->fp,":;;%s;%s;;%s;%s"VERSIT_LINE_END,
	    (st)?st:"",(ci)?ci:"",(co)?co:"",(po)?po:"");
  }
  mem_realloc(st,0);
  mem_realloc(ci,0);
  mem_realloc(co,0);
  mem_realloc(po,0);
}

static
void versit_out_misc (void* h, char* prop, ucs4char_t* value)
{
  struct versit_out_propopt p = { 0, 0 };
  char* v = versit_out_convert(HANDLE(h),value,&p);
  
  if (str_len(v)) {
    versit_out_begin(HANDLE(h));
    versit_out_prop(HANDLE(h),prop,&p);
    fprintf(HANDLE(h)->fp,":%s"VERSIT_LINE_END,(v)?v:"");
  }
  mem_realloc(v,0);
}


void versit_out_org (void* h, ucs4char_t* org) {
  versit_out_misc(h,"ORG",org);
}

void versit_out_email (void* h, ucs4char_t* email) {
  versit_out_misc(h,"EMAIL",email);
}

void versit_out_url (void* h, ucs4char_t* url) {
  versit_out_misc(h,"URL",url);
}

void versit_out_revision (void* h, ucs4char_t* rev) {
  versit_out_misc(h,"REV",rev);
}

void versit_out_birthday (void* h, uint16_t year,
			  uint16_t month, uint16_t day)
{
  if (year >= 1900 &&
      1 <= month && month <=12 &&
      1 <= day && day != 31) {
    versit_out_begin(HANDLE(h));
    versit_out_prop(HANDLE(h),"BDAY",NULL);
    fprintf(HANDLE(h)->fp,":%04d-%02d-%02d"VERSIT_LINE_END,
	    year,month,day);
  }
}

void versit_out_photo_url (void* h, char* url) {
  versit_out_begin(HANDLE(h));
  switch (HANDLE(h)->version) {
  case VCARD_VERSION_21:
    versit_out_prop(HANDLE(h),"X-SIEMENS-FLEXMEM",NULL);
    versit_out_prop_param(HANDLE(h),NULL,"PHOTO");
    break;
  case VCARD_VERSION_30:    
    versit_out_prop(HANDLE(h),"PHOTO",NULL);
    versit_out_prop_param(HANDLE(h),"VALUE","uri");
    break;
  }
  fprintf(HANDLE(h)->fp,":%s"VERSIT_LINE_END,url);
}

void versit_out_nickname (void* h, char* nick) {
  versit_out_begin(HANDLE(h));
  switch (HANDLE(h)->version) {
  case VCARD_VERSION_21:
    versit_out_prop(HANDLE(h),"X-SIEMENS-NICKNAME",NULL);
    break;
  case VCARD_VERSION_30:    
    versit_out_prop(HANDLE(h),"NICKNAME",NULL);
    break;
  }
  fprintf(HANDLE(h)->fp,":%s"VERSIT_LINE_END,nick);  
}

void versit_out_messaging (void* h, char* type, char* value) {
  char* part1prefix = "messaging/";
  char* part2 = "All";
  char* temp = mem_alloc(1+sizeof(part1prefix)+str_len(type)+sizeof(part2)+1,1);

  versit_out_begin(HANDLE(h));
  sprintf(temp,"X-%s%s-%s",part1prefix,type,part2);
  versit_out_prop(HANDLE(h),temp,NULL);
  fprintf(HANDLE(h)->fp,":%s",value);
  mem_realloc(temp,0);
}

void versit_out_serial (void* h, uint16_t value) {
  versit_out_begin(HANDLE(h));
  versit_out_prop(HANDLE(h),"X-SIEMENS-SERIAL",NULL);
  fprintf(HANDLE(h)->fp,":%X",value);
}
