package org.monospark.spongematchers.matcher.sponge;

import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;

import org.monospark.spongematchers.matcher.SpongeMatcher;
import org.monospark.spongematchers.matcher.complex.MapMatcher;
import org.spongepowered.api.block.BlockState;
import org.spongepowered.api.block.BlockType;
import org.spongepowered.api.block.trait.BlockTrait;
import org.spongepowered.api.block.trait.EnumTrait;
import org.spongepowered.api.data.DataQuery;
import org.spongepowered.api.data.DataView;

import com.google.common.collect.Maps;

public final class BlockStateMatcher extends SpongeObjectMatcher<BlockState> {

    public static SpongeMatcher<BlockState> create(SpongeMatcher<BlockType> type,
            SpongeMatcher<Map<String, Object>> traits, SpongeMatcher<Optional<DataView>> data) {
        return new BlockStateMatcher(MapMatcher.builder()
                .addMatcher("type", BlockType.class, type)
                .addMatcher("traits", Map.class, traits)
                .addOptionalMatcher("data", DataView.class, data)
                .build());
    }
    
    public static SpongeMatcher<BlockState> create(SpongeMatcher<Map<String, Object>> matcher) {
        return new BlockStateMatcher(matcher);
    }
    
    private BlockStateMatcher(SpongeMatcher<Map<String, Object>> matcher) {
        super(matcher);
    }

    @Override
    protected void fillMap(BlockState o, Map<String, Object> map) {
        map.put("type", o.getType());
        map.put("traits", createTraits(o));
        if (o.toContainer().get(DataQuery.of("UnsafeData")).isPresent()) {
            map.put("data", o.toContainer().get(DataQuery.of("UnsafeData")).get());
        }
    }
    
    private Map<String, Object> createTraits(BlockState state) {
        Map<String, Object> map = Maps.newHashMap();
        for (Entry<BlockTrait<?>,?> entry : state.getTraitMap().entrySet()) {
            Object value = entry.getKey() instanceof EnumTrait ? entry.getValue().toString() : entry.getValue();
            map.put(entry.getKey().getId(), value);
        }
        return map;
    }

//    private SpongeMatcher<BlockType> typeMatcher;
//    
//    private Map<String, BlockTraitMatcher<?>> traitMatchers;
//    
//    private SpongeMatcher<Optional<DataView>> dataMatcher;
//
//    private BlockStateMatcher(SpongeMatcher<BlockType> typeMatcher, Map<String, BlockTraitMatcher<?>> traitMatchers,
//            SpongeMatcher<Optional<DataView>> dataMatcher) {
//        this.typeMatcher = typeMatcher;
//        this.traitMatchers = traitMatchers;
//        this.dataMatcher = dataMatcher;
//    }
//
//    @Override
//    public boolean matches(BlockState o) {
//        if (!typeMatcher.matches(o.getType())) {
//            return false;
//        }
//        
//        Optional<DataView> data = o.toContainer().getView(DataQuery.of("UnsafeData"));
//        if (!dataMatcher.matches(data)) {
//            return false;
//        }
//
//        for (Entry<String, BlockTraitMatcher<?>> traitMatcher : traitMatchers.entrySet()) {
//            Optional<BlockTrait<?>> trait = o.getTrait(traitMatcher.getKey());
//            if (!trait.isPresent()) {
//                return false;
//            }
//            
//            Optional<?> value = o.getTraitValue(trait.get());
//            if (!matchesTrait(traitMatcher.getValue(), value.get())) {
//                return false;
//            }
//        }
//        return true;
//    }
//    
//    @SuppressWarnings("unchecked")
//    private <T> boolean matchesTrait(BlockTraitMatcher<T> matcher, Object value) {
//        return matcher.getMatcher().matches((T) value);
//    }
//    
//    public static Builder builder() {
//        return new Builder();
//    }
//    
//    public static final class Builder {
//        
//        private SpongeMatcher<BlockType> typeMatcher;
//        
//        private Map<String, BlockTraitMatcher<?>> traitMatchers;
//        
//        private SpongeMatcher<Optional<DataView>> dataMatcher;
//
//        public Builder() {
//            typeMatcher = SpongeMatcher.wildcard();
//            traitMatchers = Maps.newHashMap();
//            dataMatcher = SpongeMatcher.wildcard();
//        }
//        
//        public Builder type(SpongeMatcher<BlockType> typeMatcher) {
//            this.typeMatcher = Objects.requireNonNull(typeMatcher);
//            return this;
//        }
//        
//        public Builder addBooleanTrait(String name, SpongeMatcher<Boolean> traitMatcher) {
//            traitMatchers.put(name, new BlockTraitMatcher<>(Type.BOOLEAN_TRAIT, traitMatcher));
//            return this;
//        }
//        
//        public Builder addIntegerTrait(String name, SpongeMatcher<Integer> traitMatcher) {
//            traitMatchers.put(name, new BlockTraitMatcher<>(Type.INTEGER_TRAIT, traitMatcher));
//            return this;
//        }
//        
//        public Builder addEnumTrait(String name, SpongeMatcher<String> traitMatcher) {
//            traitMatchers.put(name, new BlockTraitMatcher<>(Type.ENUM_TRAIT, traitMatcher));
//            return this;
//        }
//        
//        public Builder data(SpongeMatcher<Optional<DataView>> dataMatcher) {
//            this.dataMatcher = Objects.requireNonNull(dataMatcher);
//            return this;
//        }
//        
//        public SpongeMatcher<BlockState> build() {
//            return new BlockStateMatcher(typeMatcher, traitMatchers, dataMatcher);
//        }
//    }
}

@@ -2119,3396 +2119,7 @@
    %7D
-%0A%0A//    private SpongeMatcher%3CBlockType%3E typeMatcher;%0A//    %0A//    private Map%3CString, BlockTraitMatcher%3C?%3E%3E traitMatchers;%0A//    %0A//    private SpongeMatcher%3COptional%3CDataView%3E%3E dataMatcher;%0A//%0A//    private BlockStateMatcher(SpongeMatcher%3CBlockType%3E typeMatcher, Map%3CString, BlockTraitMatcher%3C?%3E%3E traitMatchers,%0A//            SpongeMatcher%3COptional%3CDataView%3E%3E dataMatcher) %7B%0A//        this.typeMatcher = typeMatcher;%0A//        this.traitMatchers = traitMatchers;%0A//        this.dataMatcher = dataMatcher;%0A//    %7D%0A//%0A//    @Override%0A//    public boolean matches(BlockState o) %7B%0A//        if (!typeMatcher.matches(o.getType())) %7B%0A//            return false;%0A//        %7D%0A//        %0A//        Optional%3CDataView%3E data = o.toContainer().getView(DataQuery.of(%22UnsafeData%22));%0A//        if (!dataMatcher.matches(data)) %7B%0A//            return false;%0A//        %7D%0A//%0A//        for (Entry%3CString, BlockTraitMatcher%3C?%3E%3E traitMatcher : traitMatchers.entrySet()) %7B%0A//            Optional%3CBlockTrait%3C?%3E%3E trait = o.getTrait(traitMatcher.getKey());%0A//            if (!trait.isPresent()) %7B%0A//                return false;%0A//            %7D%0A//            %0A//            Optional%3C?%3E value = o.getTraitValue(trait.get());%0A//            if (!matchesTrait(traitMatcher.getValue(), value.get())) %7B%0A//                return false;%0A//            %7D%0A//        %7D%0A//        return true;%0A//    %7D%0A//    %0A//    @SuppressWarnings(%22unchecked%22)%0A//    private %3CT%3E boolean matchesTrait(BlockTraitMatcher%3CT%3E matcher, Object value) %7B%0A//        return matcher.getMatcher().matches((T) value);%0A//    %7D%0A//    %0A//    public static Builder builder() %7B%0A//        return new Builder();%0A//    %7D%0A//    %0A//    public static final class Builder %7B%0A//        %0A//        private SpongeMatcher%3CBlockType%3E typeMatcher;%0A//        %0A//        private Map%3CString, BlockTraitMatcher%3C?%3E%3E traitMatchers;%0A//        %0A//        private SpongeMatcher%3COptional%3CDataView%3E%3E dataMatcher;%0A//%0A//        public Builder() %7B%0A//            typeMatcher = SpongeMatcher.wildcard();%0A//            traitMatchers = Maps.newHashMap();%0A//            dataMatcher = SpongeMatcher.wildcard();%0A//        %7D%0A//        %0A//        public Builder type(SpongeMatcher%3CBlockType%3E typeMatcher) %7B%0A//            this.typeMatcher = Objects.requireNonNull(typeMatcher);%0A//            return this;%0A//        %7D%0A//        %0A//        public Builder addBooleanTrait(String name, SpongeMatcher%3CBoolean%3E traitMatcher) %7B%0A//            traitMatchers.put(name, new BlockTraitMatcher%3C%3E(Type.BOOLEAN_TRAIT, traitMatcher));%0A//            return this;%0A//        %7D%0A//        %0A//        public Builder addIntegerTrait(String name, SpongeMatcher%3CInteger%3E traitMatcher) %7B%0A//            traitMatchers.put(name, new BlockTraitMatcher%3C%3E(Type.INTEGER_TRAIT, traitMatcher));%0A//            return this;%0A//        %7D%0A//        %0A//        public Builder addEnumTrait(String name, SpongeMatcher%3CString%3E traitMatcher) %7B%0A//            traitMatchers.put(name, new BlockTraitMatcher%3C%3E(Type.ENUM_TRAIT, traitMatcher));%0A//            return this;%0A//        %7D%0A//        %0A//        public Builder data(SpongeMatcher%3COptional%3CDataView%3E%3E dataMatcher) %7B%0A//            this.dataMatcher = Objects.requireNonNull(dataMatcher);%0A//            return this;%0A//        %7D%0A//        %0A//        public SpongeMatcher%3CBlockState%3E build() %7B%0A//            return new BlockStateMatcher(typeMatcher, traitMatchers, dataMatcher);%0A//        %7D%0A//    %7D
 %0A%7D%0A

Remove some leftover code