/*
 * Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
 *
 * This file is part of PeerfactSim.KOM.
 * 
 * PeerfactSim.KOM is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 * 
 * PeerfactSim.KOM is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with PeerfactSim.KOM.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups;

import java.util.LinkedHashSet;
import java.util.Random;
import java.util.Set;
import java.util.UUID;

import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tudarmstadt.maki.simonstrator.api.Randoms;

/**
 * This class handles the instance of a group. Each group instance has a leader which sets the behavior. All other
 * participants follow the behavior of the leader in terms of speed, velocity, heading etc.
 * 
 * @author Julian Zobel
 */
public class SocialMovementGroup {
	
	public final UUID groupID;
	
	private SimLocationActuator leader;
	private LinkedHashSet<SimLocationActuator> members;

	private PositionVector destination;
	private PositionVector meetingPoint;
	

	/**
	 * Initializes the group with a given leader.
	 * 
	 * @param SimLocationActuator New leader of the group
	 * @param int The ID of the group.
	 */
	public SocialMovementGroup(SimLocationActuator leader) {
		
		this.groupID = UUID.randomUUID();
		
		this.leader = leader;
		this.members = new LinkedHashSet<SimLocationActuator>();
		this.members.add(leader);
	}
	
	/**
	 * Initializes the group, sets the participants and groupId, finally chooses a random leader.
	 * 
	 * @param Set<SimLocationActuator> All participants of the group.
	 * @param int The ID of the group.
	 */
	public SocialMovementGroup(Set<SimLocationActuator> participants) {
		
		this.groupID = UUID.randomUUID();
		
		setMembers(participants);
		setRandomLeader();
	}

	/**
	 * Adds a host to the group. Manually check in {@link ModularMovementModel} class, if group size is exceeded by adding another host.
	 * 
	 * @param SimLocationActuator The host to be added.
	 */
	public void addHostToGroup(SimLocationActuator host) {
		members.add(host);
	}

	/**
	 * Removes a host from the group. If the host was the current leader, a random new leader is chosen from the remaining members.
	 * 
	 * @param SimLocationActuator The host to be removed.
	 */
	public void removeHostFromGroup(SimLocationActuator host) {
		if(members.contains(host)) {
			if(host == leader) {
				members.remove(host);
				setRandomLeader();
			}
			else {
				members.remove(host);
			}
		}
	}

	/**
	 * Sets a randomly chosen participant of the group as leader.
	 */
	private void setRandomLeader() {		
		this.leader = getRandomMember();
	}

	/**
	 * Returns a random participant of all group members. 
	 * 
	 * @return SimLocationActuator
	 */
	public SimLocationActuator getRandomMember() {
		
		if(members.size() > 1) {
			Random rand = Randoms.getRandom(SocialMovementGroup.class);
			int item = rand.nextInt(members.size() - 1);
			int i = 0;
			
			for(SimLocationActuator host : members) {
				if(i == item) {
					return host;
				}
				i++;
			}
		}
		else if(members.size() == 1) {
			return members.iterator().next();
		}
				
		return null;		
	}

	/**
	 * Returns true if this group has a specific host as member, false otherwise
	 * 
	 * @param SimLocationActuator The host to be checked.
	 * @return boolean
	 */
	public boolean hasMember(SimLocationActuator host) {
		if(members.contains(host)) {
			return true;
		}
		return false;
	}

	/**
	 * Returns the position of the group leader.
	 * 
	 * @return PositionVector The position of the group leader.
	 */
	public PositionVector getLeaderPosition() {
		return leader.getRealPosition();
	}
	
	@Override
	public String toString() {
		return "Social Group of #" + leader.getHost().getId() + " ("+ members.size() +")";
		
	}
	
	// ===================
	// Getters and Setters
	// ===================

	public int getGroupSize() {
		return members.size();
	}
	
	public Set<SimLocationActuator> getMembers() {
		return members;
	}
	
	public void setMembers(Set<SimLocationActuator> participants) {
		this.members = new LinkedHashSet<>(participants);
	}
	
	public SimLocationActuator getLeader() {
		return leader;
	}
	
	public PositionVector getDestination() {
		return destination;
	}
	
	public void setDestination(PositionVector destination) {
		this.destination = destination;
	}
	
	public PositionVector getMeetingPoint() {
		return meetingPoint;
	}
	
	public void setMeetingPoint(PositionVector meetingPoint) {
		this.meetingPoint = meetingPoint;
	}
	
	@Override
	public boolean equals(Object obj) {
		if(!(obj instanceof SocialMovementGroup)) {
			return false;
		}
		
		SocialMovementGroup toCompare = (SocialMovementGroup) obj;
		
		if(this.groupID == toCompare.groupID) {
			if(this.getLeader() == toCompare.getLeader()) {
				return true;
			}
			else {
				System.out.println("Leader seems to be different, but still the same UUID");
				return false;
			}
		}
		else return false;		
	}
	
	@Override
	public int hashCode() {
		//FIXME hope this is correct -- should we include the leader and/or other fields as well?		
		return groupID.hashCode();
	}
}
