// ---------------------------------------------------------- -*- c++ -*-
// bbdb parser and converter
// supported output format: vcards abook gnokii csv
//
// Copyright (C) 1994-2003: Pierre Aubert pierre.aubert@free.fr
// Code below is dirty. Don't take model on it.
// 
// V1.21 add abook format
// V1.20 adapt for g++-3
// ----------------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
//
// ----------------------------------------------------------------------

#include <iostream>
#include <map>
#include <algorithm>
#include <fstream>
#include <vector>
#include <string>

// horrible global variable
size_t counter = 0;

// hold datas from a bbdb record
struct Id {
    enum { NAME, FORNAME, AKA, COMPANY, PHONES, ADRESSES, NET, NOTES };
    // brute force
    typedef std::vector<std::string> storage_t;
    storage_t my_d;
};


namespace trace 
{
  enum  { 
      NONE = 0, 
      SOME = 1 , 
      VERBOSE = 2 
  };

  void 
  print( std::string const& l, int jb, int je ) {
    char wl[5] = { 27, '[' , '5' , 'm', 0 };
    char wr[5] = { 27, '[' , '0' , 'm', 0 };
    int i;
    for( i=0 ; i<jb ; ++i )
	std::cerr << l[i];
    std::cerr << wl;
    for( i=jb ; i<je ; ++i )
	std::cerr << l[i];
    std::cerr << wr;
    for( i=je ; i<l.size() ; ++i )
	std::cerr << l[i];

    std::cerr << std::endl;
  }
}

// used for translation between format
struct two_strings {
    const char * const my_val;
    const char * const my_trans;
};

// parse 1 line of bbdb format, of course it is easier in elisp :)
Id * 
parse( int const debug, int const  tokmax, char const first_char,  std::string const& line ) {

    Id * id = new Id;

    int jb=0;
    int je=0;
    int l=line.size();
    int toknb=0;
    
    // sanity check
    if( line[jb] == first_char )
	++jb;
    
    // loop over line
    // invariant
    //   jb pointe sur le premier caractere du token
    //   je pointe sur le premier caractere apres la fin du token
    while( jb < l ) {
	// look for beginning of token
	char c = line[jb];
	while( jb < l && c != '(' && c != '[' && c != '"' && c != ' ' && !isalpha(c) )
	    ++jb;
	if( jb == l )
	    break;
	// current state
	char state = line[jb];
	// look for end of token
	switch( state )	{
	case '(': 
	{
	    int cnt_lpar = 1;
	    je = jb+1;
	    for( ; je < l ; ++je ) {
		if( debug >= 2 )
		    trace::print(line,jb,je);
		if( line[je] == ')' ) {
		    --cnt_lpar;
		    if( cnt_lpar == 0 ) {
			++je;
			break;
		    }
		}
		if( line[je] == '(' )
		    ++cnt_lpar;
	    }
	    break;
	}
	case '[': 
	{
	    int cnt_lbra = 1;
	    je = jb+1;
	    for( ; je < l ; ++je ) {
		if( debug >= 2 ) 
		    trace::print(line,jb,je);
		if( line[je] == ']' ) {
		    --cnt_lbra;
		    if( cnt_lbra == 0 ) {
			++je;
			break;
		    }
		}
		if( line[je] == '[' )
		    ++cnt_lbra;
	    }
	    break;
	}
	case '"': 
	{
	    je = jb+1;
	    while( je < l && line[je] != state )
		++je;
	    ++je;
	    break;
	}
	case ' ': 
	{
	    je = jb+1;
	    while( je < l && line[je] != state )
		++je;
	    ++je;
	    break;
	}
	case 'n': // nil && notes
	{ 
	    je = jb;
	    ++je;
	    if( line[je] == 'i' ) 
		while( je < l && line[je] != ' ' ) 
		    ++je;
	    else {
		while( je < l && line[je] != '.' ) 
		    ++je;
		++je;
	    }
	    break;
	}
	case 'c': // creation-date
	case 't': // timestamp
	case 'h': // http
	default:
	{
	    je = jb;
	    while( je < l && line[je] != '.' )  
		++je;
	    ++je;
	    break;
	}
	} // end case
	if( debug >= 1 )
	    trace::print(line,jb,je);
	// fill structure
	if( line[jb] == '"' ) 
	    id->my_d.push_back( line.substr(jb+1,je-jb-2) ); 
	else 
	    id->my_d.push_back( line.substr(jb,je-jb) ); 
	// loop
	++toknb;
	jb = je + 1;
    }

    if( toknb < tokmax )
    {
	delete id;
	return NULL;
    }
    
    return id;
}

// ----------------------------------------------------------------------
// GNOKII
// ----------------------------------------------------------------------
void tognokii( std::string const& l )
{
    Id * id = parse( trace::NONE, 9, '[', l);
    if( id == NULL ) {
	if( l.size() > 2 )
	    std::cerr << "Broken line ==>" << l << "<==" << std::endl;
	return;
    }
    
    Id::storage_t const& s = id->my_d;
    
    // check phone(s)
    if( s[Id::PHONES].compare( "nil" ) != 0 ) {
	Id * phones = new Id;
	phones = parse( trace::NONE, 1, '(', s[Id::PHONES] );
	if( phones == NULL ) {
	    std::cerr << "can't parse phones ==>" << s[Id::PHONES] << "<==" << std::endl;
	    delete id;
	    return;
	}

	std::cout << s[Id::NAME] << '\t' << s[Id::FORNAME] << '\t';

	Id * phone = new Id;
	for( int cur_phone = 0 ; cur_phone < phones->my_d.size() ; ++cur_phone ) {
	    phone = parse( trace::NONE, 1, '[', phones->my_d[cur_phone] );

	    if( phone != NULL ) {
		std::cout << phone->my_d[1];
		std::cout << '\t';
	    }
	}
	std::cout << std::endl;
	delete phone;
	delete phones;
    }
    
    delete id;
}



// ----------------------------------------------------------------------
// VCARD
// ----------------------------------------------------------------------
static two_strings VCARDPhoneTypes [] =
{
    // PERSONAL       VCARD
    { "home",         "home"  },
    { "tel",          "home"  },
    { "perso",        "home"  },
    { "personnel",    "home"  },
    { "personel",     "home"  },
    { "monchat",      "home"  },
    { "msg",          "msg"   },
    { "mesg",         "msg"   },
    { "work",         "work"  },
    { "travail",      "work"  },
    { "paris",        "work"  },
    { "lyon",         "work"  },
    { "issy",         "work"  },
    { "insa",         "work"  },
    { "woo",          "work"  },
    { "fac",          "work"  },
    { "voice",        "voice" },
    { "voie",         "voice" },
    { "fax",          "fax"   },
    { "cell",         "cell"  },
    { "portable",     "cell"  },
    { "gsm",          "cell"  },
    { "video",        "video" },
    { "pager",        "pager" },
    { "tatoo",        "pager" },
    { "voiture",      "car"   },
    { "modem",        "modem" },
    { "isdn",         "isdn"  },
    { "pcs",          "pcs"   },
    { 0,              0       }
};

struct VCARDPhoneTypesMap {
    static two_strings const my_default [];
    struct ltstr {
	inline bool 
	operator()(const char* s1, const char* s2) const {
	    return strcmp(s1, s2) < 0;
	}
    };
    
    VCARDPhoneTypesMap() {
	unsigned short i = 0;
	while( VCARDPhoneTypes[i].my_val != 0 )	{
	    my_h[ VCARDPhoneTypes[i].my_val ] = VCARDPhoneTypes[i].my_trans;
	    ++i;
	}
    }
    
    typedef std::map<const char*,const char*,ltstr> hash_t;
    hash_t my_h;
};

two_strings const VCARDPhoneTypesMap::my_default [] = 
    {
	{ "work", "work" },
	{ "fax", "fax" },
	{ "cell", "cell" },
	{ 0, 0 }
    };

static VCARDPhoneTypesMap vcard_phone_types_map;

void tovcard( std::string const& l )
{
    Id * id = parse( trace::NONE, 9,'[',l);
    if( id == NULL ) {
	if( l.size() > 2 )
	    std::cerr << "Broken line ==>" << l << "<==" << std::endl;
	return;
    }
    
    Id::storage_t const& s = id->my_d;
    
    // std::string filename( s[Id::FORNAME] + "_" + s[Id::NAME] + ".vcf" );
    // std::ofstream os( filename.c_str() );

    std::cout << "BEGIN:VCARD" << std::endl;
    
    std::cout << "FN:" << s[Id::NAME] << ' ' << s[Id::FORNAME] << std::endl;
    std::cout << "N:" << s[Id::FORNAME] << ';' << s[Id::NAME] << std::endl;

    if( s[Id::PHONES].compare( "nil" ) != 0 ) {
	Id * phones = new Id;
	phones = parse( trace::NONE, 1, '(', s[Id::PHONES] );
	if( phones == NULL ) {
	    std::cerr << "can't parse phones ==>" << s[Id::PHONES] << "<==" << std::endl;
	    delete id;
	    return;
	}

	Id * phone = new Id;
	for( int cur_phone = 0 ; cur_phone < phones->my_d.size() ; ++cur_phone ) {
	    phone = parse( trace::NONE, 1, '[', phones->my_d[cur_phone] );
	    if( phone != NULL ) {
		std::cout << "TEL";
		if( phone->my_d[0].find_first_of( "0123456789 _-:;," ) == phone->my_d[0].npos )	{
		    VCARDPhoneTypesMap::hash_t::iterator it;
		    if( (it=vcard_phone_types_map.my_h.find( const_cast<char*>(phone->my_d[0].c_str()))) != vcard_phone_types_map.my_h.end() ) {
			std::cout << ";" << it->second;
		    } else {
			std::cout << ";" << vcard_phone_types_map.my_default[0].my_trans;
		    }
		}
		if( phone->my_d.size() > 1 )
		    std::cout << ':' << phone->my_d[1] << std::endl;
		else 
		    std::cerr << "phone size is less than 2" << std::endl;
	    }
	}
	delete phone;
	delete phones;
    }

    if( s[Id::ADRESSES].compare( "nil" ) != 0 ) {
	Id * adresses = new Id;
	adresses = parse( trace::NONE, 1, '(', s[Id::ADRESSES] );
	if( adresses == NULL ) {
	    std::cerr << "can't parse adresses ==>" << s[Id::ADRESSES] << "<==" << std::endl;
	    delete id;
	    return;
	}

	Id * adresse = new Id;
	for( int cur_adresse = 0 ; cur_adresse < adresses->my_d.size() ; ++cur_adresse ) {
	    adresse = parse( trace::NONE, 1, '[', adresses->my_d[cur_adresse] );
	    if( adresse != NULL ) {
		// split again 
		Id * fields = new Id;
		fields = parse( trace::NONE, 1, '(' , adresse->my_d[1] );
		if( fields != NULL ) {
		    std::string const& town    = adresse->my_d[2];
		    std::string const& state   = adresse->my_d[3];
		    std::string const& postal  = adresse->my_d[4];
		    std::string const& country = adresse->my_d[5];
		    std::cout << "ADR";
		    if( adresse->my_d[0].size() > 0 )
			if( adresse->my_d[0].find_first_of( "0123456789 _-:;," ) == adresse->my_d[0].npos ) 
			    std::cout << ";TYPE=" << adresse->my_d[0];
		    std::cout << ':' << ';' << ';' << fields->my_d[0];
		    for( int i=1 ; i<fields->my_d.size() ; ++i )
			std::cout << ',' << fields->my_d[i] ;
		    std::cout << ';' << town << ';' << state << ';' << postal << ';' << country << ';' << std::endl;
		}
		else
		    std::cerr << "unknown adresse format for ==>" << adresse->my_d[1] << "<<=" << std::endl;
		
		delete fields;
	    }
	}
	delete adresse;
	delete adresses;
    }

    if( s[Id::NET].compare("nil") != 0 ) {
	Id * nets = new Id;
	nets = parse( trace::NONE, 1, '(', s[Id::NET] );
	if( nets == NULL ) {
	    std::cerr << "can't parse nets ==>" << s[Id::NET] << "<==" << std::endl;
	    delete id;
	    return;
	}

	std::cout << "EMAIL;INTERNET;PREF:" << nets->my_d[0] << std::endl;
	for( int cur_net = 1 ; cur_net < nets->my_d.size() ; ++cur_net )
	    std::cout << "EMAIL;INTERNET:" << nets->my_d[cur_net] << std::endl;

	delete nets;
    }
    
    if( s[Id::COMPANY].compare("nil") != 0 )
	std::cout << "ORG:" << s[Id::COMPANY] << std::endl;
    
    if( s[Id::AKA].compare("nil") != 0 ) {
	Id * akas = new Id;
	if( s[Id::AKA].find('(') == s[Id::AKA].npos )
	    std::cout << "NICKNAME:" << s[Id::AKA] << std::endl;
	else
	{
	    akas = parse( trace::NONE, 1, '(', s[Id::AKA] );
	    if( akas == NULL ) {
		std::cerr << "can't parse akas ==>" << s[Id::AKA] << "<==" << std::endl;
		delete id;
		return;
	    }
	    
	    Id * aka = new Id;
	    for( int cur_aka = 0 ; cur_aka < akas->my_d.size() ; ++cur_aka ) {
		aka = parse( trace::NONE, 1, '(', akas->my_d[cur_aka] );
		if( aka != NULL )
		    std::cout << "NICKNAME:" << aka->my_d[0] << std::endl;
	    }
	    delete aka;
	}
	delete akas;
    }
    
    if( s[Id::NOTES].compare("nil") != 0 ) {
	Id * notes = new Id;
	if( s[Id::NOTES].find('(') == s[Id::NOTES].npos ) 
	    std::cout << "NOTE:" << s[Id::NOTES] << std::endl;
	else {
	    notes = parse( trace::NONE, 1, '(', s[Id::NOTES] );
	    if( notes == NULL ) {
		std::cerr << "can't parse notes ==>" << s[Id::NOTES] << "<==" << std::endl;
		delete id;
		return;
	    }
	    
	    Id * note = new Id;
	    for( int cur_note = 0 ; cur_note < notes->my_d.size() ; ++cur_note ) {
		note = parse( trace::NONE, 1, '(', notes->my_d[cur_note] );
		if( note != NULL ) {
		    std::string const& t = note->my_d[0];
		    std::string const& r = note->my_d[1];

		    if( t.find("creation-date") != t.npos || t.find("timestamp") != t.npos ) {
			; // drop 
		    } else if( t.find("www") != t.npos || t.find("http") != t.npos ) {
			std::cout << "URL:" << r << std::endl;
		    } else if( t.find("notes") != t.npos ) {
			std::cout << "NOTE:" << r << std::endl;
		    } else if( t.find("icq") != t.npos ) {
			std::cout << "NOTE: ICQ #" << r << std::endl;
		    } else if( t.find("msn") != t.npos ) {
			std::cout << "NOTE: MSN " << r << std::endl;
		    } else {
			std::cerr << "drop notes: " << t << " for " << s[Id::NAME] << std::endl;
		    }
		}
	    }
	    delete note;
	}
	delete notes;
    }
    
    std::cout << "END:VCARD" << std::endl << std::endl;
    delete id;
    // os.close();
}


// ----------------------------------------------------------------------
// ABOOK
// ----------------------------------------------------------------------

static two_strings ABOOKPhoneTypes [] =
{
    { "home",         "phone" },
    { "tel",          "phone" },
    { "perso",        "phone" },
    { "personnel",    "phone" },
    { "personel",     "phone" },
    { "monchat",      "phone" },
    { "msg",          "phone" },
    { "mesg",         "phone" },
    { "work",         "workphone" },
    { "travail",      "workphone" },
    { "paris",        "workphone" },
    { "lyon",         "workphone" },
    { "issy",         "workphone" },
    { "insa",         "workphone" },
    { "woo",          "workphone" },
    { "fac",          "workphone" },
    { "voice",        "phone" },
    { "voie",         "phone" },
    { "fax",          "fax"   },
    { "cell",         "mobile" },
    { "portable",     "mobile" },
    { "gsm",          "mobile" },
    { "video",        "phone" },
    { "pager",        "phone" },
    { "tatoo",        "phone" },
    { "voiture",      "phone" },
    { "modem",        "phone" },
    { "isdn",         "phone" },
    { "pcs",          "phone" },
    { 0,              0       }
};

struct ABOOKPhoneTypesMap {
    static two_strings const my_default [];
    struct ltstr {
	inline bool 
	operator()(const char* s1, const char* s2) const {
	    return strcmp(s1, s2) < 0;
	}
    };
    
    ABOOKPhoneTypesMap() {
	unsigned short i = 0;
	while( ABOOKPhoneTypes[i].my_val != 0 )	{
	    my_h[ ABOOKPhoneTypes[i].my_val ] = ABOOKPhoneTypes[i].my_trans;
	    ++i;
	}
    }
    
    typedef std::map<const char*,const char*,ltstr> hash_t;
    hash_t my_h;
};

two_strings const ABOOKPhoneTypesMap::my_default [] = {
    // VCARD   ABOOK
    { "*",  "phone" },
    { 0, 0 }
};

static ABOOKPhoneTypesMap abook_phone_types_map;

void toabook( std::string const& l ) {
    Id * id = parse( trace::NONE, 9,'[',l);
    if( id == NULL ) {
	if( l.size() > 2 )
	    std::cerr << "Broken line ==>" << l << "<==" << std::endl;
	return;
    }
    
    Id::storage_t const& s = id->my_d;
    
    std::cout << '[' << counter << ']' << std::endl;
    ++counter;
    
    std::cout << "name=" << s[Id::FORNAME] << ' ' << s[Id::NAME] << std::endl;

    if( s[Id::NET].compare("nil") != 0 ) {
	Id * nets = new Id;
	nets = parse( trace::NONE, 1, '(', s[Id::NET] );
	if( nets == NULL ) {
	    std::cerr << "can't parse nets ==>" << s[Id::NET] << "<==" << std::endl;
	    delete id;
	    return;
	}

	std::cout << "email=" << nets->my_d[0];
	for( int cur_net = 1 ; cur_net < nets->my_d.size() ; ++cur_net ) {
	    std::cout << "," << nets->my_d[cur_net];
	}
	std::cout << std::endl;

	delete nets;
    }
    
    if( s[Id::PHONES].compare( "nil" ) != 0 ) {
	Id * phones = new Id;
	phones = parse( trace::NONE, 1, '(', s[Id::PHONES] );
	if( phones == NULL ) {
	    std::cerr << "can't parse phones ==>" << s[Id::PHONES] << "<==" << std::endl;
	    delete id;
	    return;
	}

	Id * phone = new Id;
	for( int cur_phone = 0 ; cur_phone < phones->my_d.size() ; ++cur_phone ) {
	    phone = parse( trace::NONE, 1, '[', phones->my_d[cur_phone] );
	    if( phone != NULL ) {
		if( phone->my_d[0].find_first_of( "0123456789 _-:;," ) == phone->my_d[0].npos )	{
		    ABOOKPhoneTypesMap::hash_t::iterator it;
		    if( (it=abook_phone_types_map.my_h.find( const_cast<char*>(phone->my_d[0].c_str())))
			!= abook_phone_types_map.my_h.end() ) {
			std::cout << it->second;
		    } else {
			std::cout << abook_phone_types_map.my_default[0].my_trans;
		    }
		}
		if( phone->my_d.size() > 1 )
		    std::cout << '=' << phone->my_d[1] << std::endl;
		else
		    std::cerr << "phone size is less than 2" << std::endl;
	    }
	}
	delete phone;
	delete phones;
    }

    if( s[Id::ADRESSES].compare( "nil" ) != 0 ) {
	Id * adresses = new Id;
	adresses = parse( trace::NONE, 1, '(', s[Id::ADRESSES] );
	if( adresses == NULL ) {
	    std::cerr << "can't parse adresses ==>" << s[Id::ADRESSES] << "<==" << std::endl;
	    delete id;
	    return;
	}

	Id * adresse = new Id;
	for( int cur_adresse = 0 ; cur_adresse < adresses->my_d.size() ; ++cur_adresse ) {
	    adresse = parse( trace::NONE, 1, '[', adresses->my_d[cur_adresse] );
	    if( adresse != NULL ) {
		// split again 
		Id * fields = new Id;
		fields = parse( trace::NONE, 1, '(' , adresse->my_d[1] );
		if( fields != NULL ) {
		    std::string const& town    = adresse->my_d[2];
		    std::string const& state   = adresse->my_d[3];
		    std::string const& postal  = adresse->my_d[4];
		    std::string const& country = adresse->my_d[5];
			
		    std::cout << "address=";
		    for( int i=0 ; i<fields->my_d.size() ; ++i )
			std::cout << fields->my_d[i] << ' ';
		    std::cout << std::endl;
		    
		    std::cout << "city=" << town << std::endl;
		    std::cout << "state=" << state << std::endl;
		    std::cout << "zip="   << postal << std::endl;
		    std::cout << "country=" << country << std::endl;
		} else {
		    std::cerr << "unknown adresse format for ==>" << adresse->my_d[1] << "<<=" << std::endl;
		}
		delete fields;
	    }
	}
	delete adresse;
	delete adresses;
    }

    if( s[Id::AKA].compare("nil") != 0 )
    {
	Id * akas = new Id;
	if( s[Id::AKA].find('(') == s[Id::AKA].npos )
	{
	    std::cout << "nick=" << s[Id::AKA] << std::endl;
	}
	delete akas;
    }
    
    if( s[Id::NOTES].compare("nil") != 0 ) {
	Id * notes = new Id;
	if( s[Id::NOTES].find('(') == s[Id::NOTES].npos ) {
	    std::cout << "notes=" << s[Id::NOTES] << std::endl;
	} else {
	    notes = parse( trace::NONE, 1, '(', s[Id::NOTES] );
	    if( notes == NULL ) {
		std::cerr << "can't parse notes ==>" << s[Id::NOTES] << "<==" << std::endl;
		delete id;
		return;
	    }
	    
	    Id * note = new Id;
	    for( int cur_note = 0 ; cur_note < notes->my_d.size() ; ++cur_note ) {
		note = parse( trace::NONE, 1, '(', notes->my_d[cur_note] );
		
		if( note != NULL ) {
		    std::string const& t = note->my_d[0];
		    std::string const& r = note->my_d[1];

		    if( t.find("creation-date") != t.npos || t.find("timestamp") != t.npos ) {
			; // drop 
		    } else if( t.find("www") != t.npos || t.find("http") != t.npos ) {
			std::cout << "url=" << r << std::endl;
		    } else if( t.find("notes") != t.npos ) {
			std::cout << "note=" << r << std::endl;
		    } else if( t.find("icq") != t.npos ) {
			std::cout << "note=icq #" << r << std::endl;
		    } else if( t.find("msn") != t.npos ) {
			std::cout << "note=msn" << r << std::endl;
		    } else {
			std::cerr << "drop notes: " << t << " for " << s[Id::NAME] << std::endl;
		    }
		}
	    }
	    delete note;
	    delete notes;
	}
    }
    
    std::cout << std::endl;
    delete id;
}

// ----------------------------------------------------------------------
// CSV
// ----------------------------------------------------------------------
void tocsv( std::string const& l )
{
    char const sep = ',';
    char const quo = '"';

    Id * id = parse( trace::NONE, 9,'[',l);
    if( id == NULL )
    {
	if( l.size() > 2 )
	    std::cerr << "Broken line ==>" << l << "<==" << std::endl;
	return;
    }
    
    Id::storage_t const& s = id->my_d;

    // "First Name","Last Name","Display Name"
    std::cout << quo << s[Id::NAME] << quo << sep;
    std::cout << quo << s[Id::FORNAME] << quo << sep;
    std::cout << quo << s[Id::FORNAME] << ' ' << s[Id::NAME] << quo << sep;

    // "Nickname Name"
    std::string s_aka;
    if( s[Id::AKA].compare("nil") != 0 )
    {
	Id * akas = new Id;
	if( s[Id::AKA].find('(') == s[Id::AKA].npos )
	{
	    std::cout << quo << s[Id::AKA] << quo << sep ;
	}
	else
	{
	    akas = parse( trace::NONE, 1, '(', s[Id::AKA] );
	    if( akas == NULL ) 
	    {
		std::cerr << "can't parse akas ==>" << s[Id::AKA] << "<==" << std::endl;
		delete id;
		return;
	    }
	    
	    Id * aka = new Id;
	    aka = parse( trace::NONE, 1, '(', akas->my_d[0] );
	    if( aka != NULL )
	    {
		s_aka = quo + aka->my_d[0] + quo;
	    }
	    delete aka;
	    delete akas;
	}
    }
    std::cout << s_aka << sep;
    
    // "E-mail","Secondary E-mail"
    std::string s_email1;
    std::string s_email2;
    if( s[Id::NET].compare("nil") != 0 )
    {
	Id * nets = new Id;
	nets = parse( trace::NONE, 1, '(', s[Id::NET] );
	if( nets == NULL ) 
	{
	    std::cerr << "can't parse nets ==>" << s[Id::NET] << "<==" << std::endl;
	    delete id;
	    return;
	}

	s_email1 = quo + nets->my_d[0] + quo;
	if( nets->my_d.size() > 1 )
	{
	    s_email2 = quo + nets->my_d[1] + quo;
	}
	delete nets;
    }
    std::cout << s_email1 << sep << s_email2 << sep;

    // "Business Phone","Home Phone","Fax Phone","Pager","Mobile Phone"
    std::string p_business;
    std::string p_home;
    std::string p_fax;
    std::string p_pager;
    std::string p_mobile;

    if( s[Id::PHONES].compare( "nil" ) != 0 )
    {
	Id * phones = new Id;
	phones = parse( trace::NONE, 1, '(', s[Id::PHONES] );
	if( phones == NULL ) 
	{
	    std::cerr << "can't parse phones ==>" << s[Id::PHONES] << "<==" << std::endl;
	    delete id;
	    return;
	}

	Id * phone = new Id;
	int cur_phone;
	for( cur_phone = 0 ; cur_phone < std::min(size_t(5),phones->my_d.size()) ; ++cur_phone )
	{
	    phone = parse( trace::NONE, 1, '[', phones->my_d[cur_phone] );

	    if( phone != NULL )
	    {
		std::string trans;
 		if( phone->my_d[0].find_first_of( "0123456789 _-:;," ) == phone->my_d[0].npos )
 		{
 		    ABOOKPhoneTypesMap::hash_t::iterator it;
 		    if( (it=abook_phone_types_map.my_h.find( const_cast<char*>(phone->my_d[0].c_str())))
 			!= abook_phone_types_map.my_h.end() )
 		    {
 			trans = it->second;
 		    }
 		    else
 		    {
 			trans = abook_phone_types_map.my_default[0].my_trans;
 		    }
 		}
		if( trans.find("home") != trans.npos )
		{
		    p_home = quo + phone->my_d[1] + quo ;
		}
		else if( trans.find("work") != trans.npos )
		{
		    p_business = quo + phone->my_d[1] + quo ;
		}
		else if( trans.find("cell") != trans.npos )
		{
		    p_mobile = quo + phone->my_d[1] + quo ;
		}
		else if( trans.find("fax") != trans.npos )
		{
		    p_fax = quo + phone->my_d[1] + quo ;
		}
		else if( trans.find("pager") != trans.npos )
		{
		    p_pager = quo + phone->my_d[1] + quo ;
		}
		else 
		{
		    if( p_home.size() == 0 )
		    {
			p_home = quo + phone->my_d[1] + quo ;
		    }
		}
	    }
	}
	delete phone;
	delete phones;
    }
    std::cout << p_business << sep 
	      << p_home << sep 
	      << p_fax << sep 
	      << p_pager << sep 
	      << p_mobile << sep;

    // "Home Street 1","Home Street 2","Home City","Home State","Home Postal Code","Home Country",
    // "Business Street 1","Business Street 2","Business City","Business State","Business Postal Code","Business Country"
    if( s[Id::ADRESSES].compare( "nil" ) != 0 )
    {
	Id * adresses = new Id;
	adresses = parse( trace::NONE, 1, '(', s[Id::ADRESSES] );
	if( adresses == NULL ) 
	{
	    std::cerr << "can't parse adresses ==>" << s[Id::ADRESSES] << "<==" << std::endl;
	    delete id;
	    return;
	}

	Id * adresse = new Id;
	int cur_adresse;
	for( cur_adresse = 0 ; cur_adresse < std::min(size_t(2),adresses->my_d.size()) ; ++cur_adresse )
	{
	    adresse = parse( trace::NONE, 1, '[', adresses->my_d[cur_adresse] );

	    if( adresse != NULL )
	    {
		// split again 
		Id * fields = new Id;
		fields = parse( trace::NONE, 1, '(' , adresse->my_d[1] );
		if( fields != NULL )
		{
		    std::string const& town    = adresse->my_d[2];
		    std::string const& state   = adresse->my_d[3];
		    std::string const& postal  = adresse->my_d[4];
		    std::string const& country = adresse->my_d[5];
			
// 		    if( adresse->my_d[0].size() > 0 ) 
// 		    {
// 			if( adresse->my_d[0].find_first_of( "0123456789 _-:;," ) == 
// 			    adresse->my_d[0].npos ) 
// 			{
// 			    std::cout << ";TYPE=" << adresse->my_d[0];
// 			}
// 		    }
		    int i;
		    for( i=0 ; i<std::min(size_t(2),fields->my_d.size()) ; ++i )
			std::cout << quo << fields->my_d[i] << quo << sep;
		    for( i=std::min(size_t(2),fields->my_d.size()) ; i<2; ++i )
			std::cout << sep;
		    std::cout << quo << town << quo << sep
			      << quo << state << quo << sep
			      << quo << postal << quo << sep
			      << quo << country << quo << sep;
		}
		else
		{
		    std::cerr << "unknown adresse format for ==>" << adresse->my_d[1] 
			      << "<<=" << std::endl;
		}
		delete fields;
	    }
	}
	for( cur_adresse = std::min(size_t(2),adresses->my_d.size()) ; cur_adresse<2; ++cur_adresse ) 
	{
	    std::cout << sep << sep << sep << sep << sep << sep;
	}
	delete adresse;
	delete adresses;
    }
    else
    {
	std::cout << sep << sep << sep << sep << sep << sep
		  << sep << sep << sep << sep << sep << sep;
    }

    // "Job Title","Department","Company"
    std::string s_title;
    std::string s_departement;
    std::string s_company;
    if( s[Id::COMPANY].compare("nil") != 0 )
    {
	s_company =  quo + s[Id::COMPANY] + quo;
    }
    std::cout << s_title << sep
	      << s_departement << sep
	      << s_company << sep;

    // "Web Page 1","Web Page 2","Birth Year","Birth Month","Birth Day","User Field 1","User Field 2","User Field 3","User Field 4","Notes" 
    std::string n_web1;
    std::string n_web2;
    std::string n_byear;
    std::string n_bmonth;
    std::string n_bday;
    std::string n_user1; // icq
    std::string n_user2; // im
    std::string n_user3;
    std::string n_user4;
    std::string n_note;
    
    if( s[Id::NOTES].compare("nil") != 0 )
    {
	Id * notes = new Id;
	if( s[Id::NOTES].find('(') == s[Id::NOTES].npos )
	{
	    n_note = quo + s[Id::NOTES] + quo;
	}
	else
	{
	    notes = parse( trace::NONE, 1, '(', s[Id::NOTES] );
	    if( notes == NULL ) 
	    {
		std::cerr << "can't parse notes ==>" << s[Id::NOTES] << "<==" ;
		delete id;
		return;
	    }
	    
	    Id * note = new Id;
	    int cur_note;
	    for( cur_note = 0 ; cur_note < std::min(size_t(10),notes->my_d.size()) ; ++cur_note )
	    {
		note = parse( trace::NONE, 1, '(', notes->my_d[cur_note] );
		
		if( note != NULL )
		{
		    std::string const& t = note->my_d[0];
		    std::string const& r = note->my_d[1];

		    if( t.find("www") != t.npos || t.find("http") != t.npos )
		    {
			if( n_web1.size() == 0 )
			{
			    n_web1 = quo + r + quo;
			}
			else
			{
			    n_web2 = quo + r + quo;
			}
		    }
		    else if( t.find("icq") != t.npos )
		    {
			n_user1 = quo + r + quo;
		    }
		    else if( t.find("notes") != t.npos )
		    {
			n_note = quo + r + quo;
		    }
		}
	    }
	    delete note;
	    delete notes;
	}
    }

    std::cout << n_web1  << sep << n_web2   << sep 
	      << n_byear << sep << n_bmonth << sep << n_bday  << sep 
	      << n_user1 << sep << n_user2  << sep << n_user3 << sep << n_user4 << sep 
	      << n_note;
    
    std::cout << std::endl;
    delete id;
}


// ----------------------------------------------------------------------
// USAGE
// ----------------------------------------------------------------------
int 
usage(int argc, char * argv[] )
{
    std::cerr << argv[0] << " -t (vcard|gnokii|csv|abook) -l (fr|en) bbdb.txt" << std::endl;
    std::cerr << "argc=" << argc << std::endl;
    
    return 1;
}


// ----------------------------------------------------------------------
// main
// ----------------------------------------------------------------------
int
main( int argc, char * argv[] )
{
    if( argc < 4 ) 
    {
 	return usage(argc,argv);
    }

    char t;
    char * lg = "en";
    if( argc == 4 && !strcmp(argv[1],"-t") )
    {
	if( !strcmp(argv[2],"vcard") )
	{
	    t = 'v';
	} 
	else if( !strcmp(argv[2],"csv") )
	{
	    t = 'c';
	} 
	else if( !strcmp(argv[2],"gnokii") )
	{
	    t = 'g';
	} 
	else if( !strcmp(argv[2],"abook") )
	{
	    t = 'a';
	} 
	else 
	{
	    return usage(argc,argv);    
	}
    }
    else if( argc == 6 && !strcmp(argv[3],"-l") )
    {
	lg = argv[4];
	if( strcmp(lg,"fr") && strcmp(argv[4],"en") )
	{
	    return usage(argc,argv);    
	}
    }
    else
    {
	return usage(argc,argv);    
    }
     
    std::vector<std::string> v;

    // open file
    std::ifstream fs;
    fs.open( argv[argc-1], std::ios::in );
    if( ! fs.is_open() )
    {
	std::cerr << "can't open " << argv[argc-1] << std::endl;
	return 1;
    }
    while( ! fs.eof() )
    {
	char data[1024];
	fs.getline(data,1024);
	if( data[0] != ';' )
	{
	    v.push_back( data );
	}
    }
    fs.close();

    // parse v
    switch( t )
    {
    case 'g':
	std::for_each( v.begin(), v.end(), tognokii );
	break;
    case 'v':
	std::for_each( v.begin(), v.end(), tovcard );
	break;
    case 'a':
	std::for_each( v.begin(), v.end(), toabook );
	break;
    case 'c': {
	if( !strcmp( lg, "en" ))
	{
	    std::cout << "\"First Name\",\"Last Name\",\"Display Name\",\"Nickname Name\",\"E-mail\",\"Secondary E-mail\",\"Business Phone\",\"Home Phone\",\"Fax Phone\",\"Pager\",\"Mobile Phone\",\"Home Street 1\",\"Home Street 2\",\"Home City\",\"Home State\",\"Home Postal Code\",\"Home Country\",\"Business Street 1\",\"Business Street 2\",\"Business City\",\"Business State\",\"Business Postal Code\",\"Business Country\",\"Job Title\",\"Department\",\"Company\",\"Web Page 1\",\"Web Page 2\",\"Birth Year\",\"Birth Month\",\"Birth Day\",\"User Field 1\",\"User Field 2\",\"User Field 3\",\"User Field 4\",\"Notes\"" << std::endl;
	}
	else 
	{
	    std::cout << "\"Prénom\",\"Nom\",\"Display Name\",\"Nickname Name\",\"E-mail\",\"Secondary E-mail\",\"Business Phone\",\"Téléphone\",\"Fax Phone\",\"Pager\",\"Mobile Phone\",\"Home Street 1\",\"Home Street 2\",\"Home City\",\"Home State\",\"Home Postal Code\",\"Home Country\",\"Business Street 1\",\"Business Street 2\",\"Business City\",\"Business State\",\"Business Postal Code\",\"Business Country\",\"Job Title\",\"Department\",\"Company\",\"Web Page 1\",\"Web Page 2\",\"Birth Year\",\"Birth Month\",\"Birth Day\",\"User Field 1\",\"User Field 2\",\"User Field 3\",\"User Field 4\",\"Notes\"" << std::endl;
	}
	
	std::for_each( v.begin(), v.end(), tocsv );
	break;
    }
    } // end switch

    // open output file

    return 0;
}

