package sfs3.extensions.examples.shooter;

import com.smartfoxserver.core.SFSEventType;
import com.smartfoxserver.entities.Room;
import com.smartfoxserver.entities.User;
import com.smartfoxserver.entities.data.ISFSObject;
import com.smartfoxserver.entities.data.SFSObject;
import com.smartfoxserver.extensions.SFSExtension;

import sfs3.extensions.examples.shooter.handlers.*;
import sfs3.extensions.examples.shooter.simulation.CombatPlayer;
import sfs3.extensions.examples.shooter.simulation.Item;
import sfs3.extensions.examples.shooter.simulation.World;
import sfs3.extensions.examples.shooter.utils.RoomHelper;
import sfs3.extensions.examples.shooter.utils.UserHelper;

import java.util.List;

// The extension main class. Used to handle requests from clients and send messages back to them
//
// Requests that can be send from clients:
// - sendTransform
// - sendAnim
// - spawnMe
// - getTime
// - shot
// - reload
//
// Responses send from the extension to clients:
// - time
// - anim
// - spawnPlayer
// - transform
// - notransform
// - killed
// - health
// - score
// - ammo
// - spawnItem
// - removeItem
// - enemyShotFired
// - reloaded
//

public class ShooterExtension extends SFSExtension
{

	private World world; // Reference to World simulation model

	public World getWorld()
	{
		return world;
	}

	@Override
	public void init()
	{
		this.world = new World(this); // Creating the world model

		// Subscribing the request handlers
		addRequestHandler("sendTransform", SendTransformHandler.class);
		addRequestHandler("sendAnim", SendAnimHandler.class);
		addRequestHandler("spawnMe", SpawnMeHandler.class);
		addRequestHandler("getTime", GetTimeHandler.class);
		addRequestHandler("shot", ShotHandler.class);
		addRequestHandler("reload", ReloadHandler.class);
		addEventHandler(SFSEventType.USER_DISCONNECT, OnUserGoneHandler.class);
		addEventHandler(SFSEventType.USER_LEAVE_ROOM, OnUserGoneHandler.class);
		addEventHandler(SFSEventType.USER_LOGOUT, OnUserGoneHandler.class);
		
		trace("Shooter extension initialized");
	}

	public void destroy()
	{
		this.world = null;
		super.destroy();
	}

	// Send message to client when a player is killed
	public void clientKillPlayer(CombatPlayer pl, CombatPlayer killerPl)
	{
		SFSObject sfsObj = new SFSObject();
		sfsObj.putInt("id", pl.getSfsUser().getId());
		sfsObj.putInt("killerId", killerPl.getSfsUser().getId());
		
		Room currentRoom = RoomHelper.getCurrentRoom(this);
		List<User> userList = UserHelper.getRecipientsList(currentRoom);
		
		send("killed", (ISFSObject) sfsObj, userList);
	}

	// Send message to clients when the health value of a player is updated
	public void clientUpdateHealth(CombatPlayer pl)
	{
		SFSObject sfsObj = new SFSObject();
		sfsObj.putInt("id", pl.getSfsUser().getId());
		sfsObj.putInt("health", pl.getHealth());
		
		Room currentRoom = RoomHelper.getCurrentRoom(this);
		List<User> userList = UserHelper.getRecipientsList(currentRoom);
		
		send("health", (ISFSObject) sfsObj, userList);
	}

	// Send message to clients when the score value of a player is updated
	public void updatePlayerScore(CombatPlayer pl)
	{
		SFSObject sfsObj = new SFSObject();
		sfsObj.putInt("id", pl.getSfsUser().getId());
		sfsObj.putInt("score", pl.getScore());
		
		Room currentRoom = RoomHelper.getCurrentRoom(this);
		List<User> userList = UserHelper.getRecipientsList(currentRoom);
		
		send("score", (ISFSObject) sfsObj, userList);
	}

	// When new item is spawned - send message to all the clients
	public void clientInstantiateItem(Item item)
	{
		SFSObject sfsObj = new SFSObject();
		item.toSFSObject((ISFSObject) sfsObj);
		
		Room currentRoom = RoomHelper.getCurrentRoom(this);
		List<User> userList = UserHelper.getRecipientsList(currentRoom);
		
		send("spawnItem", (ISFSObject) sfsObj, userList);
	}

	// When someone picked an item up, send message to all the clients, so they will
	// remove the item from scene
	public void clientRemoveItem(Item item, CombatPlayer player)
	{
		SFSObject sfsObj = new SFSObject();
		item.toSFSObject((ISFSObject) sfsObj);
		sfsObj.putInt("playerId", player.getSfsUser().getId());
		
		Room currentRoom = RoomHelper.getCurrentRoom(this);
		List<User> userList = UserHelper.getRecipientsList(currentRoom);
		
		send("removeItem", (ISFSObject) sfsObj, userList);
	}

	// When someone has made a shot, send message to all the clients to inform about
	// it
	public void clientEnemyShotFired(CombatPlayer pl)
	{
		SFSObject sfsObj = new SFSObject();
		sfsObj.putInt("id", pl.getSfsUser().getId());
		
		Room currentRoom = RoomHelper.getCurrentRoom(this);
		List<User> userList = UserHelper.getRecipientsList(currentRoom);
		
		send("enemyShotFired", (ISFSObject) sfsObj, userList);
	}

	// When someone has reloaded the weapon, send message to all the clients to
	// inform about it.
	public void clientReloaded(CombatPlayer player)
	{
		SFSObject sfsObj = new SFSObject();
		sfsObj.putInt("id", player.getSfsUser().getId());
		
		Room currentRoom = RoomHelper.getCurrentRoom(this);
		List<User> userList = UserHelper.getRecipientsList(currentRoom);
		
		send("reloaded", (ISFSObject) sfsObj, userList);
	}

	// Send instantiate new player message to all the clients
	public void clientInstantiatePlayer(CombatPlayer player)
	{
		clientInstantiatePlayer(player, (User) null);
		clientUpdateAmmo(player);
	}

	// Send the player instantiation message to all the clients or to a specified
	// user only
	public void clientInstantiatePlayer(CombatPlayer player, User targetUser)
	{
		SFSObject sfsObj = new SFSObject();
		player.toSFSObject((ISFSObject) sfsObj);
		
		Room currentRoom = RoomHelper.getCurrentRoom(this);
		
		if (targetUser == null)
		{
			List<User> userList = UserHelper.getRecipientsList(currentRoom);
			// Sending to all the users
			send("spawnPlayer", (ISFSObject) sfsObj, userList);
		}
		else
		{
			// Sending to the specified user
			send("spawnPlayer", (ISFSObject) sfsObj, targetUser);
		}
	}

	// Send message to clients when the ammo value of a player is updated
	public void clientUpdateAmmo(CombatPlayer player)
	{
		SFSObject sfsObj = new SFSObject();
		sfsObj.putInt("id", player.getSfsUser().getId());
		sfsObj.putInt("ammo", player.getWeapon().getAmmoCount());
		sfsObj.putInt("maxAmmo", 6);
		sfsObj.putInt("unloadedAmmo", player.getAmmoReserve());
		
		send("ammo", (ISFSObject) sfsObj, player.getSfsUser());
	}
}
