public class BukkitBungeeAdapter {
	private static BukkitBungeeAdapter instance;
	private final Plugin PAF_EXTENSION;
	private boolean forceUuidSupport = false;

	public BukkitBungeeAdapter(PAFExtension pPlugin) {
		this((PAFPluginBase) pPlugin);
	}

	public BukkitBungeeAdapter(PAFPluginBase pPlugin) {
		PAF_EXTENSION = pPlugin;
		if (instance == null)
			instance = this;
	}

	public static BukkitBungeeAdapter getInstance() {
		return instance;
	}

	public void registerListener(Object pListener, PAFPluginBase pPlugin) {
		ProxyServer.getInstance().getPluginManager().registerListener(pPlugin, (Listener) pListener);
	}

	public void callEvent(Object pEvent) {
		ProxyServer.getInstance().getPluginManager().callEvent((Event) pEvent);
	}

	public void registerCommand(Object pCommand, PAFPluginBase pPlugin) {
		ProxyServer.getInstance().getPluginManager().registerCommand(pPlugin, (Command) pCommand);
	}

	public void registerTopCommand(TopCommand<?> pCommand, PAFPluginBase pPlugin) {
		ProxyServer.getInstance().getPluginManager().registerCommand(pPlugin, pCommand);
	}

	public ServerSoftware getServerSoftware() {
		return ServerSoftware.BUNGEECORD;
	}

	public void runAsync(PAFPluginBase pPlugin, Runnable pRunnable) {
		ProxyServer.getInstance().getScheduler().runAsync(pPlugin, pRunnable);
	}

	public void schedule(PAFPluginBase instance, Runnable pRunnable, long timeInSeconds) {
		ProxyServer.getInstance().getScheduler().schedule(instance, pRunnable, timeInSeconds, TimeUnit.SECONDS);
	}

	public boolean isOnlineMode() {
		return ProxyServer.getInstance().getConfig().isOnlineMode() || forceUuidSupport;
	}

	public void setForceUuidSupport(boolean forceUuidSupport) {
		this.forceUuidSupport = forceUuidSupport;
	}
} public class LeftPartyEvent extends PartyEvent {
	private final PAFPlayer PLAYER;

	public LeftPartyEvent(PlayerParty pParty, PAFPlayer pPlayer) {
		super(pParty);
		PLAYER = pPlayer;
	}

	public PAFPlayer getPlayer() {
		return PLAYER;
	}
} public interface OnlinePAFPlayer extends PAFPlayer {

	void createEntry();

	void connect(ServerInfo pInfo);

	ServerInfo getServer();

	boolean isOnline();

	int changeSettingsWorth(int pSettingsID);

	@Deprecated
	void sendPacket(Chat chat);

	void sendPacket(TextComponent chat);

	boolean teleportTo(OnlinePAFPlayer pPlayer);

	void update();

	/**
	 * @return Returns the player if he is on this Bungeecord. If he is on
	 * another Bungeecord (RedisBungee) it returns null.
	 */
	ProxiedPlayer getPlayer();

	default PlayerParty getParty() {
		return PartyManager.getInstance().getParty(this);
	}
} public interface PAFPlayer {
	String getName();

	/**
	 * @return Returns the display name of a player. The display name is determined by a {@link DisplayNameProvider}.
	 * To register your {@link DisplayNameProvider} use {@link PAFPlayerClass#setDisplayNameProvider(DisplayNameProvider)}
	 */
	String getDisplayName();

	List<PAFPlayer> getFriends();

	UUID getUniqueId();

	/**
	 * Sends a message to a player if he is online
	 *
	 * @param pMessage The message which should be sent. Can either be a string or a list of messages
	 *                 (a random one from the list will be sent)
	 */
	void sendMessage(Object pMessage);

	/**
	 * Sends a random message from the list to the player if he is online
	 *
	 * @param pMessages A list of messages
	 */
	void sendMessage(List<String> pMessages);

	/**
	 * Sends a message to the player if he is online
	 *
	 * @param pTextComponent The message
	 */
	void sendMessage(TextComponent pTextComponent);

	/**
	 * Sends a message to a player if he is online. The message will be broken into two messages if LINE_BREAK is used
	 * in the message
	 *
	 * @param pText The message which should be sent
	 */
	void sendMessage(String pText);

	/**
	 * Please use {@link PAFPlayer#sendPacket(TextComponent)} instead
	 *
	 * @param chat The message which should be sent
	 */
	@Deprecated
	void sendPacket(Chat chat);

	void sendPacket(TextComponent chat);

	/**
	 * @return Returns true if the player exists in the database. Returns false otherwise.
	 */
	boolean doesExist();

	int getSettingsWorth(int pSettingsID);

	List<PAFPlayer> getRequests();

	int getFriendRequestCount();

	boolean hasRequestFrom(PAFPlayer pPlayer);

	boolean hasPermission(String pPermission);

	void denyRequest(PAFPlayer pPlayer);

	/**
	 * @return Returns true if the player is online. If the player is online it is safe to cast an object of this class
	 * to an {@link OnlinePAFPlayer}
	 */
	boolean isOnline();

	boolean isAFriendOf(PAFPlayer pPlayer);

	PAFPlayer getLastPlayerWroteTo();

	void sendFriendRequest(PAFPlayer pSender);

	void addFriend(PAFPlayer pPlayer);

	PAFPlayer getPAFPlayer();

	void removeFriend(PAFPlayer pPlayer);

	void setSetting(int pSettingsID, int pNewWorth);

	void setLastPlayerWroteFrom(PAFPlayer pLastWroteTo);

	long getLastOnline();

	boolean deleteAccount();

	void updateLastOnline();
} public abstract class PartySubCommand extends SubCommand {

	protected PartySubCommand(List<String> pCommands, int pPriority, String pHelpText, String pPermission) {
		super(pCommands, pPriority, pHelpText, PartyCommand.getInstance().getPrefix(), pPermission);
	}

	public PartySubCommand(String[] pCommands, int pPriority, TextComponent pHelp) {
		super(pCommands, pPriority, pHelp, PartyCommand.getInstance().getPrefix());
	}

	public PartySubCommand(String[] pCommands, int pPriority, String pHelp) {
		super(pCommands, pPriority, pHelp, PartyCommand.getInstance().getPrefix());
	}

	protected boolean isInParty(OnlinePAFPlayer pPlayer, PlayerParty pParty) {
		if (pParty == null) {
			pPlayer.sendMessage(PartyCommand.getInstance().getPrefix()
					+ Main.getInstance().getMessages().getString("Party.Command.General.ErrorNoParty"));
			return false;
		}
		return true;
	}

	public boolean hasAccess(int pPermissionHeight) {
		return true;
	}

	public boolean hasAccess(PlayerParty pPlayer, int pPermissionHeight) {
		return hasAccess(pPermissionHeight);
	}

	protected boolean isPlayerGiven(OnlinePAFPlayer pPlayer, String[] args) {
		if (args.length == 0) {
			pPlayer.sendMessage((PREFIX
					+ Main.getInstance().getMessages().getString("Party.Command.General.ErrorNoPlayer")));
			return false;
		}
		return true;
	}

	@Override
	public void sendError(OnlinePAFPlayer pPlayer, TextComponent pMessage) {
		pPlayer.sendMessage(pMessage);
		if (Main.getInstance().getGeneralConfig().getBoolean("Commands.Party.General.PrintOutHelpOnError"))
			printOutHelp(pPlayer, PartyCommand.getInstance().getName());
	}

} public class Main extends PAFPluginBase implements ErrorReporter {
	/**
	 * The main instance of this plugin
	 */
	private static Main instance;
	private final List<PAFExtension> pafExtensions = new ArrayList<>();
	/**
	 * The configuration
	 */
	private ConfigLoader config;
	/**
	 * The messages.yml
	 */
	private MessagesLoader messages = null;
	/**
	 * The party prefix
	 */
	private String partyPrefix;
	/**
	 * The language
	 */
	private Language language;
	private Friends friendCommand;
	private boolean shuttingDown = false;

	public static Main getInstance() {
		return instance;
	}

	@Deprecated
	public static PartyManager getPartyManager() {
		return PartyManager.getInstance();
	}

	@Deprecated
	public static PAFPlayerManager getPlayerManager() {
		return PAFPlayerManager.getInstance();
	}

	public ConfigurationCreator getGeneralConfig() {
		return config;
	}

	/**
	 * Will be execute on enable
	 */
	@Override
	public void onEnable() {
		instance = (this);
		loadConfiguration();
		try {
			initPAFClasses();
			registerCommands();
			registerListeners();
			new Metrics(this, 508);
			if (getConfig().getBoolean("General.CheckForUpdates")) {
				UpdateSearcher searcher = new UpdateSearcher("Party-and-Friends-Free", getDescription().getVersion());
				ProxyServer.getInstance().getConsole().sendMessage(new TextComponent(searcher.checkForUpdate()));
			}
		} catch (SQLException e) {
			if (e.getMessage().contains("Unable to load authentication plugin 'caching_sha2_password'."))
				initError(e, BootErrorType.SHA_ENCRYPTED_PASSWORD);
			else if (e.getMessage().contains("REFERENCES command denied to user"))
				initError(e, BootErrorType.MISSING_PERMISSION_REFERENCE_COMMAND);
			else
				initError(e, BootErrorType.MYSQL_CONNECTION_PROBLEM);
		}
	}

	private void initError(Throwable e, BootErrorType pType) {
		if (!getGeneralConfig().getBoolean("Commands.Party.TopCommands.Party.Disabled"))
			registerCommand(new BootErrorCommand(
					(getGeneralConfig().getStringList("Commands.Party.TopCommands.Party.Names").toArray(new String[0])), pType));
		Command partyChatCommand = new BootErrorCommand(
				(getGeneralConfig().getStringList("Commands.Party.TopCommands.PartyChat.Names").toArray(new String[0])), pType);
		if (!getGeneralConfig().getBoolean("Commands.Party.TopCommands.PartyChat.Disabled"))
			registerCommand(partyChatCommand);
		if (!getGeneralConfig().getBoolean("Commands.Friends.TopCommands.Friend.Disabled"))
			registerCommand(new BootErrorCommand(getGeneralConfig().getStringList("Commands.Friends.TopCommands.Friend.Names").toArray(new String[0]), pType));
		BootErrorCommand msg = new BootErrorCommand(
				(getGeneralConfig().getStringList("Commands.Friends.TopCommands.MSG.Names").toArray(new String[0])), pType);
		if (!getGeneralConfig().getBoolean("Commands.Friends.TopCommands.MSG.Disabled"))
			registerCommand(msg);
		if (!getGeneralConfig().getBoolean("Commands.Friends.TopCommands.Reply.Disabled"))
			registerCommand(new BootErrorCommand(
					(getGeneralConfig().getStringList("Commands.Friends.TopCommands.Reply.Names").toArray(new String[0])), pType));
		CommandSender console = ProxyServer.getInstance().getConsole();
		reportError(console, pType);
		e.printStackTrace();
	}

	private void initPAFClasses() throws SQLException {
		PoolData poolData = new PoolData(Main.getInstance().getGeneralConfig().getInt("MySQL.Pool.MinPoolSize"),
				Main.getInstance().getGeneralConfig().getInt("MySQL.Pool.MaxPoolSize"),
				Main.getInstance().getGeneralConfig().getInt("MySQL.Pool.InitialPoolSize"), Main.getInstance().getGeneralConfig().getInt("MySQL.Pool.IdleConnectionTestPeriod"),
				Main.getInstance().getGeneralConfig().getBoolean("MySQL.Pool.TestConnectionOnCheckin"), Main.getInstance().getGeneralConfig().getString("MySQL.Pool.ConnectionPool"));
		MySQLData mySQLData = new MySQLData(getGeneralConfig().get("MySQL.Host").toString(),
				getGeneralConfig().get("MySQL.Username").toString(), getGeneralConfig().get("MySQL.Password").toString(),
				getGeneralConfig().getInt("MySQL.Port"), getGeneralConfig().get("MySQL.Database").toString(),
				getGeneralConfig().get("MySQL.TablePrefix").toString(), getGeneralConfig().getBoolean("MySQL.UseSSL"), getGeneralConfig().getBoolean("MySQL.Cache"),
				false, 0);
		new PAFPlayerManagerMySQL(mySQLData, poolData);
		if (getGeneralConfig().getBoolean("General.MultiCoreEnhancement")) {
			PAFPlayerMySQL.setMultiCoreEnhancement(true);
		}
		new LocalPartyManager(Main.getInstance().getGeneralConfig().getInt("Commands.Party.SubCommands.Invite.InvitationTimeOutTimeInSeconds"));
		new StandardPermissionProvider();
		new ServerDisplayNameCollection(getGeneralConfig());
	}

	@Override
	public void onDisable() {
		shuttingDown = true;
		ProxyServer.getInstance().getPluginManager().unregisterListeners(this);
		ProxyServer.getInstance().getPluginManager().unregisterCommands(this);
		Disabler.getInstance().disableAll();
		getProxy().getScheduler().cancel(this);
	}

	/**
	 * Loads the configuration files(config.yml and messages.yml)
	 */
	private void loadConfiguration() {
		try {
			config = new ConfigLoader(new File(Main.getInstance().getDataFolder(), "config.yml"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			language = Language.valueOf(getGeneralConfig().getString("General.Language").toUpperCase());
		} catch (IllegalArgumentException e) {
			getProxy().getConsole().sendMessage(new TextComponent(TextComponent.fromLegacyText("&4The given language is not supported by Party and Friends. English will be used instead.")));
			language = Language.ENGLISH;
			e.printStackTrace();
		}
		try {
			messages = new MessagesLoader(language, getGeneralConfig().getBoolean("General.UseOwnLanguageFile"), new File(getDataFolder(), "messages.yml"), this);
			if (getGeneralConfig().getBoolean("General.UseOwnLanguageFile"))
				language = Language.OWN;
		} catch (IOException e) {
			e.printStackTrace();
		}
		partyPrefix = (getMessages().getString("Party.General.PartyPrefix"));
		System.setProperty("com.mchange.v2.log.MLog", "com.mchange.v2.log.FallbackMLog");
		System.setProperty("com.mchange.v2.log.FallbackMLog.DEFAULT_CUTOFF_LEVEL", "WARNING");
		getAdapter().setForceUuidSupport(config.getBoolean("General.ForceUUIDSupportOnOfflineServers"));
	}

	/**
	 * Registers the listeners
	 */
	private void registerListeners() {
		BukkitBungeeAdapter.getInstance().registerListener(new PlayerDisconnectListener(), this);
		ServerSwitchListener serverSwitchListener = new ServerSwitchListener();
		if (!getGeneralConfig().getBoolean("General.DisableAutomaticPartyServerSwitching"))
			BukkitBungeeAdapter.getInstance().registerListener(serverSwitchListener, this);
		JoinEvent joinEventListener;
		BukkitBungeeAdapter.getInstance().registerListener(joinEventListener = new JoinEvent(), this);
		Exception e = joinEventListener.verify();
		if (e != null)
			initError(e, BootErrorType.TOO_OLD_VERSION);
	}

	/**
	 * Registers the commands
	 */
	private void registerCommands() {
		String friendsPrefix = (getMessages().getString("Friends.General.Prefix"));
		new PartyCommand(
				(getGeneralConfig().getStringList("Commands.Party.TopCommands.Party.Names").toArray(new String[0])), partyPrefix);
		if (!getGeneralConfig().getBoolean("Commands.Party.TopCommands.Party.Disabled"))
			registerTopCommand(PartyCommand.getInstance());
		PartyChat partyChatCommand = new PartyChat(
				(getGeneralConfig().getStringList("Commands.Party.TopCommands.PartyChat.Names").toArray(new String[0])), partyPrefix);
		if (!getGeneralConfig().getBoolean("Commands.Party.TopCommands.PartyChat.Disabled"))
			registerTopCommand(partyChatCommand);
		friendCommand = new Friends(getGeneralConfig().getStringList("Commands.Friends.TopCommands.Friend.Names"), friendsPrefix);
		if (!getGeneralConfig().getBoolean("Commands.Friends.TopCommands.Friend.Disabled"))
			registerTopCommand(friendCommand);
		MSG friendsMSGCommand = new MSG(
				(getGeneralConfig().getStringList("Commands.Friends.TopCommands.MSG.Names").toArray(new String[0])), friendsPrefix);
		if (!getGeneralConfig().getBoolean("Commands.Friends.TopCommands.MSG.Disabled"))
			registerTopCommand(friendsMSGCommand);
		if (!getGeneralConfig().getBoolean("Commands.Friends.TopCommands.Reply.Disabled"))
			registerTopCommand(new Reply(
					(getGeneralConfig().getStringList("Commands.Friends.TopCommands.Reply.Names").toArray(new String[0])), friendsPrefix));
		if (getGeneralConfig().getBoolean("Commands.PAFAdmin.Enabled"))
			registerCommand(new PAFAdminCommand(getGeneralConfig().getStringList("Commands.PAFAdmin.Names").toArray(new String[0])));
	}

	@Deprecated
	public PartyChat getPartyChatCommand() {
		return PartyChat.getInstance();
	}

	@Deprecated
	public MSG getFriendsMSGCommand() {
		return MSG.getInstance();
	}

	@Deprecated
	public Friends getFriendsCommand() {
		return friendCommand;
	}

	/**
	 * @return Returns the normal Main config on Bungeecord, but on Spigot it returns the GUI config.
	 * For that reason it should not be used and instead getGeneralConfig() should be used.
	 */
	@Deprecated
	public Configuration getConfig() {
		return config.getCreatedConfiguration();
	}

	@Deprecated
	public String getFriendsPrefix() {
		return Friends.getInstance().getPrefix();
	}

	public Language getLanguage() {
		return language;
	}

	public LanguageConfiguration getMessages() {
		return messages;
	}

	@Deprecated
	public LanguageConfiguration getMessagesYml() {
		return getMessages();
	}

	@Deprecated
	public PartyCommand getPartyCommand() {
		return PartyCommand.getInstance();
	}

	@Deprecated
	public String getPartyPrefix() {
		return PartyCommand.getInstance().getPrefix();
	}

	public void registerExtension(PAFExtension pPAFExtension) {
		pafExtensions.add(pPAFExtension);
	}

	public void unregisterExtension(PAFExtension pPAFExtension) {
		pafExtensions.remove(pPAFExtension);
	}

	public void reload() {
		onDisable();
		onEnable();
		List<PAFExtension> toReload = new ArrayList<>(pafExtensions);
		pafExtensions.clear();
		for (PAFExtension extension : toReload)
			extension.reload();
	}

	public boolean isShuttingDown() {
		return shuttingDown;
	}
} public class PartyCommand extends TopCommand<PartySubCommand> {
	private static PartyCommand instance;

	/**
	 * @param pCommandNames The alias for the command
	 * @param pPrefix       The prefix for this command (e.g. [Party])
	 */
	public PartyCommand(String[] pCommandNames, String pPrefix) {
		super(pCommandNames, Main.getInstance().getGeneralConfig().getString("Commands.Party.TopCommands.Party.Permissions"), pPrefix);
		instance = this;
		ConfigurationCreator config = Main.getInstance().getGeneralConfig();
		LanguageConfiguration messages = Main.getInstance().getMessages();
		subCommands.add(
				new Join(config.getStringList("Commands.Party.SubCommands.Join.Names"),
						config.getInt("Commands.Party.SubCommands.Join.Priority"), messages.getString("Party.CommandUsage.Join"), config.getString("Commands.Party.SubCommands.Join.Permissions")));
		subCommands.add(new Invite(
				config.getStringList("Commands.Party.SubCommands.Invite.Names"), config.getInt("Commands.Party.SubCommands.Invite.Priority"),
				messages.getString("Party.CommandUsage.Invite"), config.getString("Commands.Party.SubCommands.Invite.Permissions")));
		if (!config.getBoolean("Commands.Party.SubCommands.Deny.Disabled"))
			subCommands.add(new PartyDenySubCommand(
					config.getStringList("Commands.Party.SubCommands.Deny.Names"), config.getInt("Commands.Party.SubCommands.Deny.Priority"),
					messages.getString("Party.CommandUsage.Deny"), config.getString("Commands.Party.SubCommands.Deny.Permissions")));
		if (!config.getBoolean("Commands.Party.SubCommands.Kick.Disabled"))
			subCommands.add(new Kick(
					config.getStringList("Commands.Party.SubCommands.Kick.Names"), config.getInt("Commands.Party.SubCommands.Kick.Priority"),
					messages.getString("Party.CommandUsage.Kick"), config.getString("Commands.Party.SubCommands.Kick.Permissions")));
		if (!config.getBoolean("Commands.Party.SubCommands.Info.Disabled"))
			subCommands.add(new Info(
					config.getStringList("Commands.Party.SubCommands.Info.Names"), config.getInt("Commands.Party.SubCommands.Kick.Priority"),
					messages.getString("Party.CommandUsage.List"), config.getString("Commands.Party.SubCommands.Info.Permissions")));
		subCommands.add(new Leave(
				config.getStringList("Commands.Party.SubCommands.Leave.Names"), config.getInt("Commands.Party.SubCommands.Leave.Priority"),
				messages.getString("Party.CommandUsage.Leave"), config.getString("Commands.Party.SubCommands.Leave.Permissions")));
		if (!config.getBoolean("Commands.Party.SubCommands.Chat.Disabled"))
			subCommands.add(new Chat(
					config.getStringList("Commands.Party.SubCommands.Chat.Names"), config.getInt("Commands.Party.SubCommands.Chat.Priority"),
					messages.getString("Party.CommandUsage.Chat"), config.getString("Commands.Party.SubCommands.Chat.Permissions")));
		if (!config.getBoolean("Commands.Party.SubCommands.Leader.Disabled"))
			subCommands.add(new Leader(
					config.getStringList("Commands.Party.SubCommands.Leader.Names"), config.getInt("Commands.Party.SubCommands.Leader.Priority"),
					messages.getString("Party.CommandUsage.Leader"), config.getString("Commands.Party.SubCommands.Leader.Permissions")));
		if (!config.getBoolean("Commands.Party.SubCommands.InviteSetting.Disabled"))
			subCommands.add(new PartyInviteSettingSubCommand(
					config.getStringList("Commands.Party.SubCommands.InviteSetting.Names"), config.getInt("Commands.Party.SubCommands.InviteSetting.Priority"),
					messages.getString("Party.CommandUsage.InviteSetting"), config.getString("Commands.Party.SubCommands.InviteSetting.Permissions")));
	}

	public static PartyCommand getInstance() {
		return instance;
	}

	private PartySubCommand getCommand(String name) {
		for (PartySubCommand c : subCommands) {
			if (c.isApplicable(name))
				return c;
		}
		return null;
	}

	@Override
	protected void onCommand(OnlinePAFPlayer pPlayer, String[] args) {
		if (args.length == 0) {
			PlayerParty party = PartyManager.getInstance().getParty(pPlayer);
			pPlayer.sendMessage(
					Main.getInstance().getMessages().getString("Party.General.HelpBegin"));
			int permissionHeight = PartyAPI.NO_PARTY_PERMISSION_HEIGHT;
			if (party != null)
				if (party.isLeader(pPlayer))
					permissionHeight = PartyAPI.LEADER_PERMISSION_HEIGHT;
				else
					permissionHeight = PartyAPI.PARTY_MEMBER_PERMISSION_HEIGHT;
			for (PartySubCommand cmd : subCommands)
				if (cmd.hasAccess(party, permissionHeight) || !Main.getInstance().getGeneralConfig().getBoolean("Commands.Party.General.PrintOnlyExecutableSubCommandsOut"))
					cmd.printOutHelp(pPlayer, getName());
			pPlayer.sendMessage(
					Main.getInstance().getMessages().getString("Party.General.HelpEnd"));
			return;
		}
		PartySubCommand sc = getCommand(args[0]);
		if (sc == null) {
			pPlayer.sendMessage(Main.getInstance().getMessages().get(getPrefix(), "Party.Error.CommandNotFound"));
			return;
		}
		if (!sc.hasPermission(pPlayer)) {
			pPlayer.sendMessage(getPrefix() + Main.getInstance().getMessages().getString("Party.Error.NoPermission"));
			return;
		}
		Vector<String> a = new Vector<>(Arrays.asList(args));
		a.remove(0);
		args = a.toArray(new String[a.size()]);
		sc.onCommand(pPlayer, args);
	}
} public class Join extends PartyJoinInviteSubCommand {
	public Join(List<String> pCommands, int pPriority, String pHelpText, String pPermission) {
		super(pCommands, pPriority, pHelpText, pPermission, Main.getInstance().getMessages()
				.getString("Party.Command.Join.MaxPlayersInPartyReached"));
	}

	/**
	 * Will be executed on /party join
	 *
	 * @param pPlayer The player
	 * @param args    The arguments
	 */
	@Override
	public void onCommand(OnlinePAFPlayer pPlayer, String[] args) {
		if (!isPlayerGiven(pPlayer, args))
			return;
		if (isInParty(pPlayer))
			return;
		PAFPlayer pl = PAFPlayerManager.getInstance().getPlayer(args[0]);
		if (!pl.isOnline()) {
			pPlayer.sendMessage(PREFIX
					+ Main.getInstance().getMessages().getString("Party.Command.Join.PlayerHasNoParty"));
			return;
		}
		OnlinePAFPlayer onlinePAFPlayer = (OnlinePAFPlayer) pl;
		PlayerParty party = PartyManager.getInstance().getParty(onlinePAFPlayer);
		if (hasNoParty(pPlayer, party))
			return;
		if (!party.isPrivate()) {
			if (!canInvite(party.getLeader(), party, pPlayer)) {
				return;
			}
		}
		if (party.addPlayer(pPlayer)) {
			party.sendMessage(

					PREFIX + PLAYER_PATTERN
							.matcher(Main.getInstance().getMessages()
									.getString("Party.Command.Join.PlayerHasJoined"))
							.replaceAll(Matcher.quoteReplacement(pPlayer.getDisplayName())));
			if (Main.getInstance().getGeneralConfig().getBoolean("Commands.Party.SubCommands.Join.AutoJoinLeaderServer")) {
				ServerInfo leaderServer = party.getLeader().getServer();
				if (!leaderServer.equals(pPlayer.getServer()))
					pPlayer.connect(leaderServer);
			}
		} else
			pPlayer.sendMessage(PREFIX
					+ Main.getInstance().getMessages().getString("Party.Command.Join.ErrorNoInvitation"));
	}

	@Override
	public boolean hasAccess(int pPermissionHeight) {
		return PartyAPI.NO_PARTY_PERMISSION_HEIGHT == pPermissionHeight;
	}

	private boolean hasNoParty(OnlinePAFPlayer pPlayer, PlayerParty pParty) {
		if (pParty == null) {
			pPlayer.sendMessage(PREFIX
					+ Main.getInstance().getMessages().getString("Party.Command.Join.PlayerHasNoParty"));
			return true;
		}
		return false;
	}

	private boolean isInParty(OnlinePAFPlayer pPlayer) {
		if (PartyManager.getInstance().getParty(pPlayer) != null) {
			pPlayer.sendMessage(PREFIX
					+ Main.getInstance().getMessages().getString("Party.Command.Join.AlreadyInAPartyError"));
			return true;
		}
		return false;
	}
} public class PartyDenySubCommand extends PartySubCommand {
	public PartyDenySubCommand(List<String> pCommands, int pPriority, String pHelpText, String pPermission) {
		super(pCommands, pPriority, pHelpText, pPermission);
	}

	/**
	 * Will be executed on /party join
	 *
	 * @param pPlayer The player
	 * @param args    The arguments
	 */
	@Override
	public void onCommand(OnlinePAFPlayer pPlayer, String[] args) {
		if (!isPlayerGiven(pPlayer, args))
			return;
		PAFPlayer pl = PAFPlayerManager.getInstance().getPlayer(args[0]);
		if (!pl.isOnline()) {
			pPlayer.sendMessage(PREFIX
					+ Main.getInstance().getMessages().getString("Party.Command.Deny.PlayerHasNoParty"));
			return;
		}
		OnlinePAFPlayer onlinePAFPlayer = (OnlinePAFPlayer) pl;
		PlayerParty party = PartyManager.getInstance().getParty(onlinePAFPlayer);
		if (hasNoParty(pPlayer, party))
			return;
		if (party.isInvited(pPlayer)) {
			party.removeFromInvited(pPlayer);
			party.sendMessage(
					PREFIX + PLAYER_PATTERN
							.matcher(Main.getInstance().getMessages()
									.getString("Party.Command.Deny.PlayerHasDeniedInvitation"))
							.replaceAll(Matcher.quoteReplacement(pPlayer.getDisplayName())));
			pPlayer.sendMessage(PREFIX + Main.getInstance().getMessages()
					.getString("Party.Command.Deny.DeniedInvitation"));
			if (party.isPartyEmpty()) {
				party.sendMessage(
						(PREFIX
								+ Main.getInstance().getMessages().getString(
								"Party.Command.General.DissolvedPartyCauseOfNotEnoughPlayers")));
				PartyManager.getInstance().deleteParty(party);
			}
		} else
			pPlayer.sendMessage(PREFIX
					+ Main.getInstance().getMessages().getString("Party.Command.Deny.ErrorNoInvitation"));
	}

	@Override
	public boolean hasAccess(int pPermissionHeight) {
		return PartyAPI.NO_PARTY_PERMISSION_HEIGHT == pPermissionHeight;
	}

	private boolean hasNoParty(OnlinePAFPlayer pPlayer, PlayerParty pParty) {
		if (pParty == null) {
			pPlayer.sendMessage(PREFIX
					+ Main.getInstance().getMessages().getString("Party.Command.Deny.PlayerHasNoParty"));
			return true;
		}
		return false;
	}
} public final class PatterCollection {
	public static final Pattern PLAYER_PATTERN = Pattern.compile("[PLAYER]", Pattern.LITERAL);
	public static final Pattern FRIEND_REQUEST_PATTERN = Pattern.compile("[FRIENDREQUESTS]", Pattern.LITERAL);
	public static final Pattern FRIEND_REQUEST_COUNT_PATTERN = Pattern.compile("[FRIENDREQUESTS_COUNT]", Pattern.LITERAL);
	public static final Pattern SERVER_ON = Pattern.compile("[SERVER_ON]", Pattern.LITERAL);
	public static final Pattern SENDER_NAME_PATTERN = Pattern.compile("[SENDERNAME]", Pattern.LITERAL);
	public static final Pattern MESSAGE_CONTENT_PATTERN = Pattern.compile("[MESSAGE_CONTENT]", Pattern.LITERAL);
	public static final Pattern LEADER_PATTERN = Pattern.compile("[LEADER]", Pattern.LITERAL);
	public static final Pattern MAX_PLAYERS_IN_PARTY_PATTERN = Pattern.compile("[MAXPLAYERSINPARTY]", Pattern.LITERAL);
	public static final Pattern NEW_LEADER_PATTERN = Pattern.compile("[NEWLEADER]", Pattern.LITERAL);
	public static final Pattern SPACE_PATTERN = Pattern.compile(" ", Pattern.LITERAL);
	public static final Pattern CONTENT_PATTERN = Pattern.compile("[CONTENT]", Pattern.LITERAL);
	public static final Pattern LAST_ONLINE_PATTERN = Pattern.compile("[LAST_ONLINE]", Pattern.LITERAL);
	public static final Pattern PAGE_PATTERN = Pattern.compile("[PAGE]", Pattern.LITERAL);

} public static final Pattern PLAYER_PATTERN = Pattern.compile("[PLAYER]", Pattern.LITERAL);
import de.simonsator.partyandfriends.api.adapter.BukkitBungeeAdapter;
import de.simonsator.partyandfriends.api.events.party.LeftPartyEvent;
import de.simonsator.partyandfriends.api.pafplayers.OnlinePAFPlayer;
import de.simonsator.partyandfriends.api.pafplayers.PAFPlayer;
import de.simonsator.partyandfriends.api.party.abstractcommands.PartySubCommand;
import de.simonsator.partyandfriends.main.Main;
import de.simonsator.partyandfriends.party.command.PartyCommand;
import de.simonsator.partyandfriends.party.subcommand.Join;
import de.simonsator.partyandfriends.party.subcommand.PartyDenySubCommand;
import de.simonsator.partyandfriends.utilities.PatterCollection;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.TextComponent;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import static de.simonsator.partyandfriends.utilities.PatterCollection.PLAYER_PATTERN;
package de.simonsator.partyandfriends.api.party;




/**
 * Objects of this class are the party, where a player is in
 *
 * @author Simonsator
 * @version 2.0.0
 */
public abstract class PlayerParty {
	private final String JOIN_COMMAND_NAME = " " + PartyCommand.getInstance().getSubCommand(Join.class).getCommandName() + " ";
	private final String DENY_COMMAND_NAME;
	private final Matcher acceptInviteMatcher = PLAYER_PATTERN.matcher(Main.getInstance().getMessages().getString("Party.Command.Invite.AcceptInvite"));
	private final Matcher denyInviteMatcher = PLAYER_PATTERN.matcher(Main.getInstance().getMessages().getString("Party.Command.Invite.DeclineInvite"));

	public PlayerParty() {
		PartySubCommand denyCommand = PartyCommand.getInstance().getSubCommand(PartyDenySubCommand.class);
		if (denyCommand != null)
			DENY_COMMAND_NAME = " " + denyCommand.getCommandName() + " ";
		else DENY_COMMAND_NAME = null;
	}

	/**
	 * Returns true if the given player is the leader of this party, and it will
	 * returns false if he is not the leader, of this party
	 *
	 * @param player The player
	 * @return Returns a true if the given player is the leader of this party,
	 * and it will returns false if he is not the leader, of this party
	 */
	public abstract boolean isLeader(PAFPlayer player);

	public boolean isLeader(OnlinePAFPlayer player) {
		return isLeader((PAFPlayer) player);
	}

	public abstract boolean isBanned(PAFPlayer pPlayer);

	public abstract void setBanned(PAFPlayer pPlayer, boolean pIsBanned);

	public abstract boolean isPrivate();

	public abstract void setPrivateState(boolean pIsPrivate);

	/**
	 * Returns true if the player is in the party. Returns false if the player
	 * is not in the party.
	 *
	 * @param pPlayer The player
	 * @return Returns true if the player is in the party. Returns false if the
	 * player is not in the party.
	 */
	public boolean isInParty(OnlinePAFPlayer pPlayer) {
		return isAMember(pPlayer) || pPlayer.getUniqueId().equals(getLeader().getUniqueId());
	}

	protected abstract boolean isAMember(OnlinePAFPlayer pPlayer);

	protected abstract List<UUID> getInvited();

	public boolean isNobodyInvited() {
		return getInvited().isEmpty();
	}

	/**
	 * Gets the leader of this party
	 *
	 * @return Returns the party leader
	 */
	public abstract OnlinePAFPlayer getLeader();

	/**
	 * Sets the party leader.
	 *
	 * @param player The player
	 */
	public abstract void setLeader(OnlinePAFPlayer player);

	/**
	 * @return Returns all players in this party (inclusive the leader).
	 */
	public List<OnlinePAFPlayer> getAllPlayers() {
		List<OnlinePAFPlayer> allPlayers = getPlayers();
		PAFPlayer leader = getLeader();
		if (leader != null)
			allPlayers.add((OnlinePAFPlayer) leader);
		return allPlayers;
	}

	/**
	 * Removes a player from the party
	 *
	 * @param pPlayer The player
	 */
	private void removePlayer(PAFPlayer pPlayer) {
		removePlayerSilent(pPlayer);
		sendMessage((PartyCommand.getInstance().getPrefix()
				+ PatterCollection.PLAYER_PATTERN.matcher(Main.getInstance().getMessages().getString("Party.Command.General.PlayerHasLeftTheParty")).replaceAll(Matcher.quoteReplacement(pPlayer.getDisplayName()))));
	}

	protected abstract void removePlayerSilent(PAFPlayer pPlayer);

	/**
	 * Returns the "normal" players who are in the party.
	 *
	 * @return Returns the "normal" players who are in the party.
	 */
	public abstract List<OnlinePAFPlayer> getPlayers();

	/**
	 * Adds a player to the party
	 *
	 * @param pPlayer The player
	 * @return Returns true if the player was added to this party. Returns false
	 * if the player was not added to this party
	 */
	public abstract boolean addPlayer(OnlinePAFPlayer pPlayer);

	public void leaveParty(OnlinePAFPlayer pPlayer) {
		leaveParty((PAFPlayer) pPlayer);
	}

	public void leaveParty(PAFPlayer pPlayer) {
		removePlayer(pPlayer);
		boolean needsNewLeader = needsNewLeader(pPlayer);
		BukkitBungeeAdapter.getInstance().callEvent(new LeftPartyEvent(this, pPlayer));