package nl.rutgerkok.chestsignprotect.impl.protection;

import java.util.Collection;
import java.util.List;

import nl.rutgerkok.chestsignprotect.ProtectionSign;
import nl.rutgerkok.chestsignprotect.profile.PlayerProfile;
import nl.rutgerkok.chestsignprotect.profile.Profile;
import nl.rutgerkok.chestsignprotect.protection.Protection;

import org.apache.commons.lang.Validate;

import com.google.common.base.Optional;
import com.google.common.collect.Lists;

/**
 * Base class for protection implementations.
 *
 */
abstract class AbstractProtection implements Protection {

    private Optional<Collection<Profile>> allAllowed = Optional.absent();
    private Optional<Profile> owner = Optional.absent();
    private Optional<Collection<ProtectionSign>> allSigns = Optional.absent();

    /**
     * Constructor for creating the protection with just a single sign looked
     * up. Not all signs are found yet. If it turns out that all signs are
     * needed, {@link #fetchSigns()} will be called.
     * 
     * @param sign
     *            A sign attached to the protection.
     */
    AbstractProtection(ProtectionSign sign) {
        if (sign.getType().isMainSign()) {
            List<Profile> profiles = sign.getProfiles();
            if (!profiles.isEmpty()) {
                this.owner = Optional.of(profiles.get(0));
            }
        }
    }

    /**
     * Constructor for creating the protection with all signs already looked up.
     * Collection may not be empty.
     * 
     * @param signs
     *            All signs.
     */
    AbstractProtection(Collection<ProtectionSign> signs) {
        Validate.notEmpty(signs);
        this.allSigns = Optional.of(signs);
    }

    private void fetchMainSignAndAllowed(Collection<ProtectionSign> signs) {
        Collection<Profile> allAllowed = Lists.newArrayList();
        for (ProtectionSign sign : signs) {
            // Parse it
            allAllowed.addAll(sign.getProfiles());
        }
        this.allAllowed = Optional.of(allAllowed);
    }

    private Optional<Profile> fetchOwner() {
        for (ProtectionSign sign : getSigns()) {
            if (sign.getType().isMainSign()) {
                List<Profile> profiles = sign.getProfiles();
                if (profiles.isEmpty()) {
                    return Optional.absent();
                }
                return Optional.of(profiles.get(0));
            }
        }
        return Optional.absent();
    }

    /**
     * Finds all signs attached to this protection.
     *
     * @return All signs.
     */
    protected abstract Collection<ProtectionSign> fetchSigns();

    @Override
    public final Collection<Profile> getAllowed() {
        if (allAllowed.isPresent()) {
            return allAllowed.get();
        }
        fetchMainSignAndAllowed(fetchSigns());
        return allAllowed.get();
    }

    @Override
    public final Optional<Profile> getOwner() {
        if (!owner.isPresent()) {
            owner = fetchOwner();
        }

        return owner;
    }

    @Override
    public final Collection<ProtectionSign> getSigns() {
        if (!this.allSigns.isPresent()) {
            this.allSigns = Optional.of(fetchSigns());
        }
        return this.allSigns.get();
    }

    @Override
    public final boolean isAllowed(Profile profile) {
        for (Profile allowed : getAllowed()) {
            if (allowed.includes(profile)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public final boolean isMissingUniqueIds() {
        for (Profile profile : getAllowed()) {
            if (!(profile instanceof PlayerProfile)) {
                continue;
            }
            if (((PlayerProfile) profile).getUniqueId().isPresent()) {
                continue;
            }

            // Found missing id
            return true;
        }
        return false;
    }

    @Override
    public final boolean isOwner(Profile profile) {
        Validate.notNull(profile);

        Optional<Profile> owner = getOwner();

        if (!owner.isPresent()) {
            return false;
        }

        return owner.get().includes(profile);
    }
}

@@ -1707,29 +1707,33 @@
 ate 
-void fetchMainSignAnd
+Collection%3CProfile%3E fetch
 Allo
@@ -1976,38 +1976,15 @@
     
-this.allAllowed = Optional.of(
+return 
 allA
@@ -1989,17 +1989,16 @@
 lAllowed
-)
 ;%0A    %7D%0A
@@ -2479,24 +2479,148 @@
  protection.
+ This method must always do a%0A     * lookup, results may not be cached: %7B@link AbstractProtection%7D already%0A     * does that.
 %0A     *%0A    
@@ -2792,16 +2792,17 @@
     if (
+!
 allAllow
@@ -2823,39 +2823,32 @@
 ) %7B%0A            
-return 
 allAllowed.get()
@@ -2845,58 +2845,36 @@
 owed
-.get();%0A        %7D%0A        fetchMainSignAnd
+ = Optional.of(fetch
 Allowed(
 fetc
@@ -2869,29 +2869,27 @@
 Allowed(
-f
+g
 et
-ch
 Signs())
 ;%0A      
@@ -2872,33 +2872,44 @@
 owed(getSigns())
-;
+);%0A        %7D
 %0A        return 

Fix signs being looked up twice