/* ###
 * 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.
 */
/* Generated by Together */

package ghidra.program.model.listing;

import java.util.ConcurrentModificationException;

import ghidra.program.model.address.Address;
import ghidra.program.model.lang.InstructionPrototype;
import ghidra.program.model.lang.Register;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.PropertySet;

/**
 * Interface common to both instructions and data.
 */
public interface CodeUnit extends MemBuffer, PropertySet {

	/**
	 * Indicator for a mnemonic (versus an operand).
	 */
	public final static int MNEMONIC = -1;

	/**
	 * comment type for end of line
	 * @deprecated use {@link CommentType#EOL} 
	 */
	@Deprecated(forRemoval = true, since = "11.4")
	public static final int EOL_COMMENT = 0; // CommentType.EOL.ordinal();
	/**
	 * comment type that goes before a code unit
	 * @deprecated use {@link CommentType#PRE}
	 */
	@Deprecated(forRemoval = true, since = "11.4")
	public static final int PRE_COMMENT = 1; // CommentType.PRE.ordinal();
	/**
	 * comment type that follows after a code unit
	 * @deprecated use {@link CommentType#POST} 
	 */
	@Deprecated(forRemoval = true, since = "11.4")
	public static final int POST_COMMENT = 2; // CommentType.POST.ordinal();
	/**
	 * Property name for plate comment type
	 * @deprecated use {@link CommentType#POST} 
	 */
	@Deprecated(forRemoval = true, since = "11.4")
	public static final int PLATE_COMMENT = 3; // CommentType.PLATE.ordinal();
	/**
	 * Property name for repeatable comment type
	 * @deprecated use {@link CommentType#REPEATABLE} 
	 */
	@Deprecated(forRemoval = true, since = "11.4")
	public static final int REPEATABLE_COMMENT = 4; // CommentType.REPEATABLE.ordinal();

	/**
	 * Any comment property.
	 */
	public static final String COMMENT_PROPERTY = "COMMENT__GHIDRA_";
	/** 
	 * Property name for vertical space formatting
	 */
	public static final String SPACE_PROPERTY = "Space";
	/**
	 * Property name for code units that are instructions
	 */
	public static final String INSTRUCTION_PROPERTY = "INSTRUCTION__GHIDRA_";
	/**
	 * Property name for code units that are defined data
	 */
	public static final String DEFINED_DATA_PROPERTY = "DEFINED_DATA__GHIDRA_";

	/**
	 * Get the string representation of the starting address for
	 * this code unit.
	 * @param showBlockName true if the string should include the memory block name
	 * @param pad if true, the address will be padded with leading zeros.  Even if pad is
	 * false, the string will be padded to make the address string contain at least 4 digits.
	 * @return string representation of address
	 */
	public String getAddressString(boolean showBlockName, boolean pad);

	/**
	 * {@return the label for this code unit.}
	 */
	public String getLabel();

	/**
	 * {@return the Symbols for this code unit.}
	 * @throws ConcurrentModificationException if this object is no
	 * longer valid.
	 */
	public Symbol[] getSymbols();

	/**
	 * {@return the Primary Symbol for this code unit.}
	 * @throws ConcurrentModificationException if this object is no
	 * longer valid.
	 */
	public Symbol getPrimarySymbol();

	/**
	 * {@return the starting address for this code unit.}
	 */
	public Address getMinAddress();

	/**
	 * {@return the ending address for this code unit.}
	 */
	public Address getMaxAddress();

	/**
	 * {@return the mnemonic for this code unit, e.g., MOV, JMP}
	 */
	public String getMnemonicString();

	/**
	 * Get the comment for the given type
	 *
	 * @param commentType either EOL_COMMENT, PRE_COMMENT, 
	 * POST_COMMENT, or REPEATABLE_COMMENT
	 * @return the comment string of the appropriate type or null if no comment of
	 * that type exists for this code unit
	 * @throws IllegalArgumentException if type is not one of the
	 * three types of comments supported
	 * @deprecated use {@link #getComment(CommentType)} instead
	 */
	@Deprecated(forRemoval = true, since = "11.4")
	public default String getComment(int commentType) {
		return getComment(CommentType.valueOf(commentType));
	}

	/**
	 * Get the comment for the given type
	 *
	 * @param type {@link CommentType comment type}
	 * @return the comment string of the appropriate type or null if no comment of
	 * that type exists for this code unit
	 */
	public String getComment(CommentType type);

	/**
	 * Get the comment for the given type and parse it into an array of strings
	 * such that each line is its own string.
	 *
	 * @param commentType either EOL_COMMENT, PRE_COMMENT, 
	 * POST_COMMENT, or REPEATABLE_COMMENT
	 * @return an array of strings where each item in the array is a line of text
	 * in the comment.  If there is no comment of the requested type, an empty array
	 * is returned.
	 * @throws IllegalArgumentException if type is not one of the
	 * three types of comments supported
	 * @deprecated use {@link #getCommentAsArray(CommentType)} instead
	 */
	@Deprecated(forRemoval = true, since = "11.4")
	public default String[] getCommentAsArray(int commentType) {
		return getCommentAsArray(CommentType.valueOf(commentType));
	}

	/**
	 * Get the comment for the given type and parse it into an array of strings
	 * such that each line is its own string.
	 *
	 * @param type {@link CommentType comment type}
	 * @return an array of strings where each item in the array is a line of text
	 * in the comment.  If there is no comment of the requested type, an empty array
	 * is returned.
	 */
	public String[] getCommentAsArray(CommentType type);

	/**
	 * Set the comment for the given comment type.  Passing <code>null</code> clears the comment
	 *
	 * @param commentType either EOL_COMMENT, PRE_COMMENT, 
	 * POST_COMMENT, or REPEATABLE_COMMENT
	 * @param comment comment for code unit; null clears the comment
	 * 
	 * @throws IllegalArgumentException if type is not one of the
	 * three types of comments supported
	 * @deprecated use {@link #setComment(CommentType, String)} instead
	 */
	@Deprecated(forRemoval = true, since = "11.4")
	public default void setComment(int commentType, String comment) {
		setComment(CommentType.valueOf(commentType), comment);
	}

	/**
	 * Set the comment for the given comment type.  Passing <code>null</code> clears the comment
	 *
	 * @param type {@link CommentType comment type}
	 * @param comment comment for code unit; null clears the comment
	 */
	public void setComment(CommentType type, String comment);

	/**
	 * Set the comment (with each line in its own string) for the given comment type
	 *
	 * @param commentType either EOL_COMMENT, PRE_COMMENT, 
	 * POST_COMMENT, or REPEATABLE_COMMENT
	 * @param comment an array of strings where each string is a single line of the comment.
	 * @throws IllegalArgumentException if type is not one of the
	 * three types of comments supported
	 * @deprecated use {@link #setCommentAsArray(CommentType, String[])} instead
	 */
	@Deprecated(forRemoval = true, since = "11.4")
	public default void setCommentAsArray(int commentType, String[] comment) {
		setCommentAsArray(CommentType.valueOf(commentType), comment);
	}

	/**
	 * Set the comment (with each line in its own string) for the given comment type
	 *
	 * @param type {@link CommentType comment type}
	 * @param comment an array of strings where each string is a single line of the comment.
	 * @throws IllegalArgumentException if type is not one of the
	 * three types of comments supported
	 */
	public void setCommentAsArray(CommentType type, String[] comment);

	/**
	 * Get length of this code unit.  
	 * NOTE: If an {@link Instruction#isLengthOverridden() instruction length-override} is
	 * set this method will return the reduced length.
	 * @return code unit length
	 */
	public int getLength();

	/**
	 * Get the bytes that make up this code unit.
	 * NOTE: If an {@link Instruction#isLengthOverridden() instruction length-override} is
	 * set this method will not return all bytes associated with the 
	 * {@link InstructionPrototype instruction prototype}.
	 * @return an array of bytes that are in memory at the codeunits address.  The
	 * array length is the same as the codeUnits length
	 * @throws MemoryAccessException if the full number of bytes could not be read.
	 */
	public byte[] getBytes() throws MemoryAccessException;

	/**
	 * Copies max(buffer.length, code unit length) bytes into buffer starting at the given offset in 
	 * {@code buffer}.
	 * @param buffer byte array to copy into
	 * @param bufferOffset offset in byte array the copy will start
	 * @throws MemoryAccessException if the full number of bytes could not be read.
	 */
	public void getBytesInCodeUnit(byte[] buffer, int bufferOffset) throws MemoryAccessException;

	/**
	 * {@return true if address is contained in the range of this codeUnit}
	 * @param testAddr the address to test.
	 */
	public boolean contains(Address testAddr);

	/**
	   * Compares the given address to the address range of this node.
	   *
	   * @param addr address to compare.
	   * @return a negative integer if this addr is greater than the maximum range address
	   *         zero if addr is in the range
	   *         a positive integer if addr is less than minimum range address
	   */
	public int compareTo(Address addr);

	/**
	 * Add a reference to the mnemonic for this code unit.
	 * @param refAddr address to add as a reference.
	 * @param refType the type of reference to add.
	 * @param sourceType the source of this reference
	*/
	public void addMnemonicReference(Address refAddr, RefType refType, SourceType sourceType);

	/**
	 * Remove a reference to the mnemonic for this code unit.
	 * @param refAddr the address to remove as a reference.
	 */
	public void removeMnemonicReference(Address refAddr);

	/**
	* Get references for the mnemonic for this code unit.
	* @return an array of memory references. A zero length array will be 
	* returned if there are no references for the mnemonic.
	*/
	public Reference[] getMnemonicReferences();

	/**
	 * {@return the references for the operand index.}
	 * @param index operand index (0 is the first operand)
	 */
	public Reference[] getOperandReferences(int index);

	/**
	 * {@return the primary reference for the operand index.}
	 * @param index operand index (0 is the first operand)
	 */
	public Reference getPrimaryReference(int index);

	/**
	 * Add a memory reference to the operand at the given index.
	 * @param index operand index
	 * @param refAddr reference address
	 * @param type the reference type to be added.
	 * @param sourceType the source of this reference
	 */
	public void addOperandReference(int index, Address refAddr, RefType type,
			SourceType sourceType);

	/**
	 * Remove a reference to the operand.
	 * @param index operand index
	 * @param refAddr address referencing the operand
	 */
	public void removeOperandReference(int index, Address refAddr);

	/**
	 * Get ALL memory references FROM this code unit.
	 * @return an array of memory references from this codeUnit or an empty array
	 * if there are no references.
	 */
	public Reference[] getReferencesFrom();

	/**
	 * {@return an iterator over all references TO this code unit.}
	 */
	public ReferenceIterator getReferenceIteratorTo();

	/**
	 * {@return the program that generated this CodeUnit.}
	 */
	public Program getProgram();

	/**
	 * Gets the external reference (if any) at the opIndex
	 * @param opIndex the operand index to look for external references
	 * @return the external reference at the operand or null if none exists.
	 */
	public ExternalReference getExternalReference(int opIndex);

	/**
	 * Remove external reference (if any) at the given opIndex
	 * @param opIndex the index of the operand from which to remove any external reference.
	 */
	public void removeExternalReference(int opIndex);

	/**
	 * Sets a memory reference to be the primary reference at its
	 * address/opIndex location. The primary reference is the one that
	 * is used in the getOperandRepresentation() method.
	 * @param ref the reference to be set as primary.
	 */
	public void setPrimaryMemoryReference(Reference ref);

	/**
	 * Sets a stack reference at the <code>offset</code> on the
	 * specified operand index, which effectively substitutes the previous
	 * operation interpretation
	 * <br>
	 * <i>NOTE: If another reference was previously set on the
	 * operand, then it will be replaced with this stack
	 * reference</i>
	 *
	 * @param opIndex      the index of the operand to set this stack reference
	 * @param offset       the (+/-) offset from stack base address
	 * @param sourceType   the source of this reference
	 * @param refType      type of reference, RefType.READ,WRITE,PTR...
	 */
	public void setStackReference(int opIndex, int offset, SourceType sourceType, RefType refType);

	/**
	 * Sets a register reference at the <code>offset</code> on the
	 * specified operand index, which effectively substitutes the previous
	 * operation interpretation
	 * <br>
	 * <i>NOTE: If another reference was previously set on the
	 * operand, then it will be replaced with this register
	 * reference</i>
	 *
	 * @param opIndex      the index of the operand to set this register reference
	 * @param reg		   a register
	 * @param sourceType   the source of this reference
	 * @param refType      type of reference, RefType.READ,WRITE,PTR...
	 */
	public void setRegisterReference(int opIndex, Register reg, SourceType sourceType,
			RefType refType);

	/**
	 * {@return the number of operands for this code unit.}
	 */
	public int getNumOperands();

	/**
	 * Get the Address for the given operand index if one exists.  Data
	 * objects have one operand (the value).
	 * @param opIndex index of the operand.
	 * @return An address if the operand represents a fully qualified
	 * address (given the context), or if the operand is a Scalar treated
	 * as an address. Null is returned if no address or scalar exists on that 
	 * operand.
	 */
	public Address getAddress(int opIndex);

	/**
	 * Returns the scalar at the given operand index.  Data objects have
	 * one operand (the value).
	 * @param opIndex index of the operand.
	 * @return the scalar at the given operand index or null if no
	 * scalar exists at that index.
	 */
	public Scalar getScalar(int opIndex);

}
