package be.ac.ulb.code.wit.resources.serializers.json;

import java.io.IOException;
import java.io.Reader;

import org.apache.abdera.parser.ParseException;
import org.apache.commons.lang3.StringUtils;

import be.ac.ulb.code.wit.contacts.Contacts;
import be.ac.ulb.code.wit.contacts.Contacts.IdentifiedContact;
import be.ac.ulb.code.wit.resources.serializers.helpers.ContactDeserializer;
import be.ac.ulb.code.wit.resources.serializers.helpers.MalformedRequestException;
import be.ac.ulb.code.wit.resources.serializers.helpers.NotFoundException;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.NullNode;

/**
 * An atom deserializer that create, update or delete contacts, using data from
 * atom entries.
 * 
 * @author fpicalausa
 * 
 */
public class JsonContactDeserializer extends ContactDeserializer {
	public JsonContactDeserializer(Contacts contacts, String group)
			throws NotFoundException {
		super(contacts, group);
	}

	public JsonContactDeserializer(Contacts contacts,
			IdentifiedContact existingContact, String group)
			throws NotFoundException {
		super(contacts, existingContact, group);
	}

	private String asText(JsonNode node) {
		if (node == null || node instanceof NullNode) {
			return null;
		}
		return node.asText();
	}

	/**
	 * Update the current contact with data from the given atom entry. If
	 * checkid is <tt>true</tt> the atom:id field of the entry if checked for
	 * correspondence with an existing contact. If no contact had been created
	 * and the atom:id corresponds to a contact, that contact is update instead
	 * of creating a new one.
	 * 
	 * @param entry
	 *            The atom entry to be parsed
	 * @param checkid
	 *            Whether the atom:id field should be parsed or not.
	 */
	public void updateContact(JsonNode node, boolean checkid) {
		String firstName, lastName, email, displayName;
		boolean hasDisplayName;

		// Fetch the data for building a new contact
		firstName = asText(node.get("firstname"));
		lastName = asText(node.get("lastname"));
		email = asText(node.get("email"));
		displayName = asText(node.get("displayname"));
		hasDisplayName = !StringUtils.isBlank(displayName);

		if (checkid && node.get("id") != null) {
			updateContactFromId(asText(node.get("id")));
		}

		// Create/Update the contact from the parsed information
		updateContact(firstName, lastName, email, displayName, hasDisplayName);
	}

	/**
	 * Update the current contact with data from the given atom entry. If
	 * checkid is <tt>true</tt> the atom:id field of the entry if checked for
	 * correspondence with an existing contact. If no contact had been created
	 * and the atom:id corresponds to a contact, that contact is update instead
	 * of creating a new one.
	 * 
	 * @param entry
	 *            The atom entry to be parsed
	 * @param checkid
	 *            Whether the atom:id field should be parsed or not.
	 */
	@Override
	public void updateContact(Reader data, boolean checkid) {
		try {
			ObjectMapper mapper = new ObjectMapper();
			JsonNode tree = mapper.readValue(data, JsonNode.class);
			updateContact(tree, checkid);
		} catch (ParseException e) {
			throw new MalformedRequestException(e);
		} catch (JsonParseException e) {
			throw new MalformedRequestException(e);
		} catch (JsonMappingException e) {
			throw new MalformedRequestException(e);
		} catch (IOException e) {
			throw new MalformedRequestException(e);
		}
	}
}
