package com.kiselev.reflection.ui.generic;

import com.kiselev.reflection.ui.name.NameUtils;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.List;

public class GenericsUtils {

    public String getGenerics(GenericDeclaration genericDeclaration) {
        List<String> generics = new ArrayList<String>();

        String whitespace = (genericDeclaration instanceof Class) ? " " : "";

        TypeVariable<?>[] typeParameters = genericDeclaration.getTypeParameters();
        for (TypeVariable parameter : typeParameters) {
            String bounds = String.join(" & ", getBounds(parameter));
            generics.add(parameter.getName() + (!bounds.isEmpty() ? " extends " + bounds : ""));
        }

        return (typeParameters.length != 0) ? "<" + String.join(", ", generics) + "> " : whitespace;
    }

    private List<String> getBounds(TypeVariable parameter) {
        List<String> bounds = new ArrayList<String>();
        for (Type bound : parameter.getBounds()) {
            String boundType = resolveType(bound);
            if (!boundType.isEmpty()) {
                bounds.add(boundType);
            }
        }
        return bounds;
    }

    public String resolveType(Type type) {
        String boundType = "";

        if (type instanceof Class) {
            Class clazz = Class.class.cast(type);
            if (clazz.isSynthetic()) {
                boundType = new NameUtils().getTypeName(clazz);
            } else {
                boundType = clazz.getSimpleName();
            }

        } else if (type instanceof TypeVariable) {
            boundType = TypeVariable.class.cast(type).getName();

        } else if (type instanceof ParameterizedType) {
            ParameterizedTypeImpl parameterizedType = ParameterizedTypeImpl.class.cast(type);

            String parameterizedTypeTypeName = parameterizedType.getRawType().getSimpleName();

            String parametrizedTypeArguments = String.join(", ", getGenericArguments(parameterizedType));

            String genericArguments = "<" + (parametrizedTypeArguments.isEmpty() ? "?" : parametrizedTypeArguments) + ">";

            boundType = parameterizedTypeTypeName + genericArguments;
        }

        return boundType;
    }

    private List<String> getGenericArguments(ParameterizedTypeImpl parameterizedType) {
        List<String> genericArguments = new ArrayList<String>();

        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        for (Type actualTypeArgument : actualTypeArguments) {
            genericArguments.add(resolveType(actualTypeArgument));
        }

        return genericArguments;
    }
}

@@ -190,143 +190,9 @@
 ect.
-GenericDeclaration;%0D%0Aimport java.lang.reflect.ParameterizedType;%0D%0Aimport java.lang.reflect.Type;%0D%0Aimport java.lang.reflect.TypeVariable
+*
 ;%0D%0Ai
@@ -2040,32 +2040,23 @@
  String 
-parametrizedType
+generic
 Argument
@@ -2058,16 +2058,22 @@
 uments =
+ %22%3C%22 +
  String.
@@ -2126,128 +2126,8 @@
 pe))
-;%0D%0A%0D%0A            String genericArguments = %22%3C%22 + (parametrizedTypeArguments.isEmpty() ? %22?%22 : parametrizedTypeArguments)
  + %22
@@ -2551,24 +2551,458 @@
 guments) %7B%0D%0A
+            try %7B%0D%0A                WildcardType wildcardType = WildcardType.class.cast(actualTypeArgument);%0D%0A                String wildcard = %22?%22;%0D%0A                wildcard += getWildCardsBound(wildcardType.getUpperBounds(), %22extends%22);%0D%0A                wildcard += getWildCardsBound(wildcardType.getLowerBounds(), %22super%22);%0D%0A                genericArguments.add(wildcard);%0D%0A            %7D catch (ClassCastException exception) %7B%0D%0A    
             
@@ -3053,24 +3053,39 @@
 rgument));%0D%0A
+            %7D%0D%0A
         %7D%0D%0A%0D
@@ -3125,12 +3125,452 @@
 %0D%0A    %7D%0D
+%0A%0D%0A    public String getWildCardsBound(Type%5B%5D types, String boundCase) %7B%0D%0A        String wildcard = %22%22;%0D%0A        if (types.length != 0) %7B%0D%0A            wildcard = %22 %22 + boundCase + %22 %22;%0D%0A            List%3CString%3E bounds = new ArrayList%3C%3E();%0D%0A            for (Type bound : types) %7B%0D%0A                bounds.add(resolveType(bound));%0D%0A            %7D%0D%0A            wildcard += String.join(%22 & %22, bounds);%0D%0A        %7D%0D%0A        return wildcard;%0D%0A    %7D%0D
 %0A%7D%0D%0A

add read wildcards