package sfs3.extensions.examples.spacewar2;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.io.FileUtils;

import com.smartfoxserver.bitswarm.data.TransportType;
import com.smartfoxserver.entities.SFSRoomRemoveMode;
import com.smartfoxserver.entities.User;
import com.smartfoxserver.entities.data.ISFSArray;
import com.smartfoxserver.entities.data.ISFSObject;
import com.smartfoxserver.entities.data.SFSObject;
import com.smartfoxserver.entities.match.MatchExpression;
import com.smartfoxserver.entities.match.NumberMatch;
import com.smartfoxserver.entities.match.StringMatch;
import com.smartfoxserver.entities.variables.RoomVariable;
import com.smartfoxserver.entities.variables.SFSRoomVariable;
import com.smartfoxserver.exceptions.SFSCreateRoomException;
import com.smartfoxserver.exceptions.SFSJoinRoomException;
import com.smartfoxserver.extensions.BaseClientRequestHandler;
import com.smartfoxserver.extensions.ExtensionLogLevel;
import com.smartfoxserver.mmo.CreateMMORoomSettings;
import com.smartfoxserver.mmo.Vec3D;
import com.smartfoxserver.util.JSONUtil;

import sfs3.extensions.examples.spacewar2.util.Constants;

public class SW2ZoneExtension extends com.smartfoxserver.extensions.SFSExtension
{
	private List<String> levels;
	private ISFSObject settings;
	
	@Override
	public void init()
	{
		try
		{
			// Load configuration file and extract levels configuration
			loadLevelsConfig();
			
			// Add client Request Handler
	        addRequestHandler(Constants.REQ_INIT, new StartGameReqHandler());
		}
		catch (IOException e)
		{
			trace(ExtensionLogLevel.ERROR, "SpaceWar 2 configuration could not be loaded");
		}
		
		trace(ExtensionLogLevel.INFO, "SpaceWar 2 Zone Extension initialized");
	}
	
	/**
	 * Load the game external configuration file and parse it to extract the list of available levels.
	 * This is used to validate the request to start a new game coming from the client.
	 */
	private void loadLevelsConfig() throws IOException
	{
		// Load configuration file
		String cfgData = JSONUtil.stripComments(FileUtils.readFileToString(new File(this.getCurrentFolder() + "SpaceWar2.config")));
		
		// Convert to temporary SFSObject
		ISFSObject tempCfg = SFSObject.newFromJsonData(cfgData);
		
		// Game settings
		settings = tempCfg.getSFSObject(Constants.CONFIG_SETTINGS);
		
		// Levels
		ISFSArray levelsCfg = tempCfg.getSFSArray(Constants.CONFIG_LEVELS);
		levels = new ArrayList<String>();
		
		for (int i = 0; i < levelsCfg.size(); i++)
		{
			ISFSObject level = levelsCfg.getSFSObject(i);
			levels.add(level.getString("name"));
		}
	}
	
	//----------------------------------------------------------------------------------------
	
	public class StartGameReqHandler extends BaseClientRequestHandler
    {
        @Override
        public void handleClientRequest(User sender, ISFSObject params, TransportType transportType)
        {
        	// Extract level name and validate it
        	String levelName = params.getString("name");
        	
        	// Validate level ID
        	if (levels.contains(levelName))
        	{
        		try
        		{
					createOrJoinRoom(sender, levelName);
				}
        		catch (SFSJoinRoomException e)
        		{
					// Nothing to do: a ROOM JOIN ERROR event is dispatched to the client automatically
        			trace(ExtensionLogLevel.ERROR, e);
				}
        		catch (SFSCreateRoomException e)
        		{
        			// Nothing to do: a ROOM CREATION ERROR event is dispatched to the client automatically
        			trace(ExtensionLogLevel.ERROR, e);
				}
        	}
        	else
        	{
        		// Send error notification back to client
        		send(Constants.REQ_INIT_ERROR, null, sender);
        	}
        }
        
        private void createOrJoinRoom(User player, String levelName) throws SFSJoinRoomException, SFSCreateRoomException
        {
        	// Set the MMORoom settings to create a new Room if needed
        	CreateMMORoomSettings mmors = new CreateMMORoomSettings();
        	mmors.setGroupId(Constants.ROOMS_GROUP_NAME);
			mmors.setName("SW2_" + System.currentTimeMillis());
			mmors.setMaxUsers(50);
			mmors.setDynamic(true);
			mmors.setAutoRemoveMode(SFSRoomRemoveMode.WHEN_EMPTY);
			mmors.setExtension(new CreateMMORoomSettings.RoomExtensionSettings("SpaceWar2", "sfs3.extensions.examples.spacewar2.SW2RoomExtension"));
			
			mmors.setDefaultAOI(new Vec3D(1300, 750, 0));
			mmors.setUserMaxLimboSeconds(settings.getInt(Constants.SETTING_GAME_ABORT_SECS) + settings.getInt(Constants.SETTING_GAME_START_SECS) + 20);
			mmors.setProximityListUpdateMillis(20); // We need this to be almost realtime to make the client and server simulation almost identical
			mmors.setSendAOIEntryPoint(false);
			
			// Set requirements to allow users find the Room (see match expression below) and other settings in Room Variables
			List<RoomVariable> roomVars = new ArrayList<RoomVariable>();
			roomVars.add(new SFSRoomVariable(Constants.ROOMVAR_GAME_LEVEL, levelName));
			roomVars.add(new SFSRoomVariable(Constants.ROOMVAR_USER_MIN_XP, player.getVariable(Constants.USERVAR_EXPERIENCE).getIntValue()));
			roomVars.add(new SFSRoomVariable(Constants.ROOMVAR_GAME_STATE, Constants.GAMESTATE_WAITING));
			mmors.setRoomVariables(roomVars);
			
			// Set the match expression to search for an existing Room (see Room Variables set above):
			// - it must match the game level selected by the user
			// - it must have a minimum required experience equal to or lower than the experience of the user searching the Room to join, as set when the Room was created
			// - the game must not be running yet, unless mid game join is allowed
			MatchExpression exp = new MatchExpression(Constants.ROOMVAR_GAME_LEVEL, StringMatch.EQUALS, levelName)
					 							 .and(Constants.ROOMVAR_USER_MIN_XP, NumberMatch.LESS_THAN_OR_EQUAL_TO, player.getVariable(Constants.USERVAR_EXPERIENCE).getIntValue())
												 .and(Constants.ROOMVAR_GAME_STATE, NumberMatch.LESS_THAN_OR_EQUAL_TO, (settings.getBool(Constants.SETTING_ALLOW_MIDGAME_JOIN) ? Constants.GAMESTATE_RUNNING : Constants.GAMESTATE_WAITING));
			
			// Execute
			sfsApi.quickJoinOrCreateRoom(player, exp, Arrays.asList(Constants.ROOMS_GROUP_NAME), mmors);
        }
    }
}
