/*******************************************************************************
 * Copyright (c) 2017 IBM Corporation.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.aspectj.org.eclipse.jdt.internal.core.builder;

import java.io.IOException;
import java.util.Enumeration;
import java.util.function.Predicate;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.eclipse.core.runtime.IPath;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationDecorator;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.AccessRuleSet;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.IModule;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
import org.aspectj.org.eclipse.jdt.internal.compiler.util.SimpleSet;
import org.aspectj.org.eclipse.jdt.internal.compiler.util.SuffixConstants;

public class ClasspathJMod extends ClasspathJar {

	public static char[] CLASSES = "classes".toCharArray(); //$NON-NLS-1$
	public static char[] CLASSES_FOLDER = "classes/".toCharArray(); //$NON-NLS-1$
	private static int MODULE_DESCRIPTOR_NAME_LENGTH = IModule.MODULE_INFO_CLASS.length();

	ClasspathJMod(String zipFilename, long lastModified, AccessRuleSet accessRuleSet, IPath externalAnnotationPath) {
		super(zipFilename, lastModified, accessRuleSet, externalAnnotationPath, true);
	}
	@Override
	IModule initializeModule() {
		IModule mod = null;
		ZipFile file = null;
		try {
			file = new ZipFile(this.zipFilename);
			String fileName = new String(CLASSES_FOLDER) + IModule.MODULE_INFO_CLASS;
			ClassFileReader classfile = ClassFileReader.read(file, fileName);
			if (classfile != null) {
				mod = classfile.getModuleDeclaration();
			}
		} catch (ClassFormatException | IOException e) {
			// do nothing
		} finally {
			try {
				if (file != null)
					file.close();
			} catch (IOException e) {
				// do nothing
			}
		}
		return mod;
	}

	@Override
	public NameEnvironmentAnswer findClass(String binaryFileName, String qualifiedPackageName, String moduleName, String qualifiedBinaryFileName,
											boolean asBinaryOnly, Predicate<String> moduleNameFilter) {
		if (!isPackage(qualifiedPackageName, moduleName)) return null; // most common case
		if (moduleNameFilter != null && this.module != null && !moduleNameFilter.test(String.valueOf(this.module.name())))
			return null;

		try {
			qualifiedBinaryFileName = new String(CharOperation.append(CLASSES_FOLDER, qualifiedBinaryFileName.toCharArray()));
			IBinaryType reader = ClassFileReader.read(this.zipFile, qualifiedBinaryFileName);
			if (reader != null) {
				char[] modName = this.module == null ? null : this.module.name();
				if (reader instanceof ClassFileReader) {
					ClassFileReader classReader = (ClassFileReader) reader;
					if (classReader.moduleName == null)
						classReader.moduleName = modName;
					else
						modName = classReader.moduleName;
				}
				String fileNameWithoutExtension = qualifiedBinaryFileName.substring(0, qualifiedBinaryFileName.length() - SuffixConstants.SUFFIX_CLASS.length);
				if (this.externalAnnotationPath != null) {
					try {
						if (this.annotationZipFile == null) {
							this.annotationZipFile = ExternalAnnotationDecorator
									.getAnnotationZipFile(this.externalAnnotationPath, null);
						}

						reader = ExternalAnnotationDecorator.create(reader, this.externalAnnotationPath,
								fileNameWithoutExtension, this.annotationZipFile);
					} catch (IOException e) {
						// don't let error on annotations fail class reading
					}
				}
				if (this.accessRuleSet == null)
					return new NameEnvironmentAnswer(reader, null, modName);
				return new NameEnvironmentAnswer(reader, this.accessRuleSet.getViolatedRestriction(fileNameWithoutExtension.toCharArray()), modName);
			}
		} catch (IOException e) { // treat as if class file is missing
		} catch (ClassFormatException e) { // treat as if class file is missing
		}
		return null;
	}
	@Override
	protected String readJarContent(final SimpleSet packageSet) {
		String modInfo = null;
		for (Enumeration<? extends ZipEntry> e = this.zipFile.entries(); e.hasMoreElements(); ) {
			ZipEntry entry = e.nextElement();
			char[] entryName = entry.getName().toCharArray();
			int index = CharOperation.indexOf('/', entryName);
			if (index != -1) {
				char[] folder = CharOperation.subarray(entryName, 0, index);
				if (CharOperation.equals(CLASSES, folder)) {
					char[] fileName = CharOperation.subarray(entryName, index + 1, entryName.length);
					if (modInfo == null && fileName.length == MODULE_DESCRIPTOR_NAME_LENGTH) {
						if (CharOperation.equals(fileName, IModule.MODULE_INFO_CLASS.toCharArray())) {
							modInfo = new String(entryName);
						}
					}
					addToPackageSet(packageSet, new String(fileName), false);
				}
			}
		}
		return modInfo;
	}
}
