/*
 * Copyright 2021 the original author or 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 org.gradle.internal.classpath;

import org.gradle.internal.Pair;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;

import java.util.List;

import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toList;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
import static org.objectweb.asm.Opcodes.ASM7;

class InstrumentingBackwardsCompatibilityVisitor extends ClassVisitor {

    private static final List<Pair<String, String>> RENAMED_INTERFACES = asList(
        Pair.of("org/gradle/logging/LoggingManagerInternal", "org/gradle/api/logging/LoggingManager"),
        Pair.of("org/gradle/logging/StandardOutputCapture", "org/gradle/internal/logging/StandardOutputCapture")
    );

    private static final List<Pair<String, String>> RENAMED_INTERFACE_DESCRIPTORS = RENAMED_INTERFACES.stream().map(
        p -> Pair.of("L" + p.left + ";", "L" + p.right + ";")
    ).collect(toList());

    InstrumentingBackwardsCompatibilityVisitor(ClassVisitor classVisitor) {
        super(ASM7, classVisitor);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        String newDescriptor = fixSyntheticBridgeMethodDescriptor(access, descriptor);
        MethodVisitor methodVisitor = super.visitMethod(access, name, newDescriptor, signature, exceptions);
        return methodVisitor != null
            ? new BackwardCompatibilityMethodVisitor(methodVisitor)
            : null;
    }

    private static class BackwardCompatibilityMethodVisitor extends MethodVisitor {

        public BackwardCompatibilityMethodVisitor(MethodVisitor methodVisitor) {
            super(ASM7, methodVisitor);
        }

        @Override
        public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
            final String newOwner = fixMethodOwnerForBackwardCompatibility(owner);
            final String newDescriptor = fixMethodDescriptorForBackwardCompatibility(descriptor);
            super.visitMethodInsn(opcode, newOwner, name, newDescriptor, isInterface);
        }
    }

    private String fixSyntheticBridgeMethodDescriptor(int access, String descriptor) {
        // Restore compatibility with plugins compiled with an old Groovy version (like org.samples.greeting:1.0 used by the GE build)
        // in which super class getters are accessed via bridge methods.
        return (access & ACC_SYNTHETIC) != 0 && descriptor.startsWith("()")
            ? fixMethodDescriptorForBackwardCompatibility(descriptor)
            : descriptor;
    }

    private static String fixMethodOwnerForBackwardCompatibility(String typeName) {
        // Fix renamed type references
        for (Pair<String, String> renamedInterface : RENAMED_INTERFACES) {
            if (renamedInterface.left.equals(typeName)) {
                return renamedInterface.right;
            }
        }
        return typeName;
    }

    private static String fixMethodDescriptorForBackwardCompatibility(String descriptor) {
        // Fix method signatures involving renamed types
        for (Pair<String, String> renamedDescriptor : RENAMED_INTERFACE_DESCRIPTORS) {
            descriptor = descriptor.replace(renamedDescriptor.left, renamedDescriptor.right);
        }
        return descriptor;
    }
}

@@ -1111,24 +1111,33 @@
 RENAMED_
-INTERFAC
+TYPE_INTERNAL_NAM
 ES = asL
@@ -1421,32 +1421,27 @@
 g%3E%3E RENAMED_
-INTERFAC
+TYP
 E_DESCRIPTOR
@@ -1452,24 +1452,33 @@
 RENAMED_
-INTERFAC
+TYPE_INTERNAL_NAM
 ES.strea
@@ -2543,35 +2543,36 @@
 wOwner = fix
-MethodOwner
+InternalName
 ForBackwardC
@@ -2632,30 +2632,24 @@
 riptor = fix
-Method
 DescriptorFo
@@ -3172,30 +3172,24 @@
        ? fix
-Method
 DescriptorFo
@@ -3289,19 +3289,20 @@
  fix
-MethodOwner
+InternalName
 ForB
@@ -3329,20 +3329,24 @@
 (String 
-type
+internal
 Name) %7B%0A
@@ -3445,24 +3445,33 @@
 RENAMED_
-INTERFAC
+TYPE_INTERNAL_NAM
 ES) %7B%0A  
@@ -3513,20 +3513,24 @@
 .equals(
-type
+internal
 Name)) %7B
@@ -3616,20 +3616,24 @@
  return 
-type
+internal
 Name;%0A  
@@ -3666,22 +3666,16 @@
 ring fix
-Method
 Descript
@@ -3845,16 +3845,11 @@
 MED_
-INTERFAC
+TYP
 E_DE

Rename methods and constants for clarity