/*
 * To change this template, choose Tools | Templates and open the template in the editor.
 */

package com.bekvon.bukkit.residence.protection;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginManager;

import com.bekvon.bukkit.residence.Residence;
import com.bekvon.bukkit.residence.event.ResidenceFlagChangeEvent;
import com.bekvon.bukkit.residence.event.ResidenceFlagCheckEvent;
import com.bekvon.bukkit.residence.event.ResidenceFlagEvent.FlagType;
import com.bekvon.bukkit.residence.event.ResidenceOwnerChangeEvent;
import com.bekvon.bukkit.residence.permissions.PermissionGroup;
import com.bekvon.bukkit.residence.permissions.PermissionManager;

import pw.yumc.Residence.ResidenceMain;

/**
 *
 * @author Administrator
 */
public class ResidencePermissions extends FlagPermissions {

    protected String owner;
    protected PluginManager pm;
    protected ClaimedResidence residence;
    protected String world;
    ResidenceMain plugin;

    public static ResidencePermissions load(final ResidenceMain plugin, final ClaimedResidence res, final Map<String, Object> root) throws Exception {
        final ResidencePermissions newperms = new ResidencePermissions(plugin, res);
        newperms.owner = (String) root.get("Owner");
        if (root.containsKey("OwnerLastKnownName")) {
            newperms.owner = (String) root.get("OwnerLastKnownName");
            plugin.getLogger().info("自动转换 UUID " + root.get("OwnerUUID") + " => " + newperms.owner);
        }
        if (newperms.owner == null) {
            plugin.getLogger().warning("发现未知所有者的领地,转换为Server Land...");
            newperms.owner = "Server Land";
        }
        newperms.world = (String) root.get("World");
        FlagPermissions.load(root, newperms);
        if (newperms.owner == null) {
            throw new IllegalArgumentException("错误的Owner数据...");
        }
        if (newperms.world == null) {
            throw new IllegalArgumentException("错误的World数据...");
        }
        newperms.fixNames();
        return newperms;
    }

    public ResidencePermissions(final ResidenceMain plugin, final ClaimedResidence res) {
        this.plugin = plugin;
        this.pm = plugin.getServer().getPluginManager();
        residence = res;
    }

    public ResidencePermissions(final ResidenceMain plugin, final ClaimedResidence res, final String creator, final String inworld) {
        this(plugin, res);
        owner = creator;
        world = inworld;
    }

    public void applyDefaultFlags() {
        final PermissionManager gm = plugin.getPermissionManager();
        final PermissionGroup group = gm.getGroup(owner, world);
        final Set<Entry<String, Boolean>> dflags = group.getDefaultResidenceFlags();
        final Set<Entry<String, Boolean>> dcflags = group.getDefaultCreatorFlags();
        final Set<Entry<String, Map<String, Boolean>>> dgflags = group.getDefaultGroupFlags();
        this.applyGlobalDefaults();
        for (final Entry<String, Boolean> next : dflags) {
            if (this.checkValidFlag(next.getKey(), true)) {
                if (next.getValue()) {
                    this.setFlag(next.getKey(), FlagState.TRUE);
                } else {
                    this.setFlag(next.getKey(), FlagState.FALSE);
                }
            }
        }
        for (final Entry<String, Boolean> next : dcflags) {
            if (this.checkValidFlag(next.getKey(), false)) {
                if (next.getValue()) {
                    this.setPlayerFlag(owner, next.getKey(), FlagState.TRUE);
                } else {
                    this.setPlayerFlag(owner, next.getKey(), FlagState.FALSE);
                }
            }
        }
        for (final Entry<String, Map<String, Boolean>> entry : dgflags) {
            final Map<String, Boolean> value = entry.getValue();
            for (final Entry<String, Boolean> flag : value.entrySet()) {
                if (flag.getValue()) {
                    this.setGroupFlag(entry.getKey(), flag.getKey(), FlagState.TRUE);
                } else {
                    this.setGroupFlag(entry.getKey(), flag.getKey(), FlagState.FALSE);
                }
            }
        }
    }

    public void applyDefaultFlags(final Player player, final boolean resadmin) {
        if (this.hasResidencePermission(player, true) || resadmin) {
            this.applyDefaultFlags();
            player.sendMessage(ChatColor.YELLOW + plugin.getLanguage().getPhrase("FlagsDefault"));
        } else {
            player.sendMessage(ChatColor.GREEN + plugin.getLanguage().getPhrase("NoPermission"));
        }
    }

    public void applyGlobalDefaults() {
        this.clearFlags();
        final FlagPermissions gRD = plugin.getConfigManager().getGlobalResidenceDefaultFlags();
        final FlagPermissions gCD = plugin.getConfigManager().getGlobalCreatorDefaultFlags();
        final Map<String, FlagPermissions> gGD = plugin.getConfigManager().getGlobalGroupDefaultFlags();
        for (final Entry<String, Boolean> entry : gRD.cuboidFlags.entrySet()) {
            if (entry.getValue()) {
                this.setFlag(entry.getKey(), FlagState.TRUE);
            } else {
                this.setFlag(entry.getKey(), FlagState.FALSE);
            }
        }
        for (final Entry<String, Boolean> entry : gCD.cuboidFlags.entrySet()) {
            if (entry.getValue()) {
                this.setPlayerFlag(owner, entry.getKey(), FlagState.TRUE);
            } else {
                this.setPlayerFlag(owner, entry.getKey(), FlagState.FALSE);
            }
        }
        for (final Entry<String, FlagPermissions> entry : gGD.entrySet()) {
            for (final Entry<String, Boolean> flag : entry.getValue().cuboidFlags.entrySet()) {
                if (flag.getValue()) {
                    this.setGroupFlag(entry.getKey(), flag.getKey(), FlagState.TRUE);
                } else {
                    this.setGroupFlag(entry.getKey(), flag.getKey(), FlagState.FALSE);
                }
            }
        }
    }

    public void applyTemplate(final Player player, final FlagPermissions list, boolean resadmin) {
        if (player != null) {
            if (!player.getName().equals(owner) && !resadmin) {
                player.sendMessage(ChatColor.RED + plugin.getLanguage().getPhrase("NoPermission"));
                return;
            }
        } else {
            resadmin = true;
        }
        final PermissionGroup group = plugin.getPermissionManager().getGroup(owner, world);
        for (final Entry<String, Boolean> flag : list.cuboidFlags.entrySet()) {
            if (group.hasFlagAccess(flag.getKey()) || resadmin) {
                this.cuboidFlags.put(flag.getKey(), flag.getValue());
            } else if (player != null) {
                player.sendMessage(ChatColor.RED + plugin.getLanguage().getPhrase("FlagSetDeny", ChatColor.YELLOW + flag.getKey() + ChatColor.RED));
            }
        }
        for (final Entry<String, Map<String, Boolean>> plists : list.playerFlags.entrySet()) {
            for (final Entry<String, Boolean> flag : plists.getValue().entrySet()) {
                if (group.hasFlagAccess(flag.getKey()) || resadmin) {
                    if (!this.playerFlags.containsKey(plists.getKey())) {
                        this.playerFlags.put(plists.getKey(), Collections.synchronizedMap(new HashMap<String, Boolean>()));
                    }
                    this.playerFlags.get(plists.getKey()).put(flag.getKey(), flag.getValue());
                } else if (player != null) {
                    player.sendMessage(ChatColor.RED + plugin.getLanguage().getPhrase("FlagSetDeny", ChatColor.YELLOW + flag.getKey() + ChatColor.RED));
                }
            }
        }
        for (final Entry<String, Map<String, Boolean>> glists : list.groupFlags.entrySet()) {
            for (final Entry<String, Boolean> flag : glists.getValue().entrySet()) {
                if (group.hasFlagAccess(flag.getKey()) || resadmin) {
                    if (!this.groupFlags.containsKey(glists.getKey())) {
                        this.groupFlags.put(glists.getKey(), Collections.synchronizedMap(new HashMap<String, Boolean>()));
                    }
                    this.groupFlags.get(glists.getKey()).put(flag.getKey(), flag.getValue());
                } else if (player != null) {
                    player.sendMessage(ChatColor.RED + plugin.getLanguage().getPhrase("FlagSetDeny", ChatColor.YELLOW + flag.getKey() + ChatColor.RED));
                }
            }
        }
        if (player != null) {
            player.sendMessage(ChatColor.GREEN + plugin.getLanguage().getPhrase("PermissionsApply"));
        }
    }

    public void fixNames() {
        final ArrayList<String> fixNames = new ArrayList<String>();
        final Iterator<Entry<String, Map<String, Boolean>>> it = playerFlags.entrySet().iterator();
        while (it.hasNext()) {
            final String name = it.next().getKey();
            if (!name.equals(name.toLowerCase())) {
                fixNames.add(name);
            }
        }
        for (final String name : fixNames) {
            final Map<String, Boolean> get = playerFlags.get(name);
            playerFlags.remove(name);
            playerFlags.put(name.toLowerCase(), get);
        }
    }

    public String getOwner() {
        return owner;
    }

    public Map<String, Boolean> getPlayerFlags(final String player) {
        return getPlayerFlags(player, false);
    }

    public ArrayList<String> getposibleFlags() {
        return validFlags;
    }

    public List<String> getPosibleFlags(final boolean residence, final boolean resadmin) {
        final List<String> flags = new ArrayList<>();
        for (final Entry<String, Boolean> one : Residence.getPermissionManager().getAllFlags().getFlags().entrySet())
            if ((one.getValue().booleanValue()) || (resadmin)) {
                if ((residence) || (getposibleFlags().contains(one.getKey()))) {
                    flags.add(one.getKey());
                }
            }
        return flags;
    }

    public String getWorld() {
        return world;
    }

    @Override
    public boolean groupHas(final String group, final String flag, final boolean def) {
        final ResidenceFlagCheckEvent fc = new ResidenceFlagCheckEvent(residence, flag, FlagType.GROUP, group, def);
        pm.callEvent(fc);
        if (fc.isOverriden()) {
            return fc.getOverrideValue();
        }
        return super.groupHas(group, flag, def);
    }

    @Override
    public boolean has(final String flag, final boolean def) {
        final ResidenceFlagCheckEvent fc = new ResidenceFlagCheckEvent(residence, flag, FlagType.RESIDENCE, null, def);
        pm.callEvent(fc);
        if (fc.isOverriden()) {
            return fc.getOverrideValue();
        }
        return super.has(flag, def);
    }

    public boolean hasApplicableFlag(final String player, final String flag) {
        return super.inheritanceIsPlayerSet(player, flag) || super.inheritanceIsGroupSet(plugin.getPermissionManager().getGroupNameByPlayer(player, world), flag) || super.inheritanceIsSet(flag);
    }

    public boolean hasResidencePermission(final Player player, final boolean requireOwner) {
        if (plugin.getConfigManager().enabledRentSystem()) {
            final String resname = residence.getName();
            if (plugin.getRentManager().isRented(resname)) {
                if (requireOwner) {
                    return false;
                }
                final String renter = plugin.getRentManager().getRentingPlayer(resname);
                if (player.getName().equalsIgnoreCase(renter)) {
                    return true;
                }
                return (playerHas(player.getName(), "admin", false));
            }
        }
        if (requireOwner) {
            return (owner.equalsIgnoreCase(player.getName()));
        }
        return (playerHas(player.getName(), "admin", false) || owner.equalsIgnoreCase(player.getName()));
    }

    public boolean playerHas(final String player, final String flag, final boolean def) {
        return this.playerHas(player, world, flag, def);
    }

    @Override
    public boolean playerHas(final String player, final String world, final String flag, final boolean def) {
        final ResidenceFlagCheckEvent fc = new ResidenceFlagCheckEvent(residence, flag, FlagType.PLAYER, player, def);
        pm.callEvent(fc);
        if (fc.isOverriden()) {
            return fc.getOverrideValue();
        }
        return super.playerHas(player, world, flag, def);
    }

    public boolean removeAllGroupFlags(final Player player, final String group, final boolean resadmin) {
        if (this.hasResidencePermission(player, false) || resadmin) {
            final ResidenceFlagChangeEvent fc = new ResidenceFlagChangeEvent(residence, player, "ALL", ResidenceFlagChangeEvent.FlagType.GROUP, FlagState.NEITHER, null);
            pm.callEvent(fc);
            if (fc.isCancelled()) {
                return false;
            }
            super.removeAllGroupFlags(group);
            player.sendMessage(ChatColor.GREEN + plugin.getLanguage().getPhrase("FlagSet"));
            return true;
        }
        return false;
    }

    public boolean removeAllPlayerFlags(final Player player, final String targetPlayer, final boolean resadmin) {
        if (this.hasResidencePermission(player, false) || resadmin) {
            final ResidenceFlagChangeEvent fc = new ResidenceFlagChangeEvent(residence, player, "ALL", ResidenceFlagChangeEvent.FlagType.RESIDENCE, FlagState.NEITHER, null);
            pm.callEvent(fc);
            if (fc.isCancelled()) {
                return false;
            }
            super.removeAllPlayerFlags(targetPlayer);
            player.sendMessage(ChatColor.GREEN + plugin.getLanguage().getPhrase("FlagSet"));
            return true;
        }
        return false;
    }

    @Override
    public Map<String, Object> save() {
        final Map<String, Object> root = super.save();
        root.put("Owner", owner);
        root.put("World", world);
        return root;
    }

    public boolean setFlag(final Player player, final String flag, final String flagstate, final boolean resadmin) {
        if (validFlagGroups.containsKey(flag)) {
            return this.setFlagGroup(player, flag, flagstate, resadmin);
        }
        final FlagState state = FlagPermissions.stringToFlagState(flagstate);
        if (checkCanSetFlag(player, flag, state, true, resadmin)) {
            final ResidenceFlagChangeEvent fc = new ResidenceFlagChangeEvent(residence, player, flag, ResidenceFlagChangeEvent.FlagType.RESIDENCE, state, null);
            pm.callEvent(fc);
            if (fc.isCancelled()) {
                return false;
            }
            if (super.setFlag(flag, state)) {
                player.sendMessage(ChatColor.GREEN + plugin.getLanguage().getPhrase("FlagSet"));
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean setFlag(final String flag, final FlagState state) {
        final ResidenceFlagChangeEvent fc = new ResidenceFlagChangeEvent(residence, null, flag, ResidenceFlagChangeEvent.FlagType.RESIDENCE, state, null);
        pm.callEvent(fc);
        if (fc.isCancelled()) {
            return false;
        }
        return super.setFlag(flag, state);
    }

    public boolean setFlagGroup(final Player player, final String flaggroup, final String state, final boolean resadmin) {
        if (ResidencePermissions.validFlagGroups.containsKey(flaggroup)) {
            final ArrayList<String> flags = ResidencePermissions.validFlagGroups.get(flaggroup);
            boolean changed = false;
            for (final String flag : flags) {
                if (this.setFlag(player, flag, state, resadmin)) {
                    changed = true;
                }
            }
            return changed;
        }
        return false;
    }

    public boolean setFlagGroupOnGroup(final Player player, final String flaggroup, final String group, final String state, final boolean resadmin) {
        if (ResidencePermissions.validFlagGroups.containsKey(flaggroup)) {
            final ArrayList<String> flags = ResidencePermissions.validFlagGroups.get(flaggroup);
            boolean changed = false;
            for (final String flag : flags) {
                if (this.setGroupFlag(player, group, flag, state, resadmin)) {
                    changed = true;
                }
            }
            return changed;
        }
        return false;
    }

    public boolean setFlagGroupOnPlayer(final Player player, final String target, final String flaggroup, final String state, final boolean resadmin) {
        if (ResidencePermissions.validFlagGroups.containsKey(flaggroup)) {
            final ArrayList<String> flags = ResidencePermissions.validFlagGroups.get(flaggroup);
            boolean changed = false;
            for (final String flag : flags) {
                if (this.setPlayerFlag(player, target, flag, state, resadmin)) {
                    changed = true;
                }
            }
            return changed;
        }
        return false;
    }

    public boolean setGroupFlag(final Player player, String group, final String flag, final String flagstate, final boolean resadmin) {
        group = group.toLowerCase();
        if (validFlagGroups.containsKey(flag)) {
            return this.setFlagGroupOnGroup(player, flag, group, flagstate, resadmin);
        }
        final FlagState state = FlagPermissions.stringToFlagState(flagstate);
        if (checkCanSetFlag(player, flag, state, false, resadmin)) {
            if (plugin.getPermissionManager().hasGroup(group)) {
                final ResidenceFlagChangeEvent fc = new ResidenceFlagChangeEvent(residence, player, flag, ResidenceFlagChangeEvent.FlagType.GROUP, state, group);
                pm.callEvent(fc);
                if (fc.isCancelled()) {
                    return false;
                }
                if (super.setGroupFlag(group, flag, state)) {
                    player.sendMessage(ChatColor.GREEN + plugin.getLanguage().getPhrase("FlagSet"));
                    return true;
                }
            } else {
                player.sendMessage(ChatColor.RED + plugin.getLanguage().getPhrase("InvalidGroup"));
                return false;
            }
        }
        return false;
    }

    @Override
    public boolean setGroupFlag(final String group, final String flag, final FlagState state) {
        final ResidenceFlagChangeEvent fc = new ResidenceFlagChangeEvent(residence, null, flag, ResidenceFlagChangeEvent.FlagType.GROUP, state, group);
        pm.callEvent(fc);
        if (fc.isCancelled()) {
            return false;
        }
        return super.setGroupFlag(group, flag, state);
    }

    public void setOwner(final String newOwner, final boolean resetFlags) {
        final ResidenceOwnerChangeEvent ownerchange = new ResidenceOwnerChangeEvent(residence, newOwner);
        pm.callEvent(ownerchange);
        owner = newOwner;
        if (resetFlags) {
            this.applyDefaultFlags();
        }
    }

    public boolean setPlayerFlag(final Player player, final String targetPlayer, final String flag, final String flagstate, final boolean resadmin) {
        if (!plugin.validName(targetPlayer)) {
            player.sendMessage(ChatColor.DARK_RED + "非法操作!");
            return false;
        }
        if (validFlagGroups.containsKey(flag)) {
            return this.setFlagGroupOnPlayer(player, targetPlayer, flag, flagstate, resadmin);
        }
        final FlagState state = FlagPermissions.stringToFlagState(flagstate);
        if (checkCanSetFlag(player, flag, state, false, resadmin)) {
            final ResidenceFlagChangeEvent fc = new ResidenceFlagChangeEvent(residence, player, flag, ResidenceFlagChangeEvent.FlagType.PLAYER, state, targetPlayer);
            pm.callEvent(fc);
            if (fc.isCancelled()) {
                return false;
            }
            if (super.setPlayerFlag(targetPlayer, flag, state)) {
                player.sendMessage(ChatColor.GREEN + plugin.getLanguage().getPhrase("FlagSet"));
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean setPlayerFlag(final String player, final String flag, final FlagState state) {
        final ResidenceFlagChangeEvent fc = new ResidenceFlagChangeEvent(residence, null, flag, ResidenceFlagChangeEvent.FlagType.PLAYER, state, player);
        pm.callEvent(fc);
        if (fc.isCancelled()) {
            return false;
        }
        return super.setPlayerFlag(player, flag, state);
    }

    private boolean checkCanSetFlag(final Player player, final String flag, final FlagState state, final boolean globalflag, final boolean resadmin) {
        if (!checkValidFlag(flag, globalflag)) {
            player.sendMessage(ChatColor.RED + plugin.getLanguage().getPhrase("InvalidFlag"));
            return false;
        }
        if (state == FlagState.INVALID) {
            player.sendMessage(ChatColor.RED + plugin.getLanguage().getPhrase("InvalidFlagState"));
            return false;
        }
        if (!resadmin) {
            if (!this.hasResidencePermission(player, false)) {
                player.sendMessage(ChatColor.RED + plugin.getLanguage().getPhrase("NoPermission"));
                return false;
            }
            if (!hasFlagAccess(owner, flag)) {
                player.sendMessage(ChatColor.RED + plugin.getLanguage().getPhrase("OwnerNoPermission"));
                return false;
            }
        }
        return true;
    }

    private boolean hasFlagAccess(final String player, final String flag) {
        final PermissionGroup group = plugin.getPermissionManager().getGroup(player, world);
        return group.hasFlagAccess(flag);
    }

    protected Map<String, Boolean> getPlayerFlags(final String player, final boolean allowCreate) {
        Map<String, Boolean> flags = null;
        for (final Entry<String, Map<String, Boolean>> one : this.playerFlags.entrySet())
            if (one.getKey().equalsIgnoreCase(player)) {
                flags = one.getValue();
                break;
            }
        if ((flags == null) && (allowCreate)) {
            flags = Collections.synchronizedMap(new HashMap<String, Boolean>());
            this.playerFlags.put(player, flags);
        }
        return flags;
    }
}
