/*-
 * ~~~~~~licensing~~~~~~
 * core-support-design
 * ==========
 * Copyright (C) 2020 - 2025 EmDev LLC
 * ==========
 * You may not use this file except in accordance with the License Terms of the Copyright
 * Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
 * rights to the Software and any copies are the property of the Copyright Holder. Unless
 * it is explicitly allowed the Copyright Holder, the User is prohibited from using the
 * Software for commercial purposes to provide services to third parties.
 * 
 * The Copyright Holder hereby declares that the Software is provided on an "AS IS".
 * Under no circumstances does the Copyright Holder guarantee or promise that the
 * Software provided by him will be suitable or not suitable for the specific purposes
 * of the User, that the Software will meet all commercial and personal subjective
 * expectations of the User, that the Software will work properly, without technical
 * errors, quickly and uninterruptedly.
 * 
 * Under no circumstances shall the Copyright Holder or its Affiliates is not liable
 * to the User for any direct or indirect losses of the User, his expenses or actual
 * damage, including, downtime; loss of bussines; lost profit; lost earnings; loss
 * or damage to data, property, etc.
 * ~~~~~~/licensing~~~~~~
 */
package ru.entaxy.platform.core.support.design.openapi.utils;

import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class INameEnvironmentImpl implements INameEnvironment {
    private static final Logger log = LoggerFactory.getLogger(INameEnvironmentImpl.class);
    private final ClassLoader classLoader;

    public INameEnvironmentImpl(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @Override
    public NameEnvironmentAnswer findType(char[][] compoundTypeName) {
        StringBuilder result = new StringBuilder();
        for(int i = 0; i < compoundTypeName.length; i++) {
            if (i > 0) {
                result.append(".");
            }
            result.append(compoundTypeName[i]);
        }
        String typeName = result.toString();
        log.debug("Searching for any package type {}", typeName);
        return findType(typeName);
    }

    @Override
    public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) {
        StringBuilder buffer = new StringBuilder();
        int i=0;
        for (; i < packageName.length; i++) {
            if (i > 0) {
                buffer.append('.');
            }
            buffer.append(packageName[i]);
        }
        if (i > 0) {
            buffer.append('.');
        }
        buffer.append(typeName);
        String result = buffer.toString();
        log.debug("Searching for type {} in package {}", new String(typeName), result);
        return findType(result);
    }

    private NameEnvironmentAnswer findType(String className) {
        String resourceName = className.replace('.', '/') + ".class";

        try (InputStream is = classLoader.getResourceAsStream(resourceName)) {
            if (is != null) {
                byte[] classBytes;
                byte[] buf = new byte[8192];
                ByteArrayOutputStream baos = new ByteArrayOutputStream(buf.length);
                int count;
                while ((count = is.read(buf, 0, buf.length)) > 0) {
                    baos.write(buf, 0, count);
                }
                baos.flush();
                classBytes = baos.toByteArray();
                char[] fileName = className.toCharArray();
                ClassFileReader classFileReader = new ClassFileReader(classBytes, fileName, true);
                log.debug("Found type {}", className);
                return new NameEnvironmentAnswer(classFileReader, null);
            } else {
                log.error("Class {} not found ", className);
            }
        } catch (IOException | ClassFormatException e) {
            log.error("Failed to get class {}", className, e);
        }
        return null;
    }

    private boolean isPackage(String resource) {
        boolean result = false;
        String resourceName = resource.replace('.', '/') + ".class";
        try (InputStream is = classLoader.getResourceAsStream(resourceName)) {
            result = is == null;
        } catch (IOException e) {
            result = false;
        }
        log.debug("isPackage({}) result: {}", resource, result);
        return result;
    }

    @Override
    public boolean isPackage(char[][] parentPackageName, char[] packageName) {
        StringBuilder result = new StringBuilder();
        //fast hack: package are seldom start from uppercase
        String lp = new String(packageName);
        if (lp.matches("^[A-Z].*?") || lp.contains("$")) {
            log.debug("Skipping package check for resource {}", packageName);
            return false;
        }
        int i = 0;
        if (parentPackageName != null) {
            for (; i < parentPackageName.length; i++) {
                if (i > 0) {
                    result.append('.');
                }
                result.append(parentPackageName[i]);
            }
        }

        if (Character.isUpperCase(packageName[0])) {
            if (!isPackage(result.toString())) {
                return false;
            }
        }
        if (i > 0) {
            result.append('.');
        }
        result.append(packageName);

        return isPackage(result.toString());
    }

    @Override
    public void cleanup() {

    }
}
