package com.nodexy.im.openfire.plugin;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.dom4j.Element;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.openfire.IQHandlerInfo;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.handler.IQHandler;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;

/**
 * Location IQHandler
 * 
 * @author Chris, node@github
 * 
 */
public class LocationHandler extends IQHandler {

	private static final String NAME_SPACE = "com.nodexy.im.openfire.location";
	private static final String SQL_UPDATE_LOCATION = "INSERT INTO `ofLocation` (`username`, `updatetime`, `lon`, `lat`) VALUES (?,NOW(), ?,?);";
	private static final double LON_DELTA = 0.0009; // ~ 100m
	private static final double LAT_DELTA = 0.0009; // ~ 100m 
	private static final String SQL_USERS_NEARME = String.format("select * from ofLocation where (abs(lon-?)<= %f ) AND (abs(lat-? <= %f);" 
				,LON_DELTA,LAT_DELTA);

	private IQHandlerInfo info;
	private Connection openfireConn;

	public LocationHandler(String moduleName) {
		super(moduleName);
		try {
			this.openfireConn = DbConnectionManager.getConnection();
		} catch (SQLException e) {
			e.printStackTrace();
		};
		info = new IQHandlerInfo(moduleName, NAME_SPACE);
	}

	@Override
	public IQHandlerInfo getInfo() {
		return info;
	}

	@Override
	public IQ handleIQ(IQ packet) throws UnauthorizedException {
		
		System.out.println(">>>>>>>>>>>>> RECV IQ: " + packet.toXML()); // XXX
		
		// get users near me(from JID)
		if (IQ.Type.get.equals(packet.getType())) {
			return getUsersNearme(packet);
		// set from JID's location to ...
		} else if (IQ.Type.set.equals(packet.getType())) {
			JID to = packet.getTo();
			
			// send from JID's location to to JID
			if (to.getNode() != null && !to.getNode().equals("")){  
				XMPPServer.getInstance().getIQRouter().route(packet); // route to another user 
				
				return IQ.createResultIQ(packet);
			// send from JID's location to server , and update ofLocation  
			}else{ 
				return updateLocation(packet);
			}
		} else {
			IQ reply = IQ.createResultIQ(packet);
			reply.setType(IQ.Type.error);
			reply.setError(PacketError.Condition.bad_request);
			return reply;
		}
	}
	
	/**
	 * 
	 * 
	 * @param packet
	 * @return
	 */
	private IQ getUsersNearme(IQ packet) {

		IQ reply = IQ.createResultIQ(packet);
		
		JID from = packet.getFrom();
		
		Element iq = packet.getChildElement();
		Element item = iq.element("item");
		Double myLon = Double.parseDouble(item.attributeValue("lon"));
		Double myLat = Double.parseDouble(item.attributeValue("lat"));
		
		// XXX: update user location firstly 
		insertLocation(myLon,myLat,from.getNode());
		
		// find users near me 
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			pstmt = openfireConn.prepareStatement(SQL_USERS_NEARME);
			pstmt.setDouble(1, myLon);
			pstmt.setDouble(2, myLon);
			pstmt.setDouble(3, myLat);
			pstmt.setDouble(4, myLat);
			rs = pstmt.executeQuery();
			String username = null;
			double nearLon = 0;
			double nearLat = 0;
			while (rs.next()) {  
				username = rs.getString("username");
				nearLon = rs.getDouble("lon");
				nearLat = rs.getDouble("lat");
				Element e = iq.addElement("item");
				e.addAttribute("user", username);
				e.addAttribute("lon", Double.toString(nearLon));
				e.addAttribute("lat", Double.toString(nearLat));
			}
			reply.setChildElement(iq);
		} catch (SQLException e1) {
			reply.setType(IQ.Type.error);
			reply.setError(PacketError.Condition.internal_server_error);
			e1.printStackTrace();
		}
		return reply;
	}

	private IQ updateLocation(IQ packet) {
		IQ reply = IQ.createResultIQ(packet);

		Element iq = packet.getChildElement();
		JID from = packet.getFrom();
		String username = from.getNode();

		Element item = iq.element("item");
		Double myLon = Double.parseDouble(item.attributeValue("lon"));
		Double myLat = Double.parseDouble(item.attributeValue("lat"));

		boolean f = insertLocation(myLon,myLat,username);
		if (f){
			// reply.setChildElement(iq);
		}else{
			reply.setType(IQ.Type.error);
			reply.setError(PacketError.Condition.internal_server_error);
		}
		
		return reply;
	}
	
	private boolean insertLocation(Double myLon, double myLat, String username){
		boolean f = false;
		PreparedStatement pstmt = null;
		try {
			pstmt = openfireConn.prepareStatement(SQL_UPDATE_LOCATION);
			pstmt.setDouble(1, myLon);
			pstmt.setDouble(2, myLat);
			pstmt.setString(3, username);
			pstmt.executeUpdate();

			f = true;
		} catch (SQLException e1) {
			f = false;
			e1.printStackTrace();
		}
		return f;
	}

}