package jp.ac.naka.ec.entity;

import gov.nist.javax.sip.address.SipUri;
import gov.nist.javax.sip.parser.StringMsgParser;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

import javax.sdp.SessionDescription;
import javax.sip.address.SipURI;
import javax.sip.address.URI;

import jp.ac.naka.ec.Location;
import jp.ac.naka.ec.sensor.SensorData;
import jp.ac.naka.ec.sensor.SensorDataImpl;
import jp.ac.naka.ec.sensor.SensorDataParser;
import jp.ac.naka.ec.sensor.SensorDataParserImpl;
import jp.ac.naka.ec.sip.pidf.PIDFData;
import jp.ac.naka.ec.sip.pidf.Tuple;


public class EntityImpl implements Entity {

	
	private SipURI uri ;
	private String name, description;
	private ArrayList<String> keywords = new ArrayList<String>();
	private EntityType entityType = EntityType.SENSOR;
	private boolean local = true;
	private EntityContainer container;
	private Location location;
	private List<SensorData> sensors = new ArrayList<SensorData>();
	
	public EntityImpl(){}
	public EntityImpl (SipURI uri) {
		setURI(uri);
		setName(uri.getUser());
	}
	
	public EntityImpl (String uri) throws ParseException {
		this(createSipURI(uri));
	}
	
	public EntityImpl (String user, String host) throws ParseException {
		this(createSipURI(user, host));
	}
	
	public EntityImpl(EntityInformation info) throws ParseException {
		SipURI uri = createSipURI(info.getUri());
		setName(uri.getUser());
		setURI(uri);
		setDescrpition(info.getDescription());
		setEntityType(EntityType.valueOf(info.getServiceType()));
		SensorData[] data = info.getSensors();
		for (SensorData sensor : data) {
			addSensorData(sensor);
		}
		setLocal(false);
	}



	public EntityImpl(PIDFData data) {
		Tuple tuple = data.getTuples().get(0);
		setURI((SipURI)tuple.contact);
		setName((String)tuple.getExtension("name"));
		setEntityType(EntityType.valueOf((String)tuple.getExtension("type")));
		
		// TODO Locationほか
	}
	/**
	 * @return the descrpition
	 */
	public String getDescription() {
		return description;
	}



	/**
	 * @param descrpition the descrpition to set
	 */
	public void setDescrpition(String description) {
		this.description = description;
	}



	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}



	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}



	/**
	 * @return the uri
	 */
	public SipURI getURI() {
		return uri;
	}



	/**
	 * @param uri the uri to set
	 */
	public void setURI(SipURI uri) {
		this.uri = uri;
	}



	public EntityContainer getContainer() {
		return this.container;
	}

	

	public EntityType getEntityType() {
		return this.entityType;
	}

	

	public boolean sendBye(Entity target) {
		// TODO Auto-generated method stub
		return false;
	}

	public boolean sendMessage(String msg) {
		// TODO Auto-generated method stub
		return false;
	}


	public boolean sendMessage(String msg, Entity target) {
		// TODO Auto-generated method stub
		return false;
	}

	public boolean sendOffer(SessionDescription sdp, Entity target) {
		// TODO Auto-generated method stub
		return false;
	}

	public boolean sendOffer(Entity target) {
		// TODO Auto-generated method stub
		return false;
	}

	public void setEntityType(EntityType type) {
		this.entityType = type;
	}


	

	public boolean isLocal() {
		return this.local;
	}

	public void receiveAck(EntityEvent e) {
		// TODO Auto-generated method stub

	}

	public void receiveAnswer(EntityEvent e) {
		// TODO Auto-generated method stub

	}

	public void receiveBye(EntityEvent e) {
		// TODO Auto-generated method stub

	}

	public void receiveMessage(EntityEvent e) {
		// TODO Auto-generated method stub

	}

	public SessionDescription receiveOffer(EntityEvent e) {
		// TODO Auto-generated method stub
		return null;
	}

	public void register(EntityEvent e) {
		// TODO Auto-generated method stub

	}



	public void setLocal(boolean b) {
		this.local = b;
	}

	@Override
	public String toString() {
		return "uri:" + this.uri + ", type:" + this.entityType+", description:" + description;
	}
	
	/** create a sip uri.
	 *
	 *@param uri -- the uri to parse.
	 */
	public static javax.sip.address.SipURI createSipURI(String uri)
	//  throws java.net.URISyntaxException {
	throws ParseException {
		if (uri == null)
			throw new NullPointerException("null URI");
		try {
			StringMsgParser smp = new StringMsgParser();
			SipUri sipUri = smp.parseSIPUrl(uri);
			return (SipURI) sipUri;
		} catch (ParseException ex) {
			//  throw new java.net.URISyntaxException(uri, ex.getMessage());
			throw new ParseException(ex.getMessage(), 0);
		}

	}

	/** Create a SipURI
	 *
	 *@param user -- the user
	 *@param host -- the host.
	 */
	public static javax.sip.address.SipURI createSipURI(String user, String host)
		throws ParseException {
		if (host == null)
			throw new NullPointerException("null host");

		StringBuffer uriString = new StringBuffer("sip:");
		if (user != null) {
			uriString.append(user);
			uriString.append("@");
		}

		//if host is an IPv6 string we should enclose it in sq brackets
		if (host.indexOf(':') != host.lastIndexOf(':')
			&& host.trim().charAt(0) != '[')
			host = '[' + host + ']';

		uriString.append(host);

		StringMsgParser smp = new StringMsgParser();
		try {

			SipUri sipUri = smp.parseSIPUrl(uriString.toString());
			return sipUri;
		} catch (ParseException ex) {
			throw new ParseException(ex.getMessage(), 0);
		}
	}

	public void addKeyword(String str) {
		keywords.add(str);
	}
	
	public boolean removeKeyword(String msg) {
		return keywords.remove(msg);
	}
	
	public String[] getKeywords() {
		String[] keys = new String[keywords.size()];
		keywords.toArray(keys);
		return keys;
	}
	
	@Override
	public boolean equals (Object obj) {
		if (obj instanceof Entity) {
			Entity entity = (Entity)obj;
			if (this.getURI().toString().equals(entity.getURI().toString()))
					return true;
			else
					return false;
		} else 
		return false;
	}
	
	public void setEntityContainer(EntityContainer impl) {
		this.container = impl;
	}
	
	public boolean subscribe(Entity target) {
		// TODO Auto-generated method stub
		return false;
	}
	public Location getLocation() {
		return this.location;
	}
	public void setLocation(Location location) {
		this.location = location;
	}
	public void expired(EntityEvent e) {
		// TODO Auto-generated method stub
		
	}
	public void receiveErrorResponse(EntityEvent ee) {
		// TODO Auto-generated method stub
		
	}
	public boolean unsubscribe(Entity target) {
		// TODO Auto-generated method stub
		return false;
	}
	public void receiveNotify(EntityEvent e) {
		// TODO 自動生成されたメソッド・スタブ
		
	}
	public void receiveSubscribe(EntityEvent e) {
		// TODO 自動生成されたメソッド・スタブ
		
	}
	
	private static SimpleDateFormat format1;
	private static SimpleDateFormat format2;
	private static TimeZone timezone;
	private static Calendar cal;

	// RFC3339 Format UTC仕様
	private final static String pattern1 = "yyyy-MM-DD";
	private final static String pattern2 = "kk:mm:ss";
	private final static String zone = "Europe/London";

	static {
		format1 = new SimpleDateFormat(pattern1);
		format2 = new SimpleDateFormat(pattern2);
		timezone = TimeZone.getTimeZone(zone);
		cal = Calendar.getInstance(timezone);
	}
	
	public String createPIDF() {
		Entity source = this;
		URI uri = source.getURI();
		PIDFData data = new PIDFData(uri);

		if (source.getLocation() != null)
			data.setLocation(source.getLocation());
		
		Date date = cal.getTime();
		
		// Tuple == センサデータ
		Tuple tuple = new Tuple();
		tuple.contact = source.getURI();
		tuple.id = source.getName();

		// EntityTypeの設定
		EntityType ee = source.getEntityType();
		tuple.putExtension("type", ee.toString());
		tuple.putExtension("name", source.getName());
		StringBuilder builder = new StringBuilder();
		String[] keywords = source.getKeywords();
		for (int i = 0; i < keywords.length;) {
			String keyword = keywords[i];
			builder.append(keyword);
			if (++i < keywords.length)
				builder.append(",");
		}

		tuple.putExtension("keyword", builder.toString());
		tuple.addNote(source.getDescription());
		tuple.timestamp = format1.format(date) + "T" + format2.format(date)
				+ "Z";

		data.addTuple(tuple);

		return data.toString();
	}

	public boolean removeSensorData(SensorData sensor) {
		return sensors.remove(sensor);
	}
	public void addSensorData(SensorData data) {
		sensors.add(data);
	}
	

	public SensorData[] getSensors() {
		SensorData[] ret = new SensorData[sensors.size()];
		sensors.toArray(ret);
		return ret;
	}
	
	private SipURI contact;
	
	public void setContactURI(SipURI uri) {
		this.contact = uri;
	}
	
	public SipURI getContactURI() {
		return contact;
	}
	
	public static void main(String[] args) throws ParseException {
		Entity e = new EntityImpl("sip:1kasuya@localhost:5060");
		SensorData s = new SensorDataImpl();
		e.addSensorData(s);
		System.out.println(e.getSensors().length);
		e.removeSensorData(s);
		System.out.println(e.getSensors().length);
	}
 }