/*
 * Copyright (c) 2013-2021 Chris Newland.
 * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD
 * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki
 */
package org.adoptopenjdk.jitwatch.model.assembly;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import com.chrisnewland.freelogj.Logger;
import com.chrisnewland.freelogj.LoggerFactory;

public final class AssemblyReference
{
	private static Map<String, String> mnemonicMap = new HashMap<>();

	private static final String TAG_MNEM_OPEN = "<mnem>";
	private static final String TAG_MNEM_CLOSE = "</mnem>";
	private static final String TAG_BRIEF_OPEN = "<brief>";
	private static final String TAG_BRIEF_CLOSE = "</brief>";

	private static final Logger LOGGER = LoggerFactory.getLogger(AssemblyReference.class);

	private static final String ASM_REF_PATH = "/x86reference.xml";

	static
	{
		BufferedReader bufferedReader = null;

		try
		{
			InputStream asmRefInputStream = AssemblyReference.class.getResourceAsStream(ASM_REF_PATH);

			if (asmRefInputStream == null)
			{
				LOGGER.error(
						"Could not find assembly reference {}. If launching from an IDE please add /src/main/resources to your classpath",
						ASM_REF_PATH);
			}
			else
			{
				bufferedReader = new BufferedReader(new InputStreamReader(asmRefInputStream), 65536);

				String currentLine = bufferedReader.readLine();

				Set<String> mnemonics = new HashSet<>();

				while (currentLine != null)
				{
					int openMnemPos = currentLine.indexOf(TAG_MNEM_OPEN);

					if (openMnemPos != -1)
					{
						int closeMnemPos = currentLine.indexOf(TAG_MNEM_CLOSE);

						if (closeMnemPos != -1)
						{
							String mnemonic = currentLine.substring(openMnemPos + TAG_MNEM_OPEN.length(), closeMnemPos)
									.toLowerCase();

							mnemonics.add(mnemonic);
						}
					}

					int openBriefPos = currentLine.indexOf(TAG_BRIEF_OPEN);

					if (openBriefPos != -1)
					{
						int closeBriefPos = currentLine.indexOf(TAG_BRIEF_CLOSE);

						if (closeBriefPos != -1)
						{
							String brief = currentLine.substring(openBriefPos + TAG_BRIEF_OPEN.length(), closeBriefPos);

							for (String mnemonic : mnemonics)
							{
								if (!mnemonicMap.containsKey(mnemonic))
								{
									mnemonicMap.put(mnemonic, brief);
								}
							}

							mnemonics.clear();
						}
					}

					currentLine = bufferedReader.readLine();
				}
			}
		}
		catch (IOException ioe)
		{
			LOGGER.error("Could not load assembly reference", ioe);
		}
		finally
		{
			if (bufferedReader != null)
			{
				try
				{
					bufferedReader.close();
				}
				catch (IOException ioe)
				{
					LOGGER.error("Could not close reader", ioe);
				}
			}
		}

		// patch up missing descriptions
		mnemonicMap.put("movabs", "Move a 64-bit value");
		mnemonicMap.put("ret", mnemonicMap.get("retn"));
		mnemonicMap.put("movslq", mnemonicMap.get("movsxd"));

	}

	private AssemblyReference()
	{
	}

	public static String lookupMnemonic(String mnemonic)
	{
		String result = mnemonicMap.get(mnemonic);

		if (result == null)
		{
			// check if it has a size suffix (b,w,l,q)
			if (mnemonic.endsWith("b") || mnemonic.endsWith("w") || mnemonic.endsWith("l") || mnemonic.endsWith("q"))
			{
				result = mnemonicMap.get(mnemonic.substring(0, mnemonic.length() - 1));
			}
		}

		return result;
	}
}
