/*
    BEEM is a videoconference application on the Android Platform.

    Copyright (C) 2009 by Frederic-Charles Barthelery,
                          Jean-Manuel Da Silva,
                          Nikita Kozlov,
                          Philippe Lago,
                          Jean Baptiste Vergely,
                          Vincent Veronis.

    This file is part of BEEM.

    BEEM 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 3 of the License, or
    (at your option) any later version.

    BEEM 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 BEEM.  If not, see <http://www.gnu.org/licenses/>.

    Please send bug reports with examples or suggestions to
    contact@beem-project.com or http://dev.beem-project.com/

    Epitech, hereby disclaims all copyright interest in the program "Beem"
    written by Frederic-Charles Barthelery,
               Jean-Manuel Da Silva,
               Nikita Kozlov,
               Philippe Lago,
               Jean Baptiste Vergely,
               Vincent Veronis.

    Nicolas Sadirac, November 26, 2009
    President of Epitech.

    Flavien Astraud, November 26, 2009
    Head of the EIP Laboratory.

 */

package com.beem.project.beem.account;

import java.util.ArrayList;

import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.RosterGroup;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Presence;

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.BaseColumns;
import android.provider.ContactsContract;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.RawContacts.Entity;
import android.util.Log;
import android.widget.ImageButton;
import android.widget.ImageView;

import com.beem.project.beem.BeemConnection;
import com.beem.project.beem.R;
import com.beem.project.beem.providers.AvatarProvider;
import com.beem.project.beem.utils.Status;

/**
 * Class to integrate beem in android's account
 * @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;

    /**
     * Constructor.
     */
    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;
    }

    /**
     * Method to sync Beem roster with Android
     * @param context context
     * @param account account
     * @param extras extras
     * @param authority authority
     * @param provider provider
     * @param syncResult syncResult
     * @throws OperationCanceledException OperationCanceledException
     */
    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) {
		long rawContactID = manageEntry(ops, a, entry);
		if (rawContactID != -1)
		    updateContactStatus(ops, entry, rawContactID, r.getPresence(entry.getUser()));
	    }
	    if (ops.size() > 100)
		executeOperation(ops);
	}
	// TODO: dont know how to ttest
	//	for (RosterGroup group : r.getGroups()) {
	//	    if (group != null)
	//		manageGroup(ops, a, group);
	//	    if (ops.size() > 0)
	//		    executeOperation(ops);
	//	}
	if (ops.size() > 0)
	    executeOperation(ops);
    }

    private static long manageEntry(ArrayList<ContentProviderOperation> ops, Account account, RosterEntry entry) {
	long rawContactID = getRawContactID(account.name, entry.getUser());
	Log.i(TAG, "Sync Contact : " + entry.getUser() + " RawContactID : " + rawContactID);
	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 = BuildProfile(entry, rawContactID);
	    ops.add(builder.build());	    
	    builder = buildStructuredName(entry, rawContactID);
	    ops.add(builder.build());
	} else { // Found, update
	    ContentProviderOperation.Builder builder = buildStructuredName(entry, rawContactID);
	    ops.add(builder.build());
	}
	return rawContactID;
    }

    private static void manageGroup(ArrayList<ContentProviderOperation> ops, Account account, RosterGroup group) {
	Log.i(TAG, "Sync Group : " + group.getName());
	ContentProviderOperation.Builder builder = ContentProviderOperation
	    .newInsert(ContactsContract.Groups.CONTENT_URI);
	builder.withValue(ContactsContract.Groups.TITLE, group.getName());
	builder.withValue(ContactsContract.Settings.UNGROUPED_VISIBLE, true);
    }

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

    private static ContentProviderOperation.Builder BuildProfile(RosterEntry entry, long rawContactID) {
	ContentProviderOperation.Builder builder = ContentProviderOperation
	    .newInsert(ContactsContract.Data.CONTENT_URI);
	String displayName = entry.getName() != null ? entry.getName() : entry.getUser();
	builder.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactID);
	builder.withValue(ContactsContract.Data.MIMETYPE,
	    "vnd.android.cursor.item/vnd.com.beem.project.beem.android.profile");
	builder.withValue(ContactsContract.Data.DATA2, displayName);
	//builder.withValue(ContactsContract.Data.DATA2, R.string.chat_name);
	return builder;
    }

    private static void updateContactStatus(ArrayList<ContentProviderOperation> operationList, RosterEntry entry,
	long rawContactId, Presence p) {
	Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
	Uri entityUri = Uri.withAppendedPath(rawContactUri, Entity.CONTENT_DIRECTORY);
	Cursor c = mContentResolver.query(entityUri, new String[] { Entity.DATA_ID }, Entity.MIMETYPE
	    + " = 'vnd.android.cursor.item/vnd.com.beem.project.beem.android.profile'", null, null);
	try {
	    if (c.moveToNext()) {
		if (!c.isNull(0)) {
		    Log.e(TAG + "UPDATECONTACTSTATUS", "Entity.DATA_ID : " + c.getLong(0));
		    // Build raw Contact after click on it
		    ContentProviderOperation.Builder builder = ContentProviderOperation
			.newInsert(ContactsContract.StatusUpdates.CONTENT_URI);
		    builder.withValue(ContactsContract.StatusUpdates.DATA_ID, c.getLong(0));
		    //TODO: Get status message
		    builder.withValue(ContactsContract.StatusUpdates.STATUS, "BEEM STATUS");
		    builder.withValue(ContactsContract.StatusUpdates.STATUS_RES_PACKAGE, "com.beem.project.beem");
		    builder.withValue(ContactsContract.StatusUpdates.STATUS_LABEL, R.string.app_name);
		    //TODO: GET Status Logo
		    // Actually getting default logo in the xml
//		    builder.withValue(ContactsContract.StatusUpdates.STATUS_ICON, img);
		    operationList.add(builder.build());
		}
	    }
	} finally {
	    c.close();
	}
    }

    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;
    }

}
