/**
 * 
 */
package com.beem.project.beem.account;

import java.util.ArrayList;

import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;

import android.accounts.Account;
import android.accounts.OperationCanceledException;
import android.app.Service;
import android.content.ContentProviderClient;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.OperationApplicationException;
import android.content.SyncResult;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.util.Log;

import com.beem.project.beem.BeemConnection;

/**
 * @author marseille
 */
public class SyncAdapterService extends Service {

    private static final String TAG = "SynAcapterService";
    private static SyncAdapter mSyncAdapter = null;
    private static ContentResolver mContentResolver = null;
    private static Context mContext;

    public SyncAdapterService() {
	super();
    }

    @Override
    public void onCreate() {
	mContext = SyncAdapterService.this;
    }

    @Override
    public IBinder onBind(Intent intent) {
	IBinder ret = null;
	ret = getSyncAdapter().getSyncAdapterBinder();
	return ret;

    }

    private SyncAdapter getSyncAdapter() {
	if (mSyncAdapter == null)
	    mSyncAdapter = new SyncAdapter(this, true);
	return mSyncAdapter;
    }

    public static void performSync(Context context, final Account account, Bundle extras, String authority,
	ContentProviderClient provider, SyncResult syncResult) throws OperationCanceledException {
	mContentResolver = context.getContentResolver();
	Log.i(TAG, "performSync: " + account.toString());

	BeemConnection beemco = new BeemConnection(mContext.getSharedPreferences(account.name, MODE_PRIVATE), null);
	beemco.setNoPresence();
	XMPPConnection con = new XMPPConnection(beemco.getConnectionConfiguration());
	Roster roster = null;
	try {
	    con.connect();
	    con.login(beemco.getLogin(), beemco.getPassword(), "BEEM_SYNC_ADAPTER");
	    roster = con.getRoster();

	} catch (XMPPException e) {
	    Log.e(TAG, "Error while connecting with syncAdapter", e);
	} catch (IllegalStateException e) {
	    Log.e(TAG, "Not connected to server", e);
	}
	if (roster != null) {
	    manageRoster(roster, account);
	}

	con.disconnect();

    }

    private static void executeOperation(ArrayList<ContentProviderOperation> ops) {
	try {
	    mContentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
	} catch (RemoteException e) {
	    Log.d(TAG, "Error during sync of contact", e);
	} catch (OperationApplicationException e) {
	    Log.d(TAG, "Error during sync of contact", e);
	}
    }
    
    private static void manageRoster(final Roster r, final Account a) {
	ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();

	for (RosterEntry entry : r.getEntries()) {
	    if (entry != null) {
		manageEntry(ops, a, entry);
	    }
	    if (ops.size() > 100)
		executeOperation(ops);
	}
	if (ops.size() > 0)
	    executeOperation(ops);
    }

    private static void manageEntry(ArrayList<ContentProviderOperation> ops, Account account, RosterEntry entry) {

	long rawContactID = getRawContactID(account.name, entry.getUser());
	if (rawContactID == -1) { // Not found in database, add new 	    
	    ContentValues values = new ContentValues();
	    values.put(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type);
	    values.put(ContactsContract.RawContacts.ACCOUNT_NAME, account.name);
	    values.put(ContactsContract.RawContacts.SOURCE_ID, entry.getUser());
	    Uri rawContactUri = mContentResolver.insert(ContactsContract.RawContacts.CONTENT_URI, values);
	    rawContactID = ContentUris.parseId(rawContactUri);
	    values.clear();
	    ContentProviderOperation.Builder builder = buildStructuredName(ContentProviderOperation
		.newInsert(ContactsContract.Data.CONTENT_URI), entry, rawContactID);
	    ops.add(builder.build());
	} else { // Found, update
	    // if newUpdate instead of newInster ... fail update : all rows = 1 row
	    ContentProviderOperation.Builder builder = buildStructuredName(ContentProviderOperation
		.newInsert(ContactsContract.Data.CONTENT_URI), entry, rawContactID);
	    ops.add(builder.build());
	}
    }

    private static ContentProviderOperation.Builder buildStructuredName(ContentProviderOperation.Builder builder,
	RosterEntry entry, long rawContactID) {
	String displayName = entry.getName() != null ? entry.getName() : entry.getUser();
	builder.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactID);
	builder.withValue(ContactsContract.Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
	builder.withValue(StructuredName.DISPLAY_NAME, displayName);
	return builder;
    }

    private static long getRawContactID(String account, String jid) {
	long authorId = -1;
	final Cursor c = mContentResolver.query(ContactsContract.RawContacts.CONTENT_URI, new String[] {
	    ContactsContract.RawContacts._ID, ContactsContract.RawContacts.SOURCE_ID },
	    ContactsContract.RawContacts.ACCOUNT_NAME + "=? AND " + ContactsContract.RawContacts.SOURCE_ID + "=?",
	    new String[] { account, jid }, null);
	try {
	    if (c.moveToFirst())
		authorId = c.getInt(c.getColumnIndex(ContactsContract.RawContacts._ID));
	} finally {
	    if (c != null)
		c.close();
	}
	return authorId;
    }

}
