/*

  $Id: vcard.c,v 1.1.2.6 2004/09/25 20:00:07 walentino Exp $
  
  G N O K I I

  A Linux/Unix GUI for Nokia mobile phones.

  This file is part of gnokii.

  Gnokii 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.

  Gnokii 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
  along with gnokii; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  Copyright (C) 2002 Pavel Machek <pavel@ucw.cz>
  
  Copyright (C) 2004 Balint Reczey <balint_reczey@yahoo.com>

 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <assert.h>
#include <unistd.h>
#include <gnokii.h>

#define USE_INTTYPES_H		// needed for vf_iface.h
#include <vf_iface.h>

#define ADDTOCARD(card,attr,value) { \
		if (size < max) { \
			card[size] = malloc(strlen(attr)+strlen(value)+2);\
			sprintf(card[size],"%s%s\n",attr,value);\
			size++; \
		} else { \
			char ** card_tmp;\
			card_tmp = card; \
			card = malloc(2*max*sizeof(char*)); \
			memcpy(card,card_tmp,max*sizeof(char*) ); \
			free(card_tmp); \
			max*=2; \
			card[size] = malloc(strlen(attr)+strlen(value)+2);\
			sprintf(card[size],"%s%s\n",attr,value);\
			size++; \
		} \
}
// needed for qsort
static int qstrcmp(const void *p1, const void *p2)
{
	return strcmp(*(char **) p1, *(char **) p2);
}

// FIXME should be static until next gnokii API change
int gn_phonebook2vcard_str(char ** vcard_str_ptr, gn_phonebook_entry * entry, char *location)
{
	int i;
	int size = 0;
	int max = 8;
	char **vcard;
	int strsize;
	vcard = malloc(8 * sizeof(char *));

	ADDTOCARD(vcard, "FN:", entry->name);
	ADDTOCARD(vcard, "TEL;VOICE:", entry->number);
	ADDTOCARD(vcard, "X_GSM_STORE_AT:", location);
	{
		char tmp_attr[32];
		sprintf(tmp_attr, "X_GSM_CALLERGROUP:%d",
			entry->caller_group);

		ADDTOCARD(vcard, tmp_attr, "");
	}
	/* Add ext. pbk info if required */
	for (i = 0; i < entry->subentries_count; i++) {
		switch (entry->subentries[i].entry_type) {
		case GN_PHONEBOOK_ENTRY_Email:
			ADDTOCARD(vcard, "EMAIL;INTERNET:",
				  entry->subentries[i].data.number);
			break;
		case GN_PHONEBOOK_ENTRY_Postal:
			ADDTOCARD(vcard, "ADR;HOME:",
				  entry->subentries[i].data.number);
			break;
		case GN_PHONEBOOK_ENTRY_Note:
			ADDTOCARD(vcard, "NOTE:",
				  entry->subentries[i].data.number);
			break;
		case GN_PHONEBOOK_ENTRY_Number:
			switch (entry->subentries[i].number_type) {
			case GN_PHONEBOOK_NUMBER_Home:
				ADDTOCARD(vcard, "TEL;HOME:",
					  entry->subentries[i].data.
					  number);
				break;
			case GN_PHONEBOOK_NUMBER_Mobile:
				ADDTOCARD(vcard, "TEL;CELL:",
					  entry->subentries[i].data.
					  number);
				break;
			case GN_PHONEBOOK_NUMBER_Fax:
				ADDTOCARD(vcard, "TEL;FAX:",
					  entry->subentries[i].data.
					  number);
				break;
			case GN_PHONEBOOK_NUMBER_Work:
				ADDTOCARD(vcard, "TEL;WORK:",
					  entry->subentries[i].data.
					  number);
				break;
			case GN_PHONEBOOK_NUMBER_General:
				ADDTOCARD(vcard, "TEL;PREF:",
					  entry->subentries[i].data.
					  number);
				break;
			default:
				{
					char tmp_attr[32];
					sprintf(tmp_attr,
						"TEL;X_UNKNOWN_%d:",
						entry->subentries[i].
						number_type);
					ADDTOCARD(vcard, tmp_attr,
						  entry->subentries[i].
						  data.number);
					break;
				}
			}
			break;
		case GN_PHONEBOOK_ENTRY_URL:
			ADDTOCARD(vcard, "URL:",
				  entry->subentries[i].data.number);
			break;
		default:
			{
				char tmp_attr[32];
				sprintf(tmp_attr, "X_GNOKII_%d:",
					entry->subentries[i].entry_type);

				ADDTOCARD(vcard, tmp_attr,
					  entry->subentries[i].data.
					  number);
				break;
			}
		}
	}

	// to make it return the same file from the same entry
	qsort(vcard, size, sizeof(char *), qstrcmp);

	strsize = 0;
	strsize+= strlen("BEGIN:VCARD\n");
	strsize+= strlen("VERSION:3.0\n");
	
	for (i = 0; i < size; i++) {
		strsize+=strlen(vcard[i]);
	}
	
	strsize+= strlen("END:VCARD\n");
	
	*vcard_str_ptr = (char*)malloc(strsize+1);
	
	strcpy(*vcard_str_ptr,"BEGIN:VCARD\nVERSION:3.0\n");
	
	strsize = strlen("BEGIN:VCARD\nVERSION:3.0\n");
	for (i = 0; i < size; i++) {
		
		strcpy((*vcard_str_ptr)+strsize,vcard[i]);
		strsize+=strlen(vcard[i]);
	
	}
	strcpy(*vcard_str_ptr+strsize,"END:VCARD\n");

	for (i = 0; i < size; i++) {
		
		free(vcard[i]);
	
	}
	
	
	return 0;	
}
API int
gn_phonebook2vcard2(FILE * f, gn_phonebook_entry * entry, char *location)
{
	char *vcard_str;
	
	gn_phonebook2vcard_str(&vcard_str,entry,location);
	
	// print to file
	fprintf(f,"%s", vcard_str);
	free(vcard_str);
	return 0;
}


#define GN_DEFAULT_CALLERGROUP GN_PHONEBOOK_CALLER_GROUPS_MAX_NUMBER

#define STORE2(a1,a2,a3 , target, size, c)  if (vf_get_property(&tmp_prop,vcard,VFGP_FIND,NULL,a1, a2, a3, NULL)) \
 { \
	 do {  \
	 	c; \
	 	strncpy(target , vf_get_prop_value_string(tmp_prop,0),size); \
	 } while (vf_get_next_property(&tmp_prop));\
 }

#define STORE(a, b, c, target,size)  if (vf_get_property(&tmp_prop,vcard,VFGP_FIND,NULL,a, b, c, NULL)) \
	{ strncpy(target , vf_get_prop_value_string(tmp_prop,0),size);   }



#define STOREINT(a1, a2, a3, b) { \
		char tmp_store_int[64];\
		STORE(a1,a2,a3,tmp_store_int,64);\
		b = atoi(tmp_store_int);\
}

// doesn't handle multiple values correctly (e.g. : Adr:asd;qwe)
#define STORESUB(a1, a2, a3, c) if (entry->subentries_count < GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER) \
		{STORE2(a1,a2,a3, entry->subentries[entry->subentries_count++].data.number, GN_PHONEBOOK_NAME_MAX_LENGTH + 1, \
				entry->subentries[entry->subentries_count].entry_type = c);}

#define STORENUM(a1,a2,a3, c)    if (entry->subentries_count < GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER) \
		{STORE2(a1,a2,a3, entry->subentries[entry->subentries_count++].data.number,GN_PHONEBOOK_NAME_MAX_LENGTH, \
				entry->subentries[entry->subentries_count].entry_type = GN_PHONEBOOK_ENTRY_Number; \
				entry->subentries[entry->subentries_count].number_type = c);}

#undef ERROR
#define ERROR(a) fprintf(stderr, "%s\n", a)

API int gn_vcard_str2phonebook(char * vcard_str, gn_phonebook_entry * entry)
{
	
	char memloc[10];

	VF_PARSER_T *vcard_parser = NULL;
	VF_OBJECT_T *vcard = NULL;
	VF_PROP_T *tmp_prop = NULL;
	memset(memloc, 0, 10);
	
	// parse in the vCard
	if (vf_parse_init(&vcard_parser, &vcard)) {



			    vf_parse_text(vcard_parser, vcard_str, strlen(vcard_str));

		vf_parse_end(vcard_parser);

		// fill entry with values form vCard

		STORE("FN", NULL, NULL, entry->name,
		      GN_PHONEBOOK_NAME_MAX_LENGTH + 1);
		STORE("TEL", "VOICE", NULL, entry->number,
		      GN_PHONEBOOK_NUMBER_MAX_LENGTH + 1);

		STORESUB("URL", NULL, NULL, GN_PHONEBOOK_ENTRY_URL);
		STORESUB("EMAIL", "INTERNET", NULL,
			 GN_PHONEBOOK_ENTRY_Email);
		STORESUB("ADR", "HOME", NULL, GN_PHONEBOOK_ENTRY_Postal);
		STORESUB("NOTE", NULL, NULL, GN_PHONEBOOK_ENTRY_Note);


		STORENUM("TEL", "HOME", NULL, GN_PHONEBOOK_NUMBER_Home);
		STORENUM("TEL", "CELL", NULL, GN_PHONEBOOK_NUMBER_Mobile);
		STORENUM("TEL", "FAX", NULL, GN_PHONEBOOK_NUMBER_Fax);
		STORENUM("TEL", "WORK", NULL, GN_PHONEBOOK_NUMBER_Work);
		STORENUM("TEL", "PREF", NULL, GN_PHONEBOOK_NUMBER_General);


		STORE("X_GSM_STORE_AT", NULL, NULL, memloc, 10);

		// if the field is present and correctly formed 
		if (memloc && (strlen(memloc) > 2)) {
			entry->location = atoi(memloc + 2);
			memloc[2] = 0;
			entry->memory_type = gn_str2memory_type(memloc);


		}
		entry->caller_group = GN_DEFAULT_CALLERGROUP;
		STOREINT("X_GSM_CALLERGROUP", NULL, NULL,
			 entry->caller_group);


		// not bijective mapping (will be stored as TEL;HOME:)
		STORENUM("TEL", "ISDN", NULL, GN_PHONEBOOK_NUMBER_Home);

		// not bijective mapping (will be stored as TEL;HOME:)
		STORENUM("TEL", "WORK", "FAX", GN_PHONEBOOK_NUMBER_Fax);
		
		vf_delete_object(vcard, ((bool_t)(1)));

	} else {
		return 1;
	}
	return 0;
}

/* We assume gn_phonebook_entry is ready for writing in, ie. no rubbish inside */
API int gn_vcard2phonebook2(FILE * f, gn_phonebook_entry * entry)
{
	char buffer[1024];


	
	char *vcard_str = NULL;
	char *vcard_str_tmp = NULL; // string to store vCard
	int numchars = 0;
	int len=0;
	

	// parse in the vCard
			
		while ((numchars = fread(buffer, 1, 1024, f)))
		{
			len+=numchars;
			
			vcard_str_tmp = vcard_str;
			
			if (vcard_str_tmp) {
				vcard_str = malloc(len+1);
				memcpy(vcard_str,vcard_str_tmp,len-numchars);
				memcpy(vcard_str+len-numchars, buffer,numchars);
				memset(vcard_str+len+1,'\0',1);
				free(vcard_str_tmp);
			
			} else {
				vcard_str = malloc(numchars+1);
				memcpy(vcard_str, buffer,numchars);
				memset(vcard_str+numchars+1,'\0',1);
			}
			
			
		}
	
	gn_vcard_str2phonebook(vcard_str,entry);	
	
	free(vcard_str);

	return 0;
}
