/*
 * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.controller.sal.rest.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.stream.events.StartElement;

import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO.IdentityValue;
import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO.Predicate;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;

public final class RestUtil {

    public static final String SQUOTE = "'";
    public static final String DQUOTE = "\"";
    private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]");

    public final static TypeDefinition<?> resolveBaseTypeFrom(TypeDefinition<?> type) {
        TypeDefinition<?> superType = type;
        while (superType.getBaseType() != null) {
            superType = superType.getBaseType();
        }
        return superType;
    }

    public static IdentityValuesDTO asInstanceIdentifier(String value, PrefixesMaping prefixMap) {
        String valueTrimmed = value.trim();
        if (!valueTrimmed.startsWith("/")) {
            return null;
        }
        String[] xPathParts = valueTrimmed.split("/");
        if (xPathParts.length < 2) { // must be at least "/pr:node"
            return null;
        }
        IdentityValuesDTO identityValuesDTO = new IdentityValuesDTO(value);
        for (int i = 1; i < xPathParts.length; i++) {
            String xPathPartTrimmed = xPathParts[i].trim();

            String xPathPartStr = getIdAndPrefixAsStr(xPathPartTrimmed);
            IdentityValue identityValue = toIdentity(xPathPartStr, prefixMap);
            if (identityValue == null) {
                return null;
            }

            List<Predicate> predicates = toPredicates(xPathPartTrimmed, prefixMap);
            if (predicates == null) {
                return null;
            }
            identityValue.setPredicates(predicates);

            identityValuesDTO.add(identityValue);
        }
        return identityValuesDTO.getValuesWithNamespaces().isEmpty() ? null : identityValuesDTO;
    }

    private static String getIdAndPrefixAsStr(String pathPart) {
        int predicateStartIndex = pathPart.indexOf("[");
        return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex);
    }

    private static IdentityValue toIdentity(String xPathPart, PrefixesMaping prefixMap) {
        String xPathPartTrimmed = xPathPart.trim();
        if (xPathPartTrimmed.isEmpty()) {
            return null;
        }
        String[] prefixAndIdentifier = xPathPartTrimmed.split(":");
        // it is not "prefix:value"
        if (prefixAndIdentifier.length != 2) {
            return null;
        }
        String prefix = prefixAndIdentifier[0].trim();
        String identifier = prefixAndIdentifier[1].trim();
        if (prefix.isEmpty() || identifier.isEmpty()) {
            return null;
        }
        String namespace = prefixMap.getNamespace(prefix);
        return new IdentityValue(namespace, identifier, namespace.equals(prefix) ? null : prefix);
    }

    private static List<Predicate> toPredicates(String predicatesStr, PrefixesMaping prefixMap) {
        List<Predicate> result = new ArrayList<>();
        List<String> predicates = new ArrayList<>();
        Matcher matcher = PREDICATE_PATTERN.matcher(predicatesStr);
        while (matcher.find()) {
            predicates.add(matcher.group(1).trim());
        }
        for (String predicate : predicates) {
            int indexOfEqualityMark = predicate.indexOf("=");
            if (indexOfEqualityMark != -1) {
                String predicateValue = toPredicateValue(predicate.substring(indexOfEqualityMark + 1));
                if (predicate.startsWith(".")) { // it is leaf-list
                    if (predicateValue == null) {
                        return null;
                    }
                    result.add(new Predicate(null, predicateValue));
                } else {
                    IdentityValue identityValue = toIdentity(predicate.substring(0, indexOfEqualityMark),
                            prefixMap);
                    if (identityValue == null || predicateValue == null) {
                        return null;
                    }
                    result.add(new Predicate(identityValue, predicateValue));
                }
            }
        }
        return result;
    }

    private static String toPredicateValue(String predicatedValue) {
        String predicatedValueTrimmed = predicatedValue.trim();
        if ((predicatedValueTrimmed.startsWith(DQUOTE) || predicatedValueTrimmed.startsWith(SQUOTE))
                && (predicatedValueTrimmed.endsWith(DQUOTE) || predicatedValueTrimmed.endsWith(SQUOTE))) {
            return predicatedValueTrimmed.substring(1, predicatedValueTrimmed.length() - 1);
        }
        return null;
    }

    public interface PrefixesMaping {
        public String getNamespace(String prefix);
    }

    public static class PrefixMapingFromXml implements PrefixesMaping {
        StartElement startElement = null;

        public PrefixMapingFromXml(StartElement startElement) {
            this.startElement = startElement;
        }

        @Override
        public String getNamespace(String prefix) {
            return startElement.getNamespaceContext().getNamespaceURI(prefix);
        }
    }

    public static class PrefixMapingFromJson implements PrefixesMaping {

        @Override
        public String getNamespace(String prefix) {
            return prefix;
        }
    }

}

@@ -848,24 +848,89 @@
 RestUtil %7B%0A%0A
+    // FIXME: BUG-1275: this is code duplicates data.impl.codec%0A%0A
     public s
@@ -962,16 +962,16 @@
  = %22'%22;%0A
-
     publ
@@ -1156,16 +1156,22 @@
 ypeFrom(
+final 
 TypeDefi
@@ -1431,16 +1431,22 @@
 ntifier(
+final 
 String v
@@ -1450,16 +1450,22 @@
 g value,
+ final
  Prefixe
@@ -2624,16 +2624,22 @@
 ixAsStr(
+final 
 String p
@@ -2855,16 +2855,22 @@
 dentity(
+final 
 String x
@@ -2878,16 +2878,22 @@
 athPart,
+ final
  Prefixe
@@ -3646,16 +3646,22 @@
 dicates(
+final 
 String p
@@ -3673,16 +3673,22 @@
 atesStr,
+ final
  Prefixe
@@ -4978,16 +4978,22 @@
 teValue(
+final 
 String p
@@ -5659,16 +5659,22 @@
 FromXml(
+final 
 StartEle
@@ -5796,32 +5796,38 @@
 ng getNamespace(
+final 
 String prefix) %7B
@@ -6007,32 +6007,32 @@
       @Override%0A
-
         public S
@@ -6042,32 +6042,38 @@
 ng getNamespace(
+final 
 String prefix) %7B

drop a marker for follow-up