/*
 * Copyright 2015 The original authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package io.sundr.dsl.internal.utils;

import io.sundr.codegen.model.JavaClazz;
import io.sundr.codegen.model.JavaClazzBuilder;
import io.sundr.codegen.model.JavaMethod;
import io.sundr.codegen.model.JavaType;
import io.sundr.codegen.model.JavaTypeBuilder;
import io.sundr.dsl.internal.processor.Node;
import io.sundr.dsl.internal.type.functions.Combine;
import io.sundr.dsl.internal.type.functions.Generics;
import io.sundr.dsl.internal.type.functions.Generify;

import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;

public final class DslUtils {

    private DslUtils() {
        //Utility Class
    }

    public static JavaClazz createRootInterface(Node<JavaClazz> current, Set<JavaClazz> intermediate) {
        JavaClazz rootInterface = null;
        Set<JavaType> interfaces = new LinkedHashSet<JavaType>();
        for (Node<JavaClazz> child : current.getTransitions()) {
            JavaClazz transitionInterface = createTransitionInterface(child, intermediate);
            interfaces.add(transitionInterface.getType());
            intermediate.add(transitionInterface);
            intermediate.add(child.getItem());
        }
        JavaType rootType = new JavaTypeBuilder(current.getItem().getType())
                .withInterfaces(interfaces)
                .withGenericTypes(new JavaType[]{})
                .build();
        return new JavaClazzBuilder(current.getItem())
                .withType(Generics.UNWRAP.apply(rootType))
                .withMethods(new LinkedHashSet<JavaMethod>())
                .build();
    }
    
    public static JavaClazz createTransitionInterface(Node<JavaClazz> current, Set<JavaClazz> intermediate) {
        if (current.getTransitions().isEmpty()) {
            return current.getItem();
        } else if (current.getTransitions().size() == 1) {
            Node next = current.getTransitions().iterator().next();
            JavaClazz clazz = current.getItem();
            JavaClazz nextClazz = createTransitionInterface(next, intermediate);
            JavaClazz transition = transition(clazz, nextClazz);
            intermediate.add(nextClazz);
            return transition;
        } else {
            JavaClazz clazz = current.getItem();
            Set<JavaClazz> toCombine = new LinkedHashSet<JavaClazz>();

            for (Node<JavaClazz> v : current.getTransitions()) {
                toCombine.add(createTransitionInterface(v, intermediate));
            }

            JavaClazz combined = Combine.FUNCTION.apply(Generify.FUNCTION.apply(toCombine));
            intermediate.addAll(toCombine);
            if (JavaTypeUtils.isCardinalityMultiple(clazz)) {
                JavaClazz selfRef = transition(clazz, combined);
                Set<JavaClazz> toReCombine = new LinkedHashSet<JavaClazz>(toCombine);
                toReCombine.add(selfRef);
                JavaClazz reCombined = Combine.FUNCTION.apply(toReCombine);
                intermediate.add(reCombined);
                intermediate.add(combined);
                return transition(clazz, reCombined);
            } else {
                intermediate.add(combined);
                return transition(clazz, combined);
            }
        }
    }

    public static JavaClazz transition(JavaClazz from, JavaClazz to) {
        JavaType transitionInterface = new JavaTypeBuilder(from.getType())
                .withGenericTypes(to.getType()).build();

        return new JavaClazzBuilder(from)
                .withType(transitionInterface)
                .build();

    }

}

@@ -3282,24 +3282,71 @@
 e(clazz)) %7B%0A
+                //1st pass create the self ref%0A
             
@@ -3390,32 +3390,32 @@
 azz, combined);%0A
-
                 
@@ -3593,32 +3593,322 @@
 ly(toReCombine);
+%0A%0A                //2nd pass recreate the combination%0A                selfRef =  transition(clazz, reCombined);%0A                toReCombine = new LinkedHashSet%3CJavaClazz%3E(toCombine);%0A                toReCombine.add(selfRef);%0A                reCombined = Combine.FUNCTION.apply(toReCombine);
 %0A               
@@ -4511,10 +4511,314 @@
 %0A    %7D%0A%0A
+    public static boolean isExtendedBy(JavaType candidate, JavaType target) %7B%0A        for (JavaType type : target.getInterfaces()) %7B%0A            if (type.getFullyQualifiedName().equals(candidate.getFullyQualifiedName())) %7B%0A                return true;%0A            %7D%0A        %7D%0A        return false;%0A    %7D%0A
 %7D%0A

Add a second pass when combining interfaces for use with @Multiple.