/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is 
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or 
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the NPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the NPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */


/*

  A sample of XPConnect. This file contains an implementation of
  nsISample.

*/
#include "nscore.h"
#include "nsCRT.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsIImportService.h"
#include "nsIImportFieldMap.h"
#include "nsABBaseCID.h"
#include "nsIAbCard.h"

#include "nsOEAddressIterator.h"

#include "OEDebugLog.h"

static NS_DEFINE_CID(kAbCardPropertyCID,	NS_ABCARDPROPERTY_CID);

typedef struct {
	PRInt32		mozField;
	PRInt32		multiLine;
	ULONG		mapiTag;
} MAPIFields;

/*
	Fields in MAPI, not in Mozilla
	PR_OFFICE_LOCATION
	FIX - PR_BIRTHDAY - stored as PT_SYSTIME - FIX to extract for moz address book birthday
	PR_DISPLAY_NAME_PREFIX - Mr., Mrs. Dr., etc.
	PR_SPOUSE_NAME
	PR_GENDER - integer, not text
	FIX - PR_CONTACT_EMAIL_ADDRESSES - multiuline strings for email addresses, needs
		parsing to get secondary email address for mozilla
*/

#define kIsMultiLine	-2
#define	kNoMultiLine	-1

static MAPIFields	gMapiFields[] = {
	{ 35, kIsMultiLine, PR_COMMENT},
	{ 6, kNoMultiLine, PR_BUSINESS_TELEPHONE_NUMBER},
	{ 7, kNoMultiLine, PR_HOME_TELEPHONE_NUMBER},
	{ 25, kNoMultiLine, PR_COMPANY_NAME},
	{ 23, kNoMultiLine, PR_TITLE},
	{ 10, kNoMultiLine, PR_CELLULAR_TELEPHONE_NUMBER},
	{ 9, kNoMultiLine, PR_PAGER_TELEPHONE_NUMBER},
	{ 8, kNoMultiLine, PR_BUSINESS_FAX_NUMBER},
	{ 8, kNoMultiLine, PR_HOME_FAX_NUMBER},
	{ 22, kNoMultiLine, PR_COUNTRY},
	{ 19, kNoMultiLine, PR_LOCALITY},
	{ 20, kNoMultiLine, PR_STATE_OR_PROVINCE},
	{ 17, 18, PR_STREET_ADDRESS},
	{ 21, kNoMultiLine, PR_POSTAL_CODE},
	{ 27, kNoMultiLine, PR_PERSONAL_HOME_PAGE},
	{ 26, kNoMultiLine, PR_BUSINESS_HOME_PAGE},
	{ 13, kNoMultiLine, PR_HOME_ADDRESS_CITY},
	{ 16, kNoMultiLine, PR_HOME_ADDRESS_COUNTRY},
	{ 15, kNoMultiLine, PR_HOME_ADDRESS_POSTAL_CODE},
	{ 14, kNoMultiLine, PR_HOME_ADDRESS_STATE_OR_PROVINCE},
	{ 11, 12, PR_HOME_ADDRESS_STREET},
	{ 24, kNoMultiLine, PR_DEPARTMENT_NAME}
};

nsOEAddressIterator::nsOEAddressIterator( CWAB *pWab, nsIAddrDatabase *database)
{
	m_pWab = pWab;
	m_database = database;
	NS_IF_ADDREF( m_database);
}

nsOEAddressIterator::~nsOEAddressIterator()
{
	NS_IF_RELEASE( m_database);
}

PRBool nsOEAddressIterator::EnumUser( const PRUnichar * pName, LPENTRYID pEid, ULONG cbEid)
{
	IMPORT_LOG1( "User: %S\n", pName);
	
	nsresult 	rv = NS_OK;
	
	if (m_database) {
		LPMAILUSER	pUser = m_pWab->GetUser( cbEid, pEid);
		if (pUser) {
			// Get a new row from the database!
			nsIMdbRow* newRow = nsnull;
			m_database->GetNewRow( &newRow); 
			// FIXME: Check with Candice about releasing the newRow if it
			// isn't added to the database.  Candice's code in nsAddressBook
			// never releases it but that doesn't seem right to me!
			if (newRow) {
				if (BuildCard( pName, newRow, pUser)) {
					m_database->AddCardRowToDB( newRow);
					IMPORT_LOG0( "* Added entry to address book database\n");
				}
			}
			m_pWab->ReleaseUser( pUser);
		}
	}	
	
	return( PR_TRUE);
}

PRBool nsOEAddressIterator::EnumList( const PRUnichar * pName, LPENTRYID pEid, ULONG cbEid)
{
	IMPORT_LOG1( "List: %S\n", pName);
	return( PR_TRUE);
}

void nsOEAddressIterator::SanitizeValue( nsString& val)
{
	val.ReplaceSubstring(NS_LITERAL_STRING("\x0D\x0A").get(),
                         NS_LITERAL_STRING(", ").get());
	val.ReplaceChar( 13, ',');
	val.ReplaceChar( 10, ',');
}

void nsOEAddressIterator::SplitString( nsString& val1, nsString& val2)
{
	nsString	temp;

	// Find the last line if there is more than one!
	PRInt32 idx = val1.RFind( "\x0D\x0A");
	PRInt32	cnt = 2;
	if (idx == -1) {
		cnt = 1;
		idx = val1.RFindChar( 13);
	}
	if (idx == -1)
		idx= val1.RFindChar( 10);
	if (idx != -1) {
		val1.Right( val2, val1.Length() - idx - cnt);
		val1.Left( temp, idx);
		val1 = temp;
		SanitizeValue( val1);
	}
}

PRBool nsOEAddressIterator::BuildCard( const PRUnichar * pName, nsIMdbRow *newRow, LPMAILUSER pUser)
{
	
	nsString		lastName;
	nsString		firstName;
	nsString		eMail;
	nsString		nickName;
	nsString		middleName;

	LPSPropValue	pProp = m_pWab->GetUserProperty( pUser, PR_EMAIL_ADDRESS);
	if (pProp) {
		m_pWab->GetValueString( pProp, eMail);
		SanitizeValue( eMail);
		m_pWab->FreeProperty( pProp);
	}
	pProp = m_pWab->GetUserProperty( pUser, PR_GIVEN_NAME);
	if (pProp) {
		m_pWab->GetValueString( pProp, firstName);
		SanitizeValue( firstName);
		m_pWab->FreeProperty( pProp);
	}
	pProp = m_pWab->GetUserProperty( pUser, PR_SURNAME);
	if (pProp) {
		m_pWab->GetValueString( pProp, lastName);
		SanitizeValue( lastName);
		m_pWab->FreeProperty( pProp);
	}
	pProp = m_pWab->GetUserProperty( pUser, PR_MIDDLE_NAME);
	if (pProp) {
		m_pWab->GetValueString( pProp, middleName);
		SanitizeValue( middleName);
		m_pWab->FreeProperty( pProp);
	}
	pProp = m_pWab->GetUserProperty( pUser, PR_NICKNAME);
	if (pProp) {
		m_pWab->GetValueString( pProp, nickName);
		SanitizeValue( nickName);
		m_pWab->FreeProperty( pProp);
	}
	
	// The idea here is that firstName and lastName cannot both be empty!
	if (firstName.IsEmpty() && lastName.IsEmpty())
		firstName = pName;

	nsString	displayName;
	pProp = m_pWab->GetUserProperty( pUser, PR_DISPLAY_NAME);
	if (pProp) {
		m_pWab->GetValueString( pProp, displayName);
		SanitizeValue( displayName);
		m_pWab->FreeProperty( pProp);
	}
	if (displayName.IsEmpty()) {
		if (firstName.IsEmpty())
			displayName = pName;
		else {
			displayName = firstName;
			if (!middleName.IsEmpty()) {
				displayName.Append(PRUnichar(' '));
				displayName.Append( middleName);
			}
			if (!lastName.IsEmpty()) {
				displayName.Append(PRUnichar(' '));
				displayName.Append( lastName);
			}
		}
	}
	
	// We now have the required fields
	// write them out followed by any optional fields!
	if (!displayName.IsEmpty()) {
		m_database->AddDisplayName( newRow, NS_ConvertUCS2toUTF8(displayName).get());
	}
	if (!firstName.IsEmpty()) {
		m_database->AddFirstName( newRow, NS_ConvertUCS2toUTF8(firstName).get());
	}
	if (!lastName.IsEmpty()) {
		m_database->AddLastName( newRow, NS_ConvertUCS2toUTF8(lastName).get());
	}
	if (!nickName.IsEmpty()) {
		m_database->AddNickName( newRow, NS_ConvertUCS2toUTF8(nickName).get());
	}
	if (!eMail.IsEmpty()) {
		m_database->AddPrimaryEmail( newRow, NS_ConvertUCS2toUTF8(eMail).get());
	}

	// Do all of the extra fields!

	nsString	value;
	nsString	line2;
	nsresult	rv;
	// Create a field map

	nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
	if (NS_SUCCEEDED( rv)) {
		nsIImportFieldMap *		pFieldMap = nsnull;
		rv = impSvc->CreateNewFieldMap( &pFieldMap);
		if (NS_SUCCEEDED( rv) && pFieldMap) {
			int max = sizeof( gMapiFields) / sizeof( MAPIFields);
			for (int i = 0; i < max; i++) {
				pProp = m_pWab->GetUserProperty( pUser, gMapiFields[i].mapiTag);
				if (pProp) {
					m_pWab->GetValueString( pProp, value);
					m_pWab->FreeProperty( pProp);
					if (!value.IsEmpty()) {
						if (gMapiFields[i].multiLine == kNoMultiLine) {
							SanitizeValue( value);
							pFieldMap->SetFieldValue( m_database, newRow, gMapiFields[i].mozField, value.get());
						}
						else if (gMapiFields[i].multiLine == kIsMultiLine) {
							pFieldMap->SetFieldValue( m_database, newRow, gMapiFields[i].mozField, value.get());
						}
						else {
							line2.Truncate();
							SplitString( value, line2);
							if (!value.IsEmpty())
								pFieldMap->SetFieldValue( m_database, newRow, gMapiFields[i].mozField, value.get());
							if (!line2.IsEmpty())
								pFieldMap->SetFieldValue( m_database, newRow, gMapiFields[i].multiLine, line2.get());
						}
					}
				}
			}
			// call fieldMap SetFieldValue based on the table of fields

			NS_RELEASE( pFieldMap);
		}
	}


	return( PR_TRUE);
}
