package alien4cloud.tosca.parser.impl.base;

import java.util.Map;
import java.util.Set;

import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeTuple;
import org.yaml.snakeyaml.nodes.ScalarNode;

import alien4cloud.tosca.parser.INodeParser;
import alien4cloud.tosca.parser.ParsingContextExecution;
import alien4cloud.tosca.parser.ParsingError;
import alien4cloud.tosca.parser.impl.ErrorCode;
import alien4cloud.tosca.parser.mapping.DefaultParser;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/**
 * Map using a child parser based on a discriminator key (valid only for MappingNode).
 */
public class KeyDiscriminatorParser<T> extends DefaultParser<T> {
    private static final String CATCH_ALL = "__";

    private Map<String, INodeParser<T>> parserByExistKey;
    private INodeParser<T> fallbackParser;

    /**
     * Create a new key discriminator parser instance.
     * 
     * @param parserByExistKey A map of existing keys to the parser to use in case the key exists.
     * @param fallbackParser The parser to use if none of the key is actually found or if the node type is not a MappingNode.
     */
    public KeyDiscriminatorParser(Map<String, INodeParser<T>> parserByExistKey, INodeParser<T> fallbackParser) {
        if (parserByExistKey == null) {
            this.parserByExistKey = Maps.newLinkedHashMap();
        } else {
            this.parserByExistKey = parserByExistKey;
        }
        this.fallbackParser = fallbackParser;
    }

    @Override
    public T parse(Node node, ParsingContextExecution context) {
        Set<String> keySet = Sets.newHashSet();
        if (node instanceof MappingNode) {
            // create a set of available keys
            MappingNode mappingNode = (MappingNode) node;
            for (NodeTuple tuple : mappingNode.getValue()) {
                keySet.add(((ScalarNode) tuple.getKeyNode()).getValue());
            }
            // check if one of the discriminator key exists and if so use it for parsing.
            for (Map.Entry<String, INodeParser<T>> entry : parserByExistKey.entrySet()) {
                if (CATCH_ALL.equals(entry.getKey()) || keySet.contains(entry.getKey())) {
                    return entry.getValue().parse(node, context);
                }
            }
        }
        if (fallbackParser != null) {
            return fallbackParser.parse(node, context);
        } else {
            context.getParsingErrors().add(
                    new ParsingError(ErrorCode.UNKNWON_DISCRIMINATOR_KEY, "Invalid scalar value.", node.getStartMark(),
                            "Tosca type cannot be expressed with the given scalar value.", node.getEndMark(), keySet.toString()));
        }
        return null;
    }

}

@@ -778,25 +778,41 @@
  String 
-CATCH_ALL
+MAPPING_NODE_FALLBACK_KEY
  = %22__%22;
@@ -1982,32 +1982,93 @@
 ;%0A            %7D%0A
+            INodeParser%3CT%3E mappingNodeFallbackParser = null;%0A
             // c
@@ -2255,23 +2255,22 @@
 if (
-CATCH_ALL.equal
+keySet.contain
 s(en
@@ -2286,43 +2286,8 @@
 y())
- %7C%7C keySet.contains(entry.getKey())
 ) %7B%0A
@@ -2361,32 +2361,386 @@
                %7D
+ else if (MAPPING_NODE_FALLBACK_KEY.equals(entry.getKey())) %7B%0A                    mappingNodeFallbackParser = entry.getValue();%0A                %7D%0A            %7D%0A%0A            // if not we should use the mapping node fallback parser.%0A            if (mappingNodeFallbackParser != null) %7B%0A                return mappingNodeFallbackParser.parse(node, context);
 %0A            %7D%0A 
@@ -2902,37 +2902,16 @@
 s().add(
-%0A                    
 new Pars
@@ -3002,24 +3002,16 @@
 Mark(),%0A
-        
         

Fix a bug in parsing of values introduced since mapping of complex properties and causing get_inputs parsing to fail.