/* ###
 * IP: GHIDRA
 *
 * 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 ghidra.app.util.bin.format.pe.debug;

import java.io.IOException;
import java.util.ArrayList;

import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.pe.OffsetValidator;

/**
 * A helper class to parsing different types of 
 * debug information from a debug directory
 */
public class DebugDirectoryParser implements OffsetValidator {

	/**
	 * Unknown debug type.
	 */
	public final static byte IMAGE_DEBUG_TYPE_UNKNOWN = 0;
	/**
	 * COFF debug type.
	 */
	public final static byte IMAGE_DEBUG_TYPE_COFF = 1;
	/**
	 * CodeView debug type.
	 */
	public final static byte IMAGE_DEBUG_TYPE_CODEVIEW = 2;
	/**
	 * FPO debug type.
	 */
	public final static byte IMAGE_DEBUG_TYPE_FPO = 3;
	/**
	 * Misc debug type.
	 */
	public final static byte IMAGE_DEBUG_TYPE_MISC = 4;
	/**
	 * Exception debug type.
	 */
	public final static byte IMAGE_DEBUG_TYPE_EXCEPTION = 5;
	/**
	 * Fixup debug type.
	 */
	public final static byte IMAGE_DEBUG_TYPE_FIXUP = 6;
	/**
	 * OMAP-To-Source debug type.
	 */
	public final static byte IMAGE_DEBUG_TYPE_OMAP_TO_SRC = 7;
	/**
	 * OMAP-From-Source debug type.
	 */
	public final static byte IMAGE_DEBUG_TYPE_OMAP_FROM_SRC = 8;
	/**
	 * Borland debug type.
	 */
	public final static byte IMAGE_DEBUG_TYPE_BORLAND = 9;
	/**
	 * Reserved debug type.
	 */
	public final static byte IMAGE_DEBUG_TYPE_RESERVED10 = 10;
	/**
	 * CLS ID debug type.
	 */
	public final static byte IMAGE_DEBUG_TYPE_CLSID = 11;

	private ArrayList<DebugDirectory> debugFormatList = new ArrayList<DebugDirectory>();
	private DebugMisc miscDebug;
	private DebugCodeView codeViewDebug;
	private DebugCOFFSymbolsHeader coffDebug;
	private DebugFixup fixupDebug;
	private BinaryReader reader;
	private long sizeOfImage;

	/**
	 * Constructs a new debug directory parser.
	 * @param reader the binary reader
	 * @param ptr the pointer into the binary reader
	 * @param size the size of the directory
	 * @param sizeOfImage the size of the image in memory
	 * @throws IOException if an I/O error occurs
	 */
	public DebugDirectoryParser(BinaryReader reader, long ptr, int size, long sizeOfImage)
			throws IOException {
		this.reader = reader;
		this.sizeOfImage = sizeOfImage;
		int debugFormatsCount = size / DebugDirectory.IMAGE_SIZEOF_DEBUG_DIRECTORY;

		for (int i = 0; i < debugFormatsCount; ++i) {
			DebugDirectory debugDir = new DebugDirectory(reader, ptr, this);
			if (debugDir.getSizeOfData() == 0)
				break;

			ptr += DebugDirectory.IMAGE_SIZEOF_DEBUG_DIRECTORY;

			switch (debugDir.getType()) {
				case IMAGE_DEBUG_TYPE_CLSID:
					debugDir.setDescription("CLSID");
					break;
				case IMAGE_DEBUG_TYPE_RESERVED10:
					debugDir.setDescription("Reserved");
					break;
				case IMAGE_DEBUG_TYPE_BORLAND:
					debugDir.setDescription("Borland");
					break;
				case IMAGE_DEBUG_TYPE_OMAP_FROM_SRC:
					debugDir.setDescription("OMAPfromSrc");
					break;
				case IMAGE_DEBUG_TYPE_OMAP_TO_SRC:
					debugDir.setDescription("OMAPtoSrc");
					break;
				case IMAGE_DEBUG_TYPE_FIXUP:
					debugDir.setDescription("Fixup");
					fixupDebug = new DebugFixup(reader, debugDir, this);
					break;
				case IMAGE_DEBUG_TYPE_EXCEPTION:
					debugDir.setDescription("Exception");
					break;
				case IMAGE_DEBUG_TYPE_MISC:
					debugDir.setDescription("Misc");
					miscDebug = new DebugMisc(reader, debugDir, this);
					break;
				case IMAGE_DEBUG_TYPE_FPO:
					debugDir.setDescription("FPO");
					break;
				case IMAGE_DEBUG_TYPE_CODEVIEW:
					debugDir.setDescription("CodeView");
					codeViewDebug = new DebugCodeView(reader, debugDir, this);
					break;
				case IMAGE_DEBUG_TYPE_COFF:
					debugDir.setDescription("COFF");
					coffDebug =
						new DebugCOFFSymbolsHeader(reader, debugDir, this);
					break;
				case IMAGE_DEBUG_TYPE_UNKNOWN:
					debugDir.setDescription("Unknown");
					break;
				default:
					debugDir.setDescription("DebugType-" + debugDir.getType());
					break;
			}
			debugFormatList.add(debugDir);
		}
	}

	public DebugDirectory[] getDebugDirectories() {
		DebugDirectory[] ddArr = new DebugDirectory[debugFormatList.size()];
		debugFormatList.toArray(ddArr);
		return ddArr;
	}

	/**
	 * Returns the miscellaneous debug information, or null if it does not exists.
	 * @return the miscellaneous debug information
	 */
	public DebugMisc getDebugMisc() {
		return miscDebug;
	}

	/**
	 * Returns the CodeView debug information, or null if it does not exists.
	 * @return the CodeView debug information
	 */
	public DebugCodeView getDebugCodeView() {
		return codeViewDebug;
	}

	/**
	 * Returns the COFF debug information, or null if it does not exists.
	 * @return the COFF debug information
	 */
	public DebugCOFFSymbolsHeader getDebugCOFFSymbolsHeader() {
		return coffDebug;
	}

	/**
	 * Returns the Fixup debug information, or null if it does not exists.
	 * @return the Fixup debug information
	 */
	public DebugFixup getDebugFixup() {
		return fixupDebug;
	}

	@Override
	public boolean checkPointer(long ptr) {
		try {
			return ptr > 0 && ptr < reader.length();
		}
		catch (IOException e) {
			return false;
		}
	}

	@Override
	public boolean checkRVA(long rva) {
		return (0 <= rva) && (rva <= sizeOfImage);
	}
}
