/*******************************************************************************
 * Copyright (c) 2000, 2009 QNX Software Systems and others.
 *
 * 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:
 *     QNX Software Systems - Initial API and implementation
 *     Ericsson  - Modified for DSF Reference Implementation
 *******************************************************************************/

package org.eclipse.cdt.dsf.mi.service.command.output;

import java.util.ArrayList;
import java.util.List;

/**
 * The parsed output of the data-disassemble command. The output format is
 * determined by the mode field on the request.
 *
 * -data-disassemble -s $pc -e "$pc + 20" -- 0
 * ^done,asm_insns=[
 * {address="0x000107c0",func-name="main",offset="4",inst="mov 2, %o0"},
 * {address="0x000107c4",func-name="main",offset="8",inst="sethi %hi(0x11800), %o2"},
 * {address="0x000107c8",func-name="main",offset="12",inst="or %o2, 0x140, %o1\t! 0x11940 <_lib_version+8>"},
 * {address="0x000107cc",func-name="main",offset="16",inst="sethi %hi(0x11800), %o2"},
 * {address="0x000107d0",func-name="main",offset="20",inst="or %o2, 0x168, %o4\t! 0x11968 <_lib_version+48>"}]
 *
 * -data-disassemble -f basics.c -l 32 -- 0
 * ^done,asm_insns=[
 * {address="0x000107bc",func-name="main",offset="0",inst="save %sp, -112, %sp"},
 * {address="0x000107c0",func-name="main",offset="4",inst="mov   2, %o0"},
 * {address="0x000107c4",func-name="main",offset="8",inst="sethi %hi(0x11800), %o2"},
 * [...]
 * {address="0x0001081c",func-name="main",offset="96",inst="ret "},
 * {address="0x00010820",func-name="main",offset="100",inst="restore "}]
 *
 * -data-disassemble -f basics.c -l 32 -n 3 -- 1
 * ^done,asm_insns=[
 * src_and_asm_line={line="31",file="/dir1/dir2/basics.c",line_asm_insn=[
 * {address="0x000107bc",func-name="main",offset="0",inst="save %sp, -112, %sp"}]},
 * src_and_asm_line={line="32",file="/dir1/dir2/basics.c",line_asm_insn=[
 * {address="0x000107c0",func-name="main",offset="4",inst="mov 2, %o0"},
 * {address="0x000107c4",func-name="main",offset="8",inst="sethi %hi(0x11800), %o2"}]}]
 *
 */

public class MIDataDisassembleInfo extends MIInfo {

	// The parsed information
	private boolean mixed;
	private MIMixedInstruction[] mixedCode;
	private MIInstruction[] assemblyCode;

	public MIDataDisassembleInfo(MIOutput record) {
		super(record);
		mixed = false;
		parse();
	}

	public boolean isMixed() {
		return mixed;
	}

	public MIInstruction[] getMIAssemblyCode() {
		return assemblyCode;
	}

	public MIMixedInstruction[] getMIMixedCode() {
		return mixedCode;
	}

	/**
	 *  Find the relevant tag in the output record ("asm_insns") and then
	 *  parse its value.
	 */
	private void parse() {
		List<MIInstruction> asmList = new ArrayList<>();
		List<MIMixedInstruction> srcList = new ArrayList<>();

		if (isDone()) {
			MIOutput out = getMIOutput();
			MIResultRecord rr = out.getMIResultRecord();
			if (rr != null) {
				MIResult[] results = rr.getMIResults();
				// Technically, there should be only one field (asm_insns), but just in case...
				for (int i = 0; i < results.length; i++) {
					String var = results[i].getVariable();
					if (var.equals("asm_insns")) { //$NON-NLS-1$
						MIValue value = results[i].getMIValue();
						if (value instanceof MIList) {
							parseResult((MIList) value, srcList, asmList);
						}
					}
				}
			}
		}

		assemblyCode = asmList.toArray(new MIInstruction[asmList.size()]);
		mixedCode = srcList.toArray(new MIMixedInstruction[srcList.size()]);
	}

	/**
	 *  Parse the back-end result. Depending on the requested mode
	 *  ("-- 0" or "-- 1" on the request), the result has one of the
	 *  following forms:
	 *
	 *  [1] Mode == 0 (assembly instructions only)
	 *      {address="0x000107c0",func-name="main",offset="4",inst="mov 2, %o0"},
	 *      {address="0x000107c4",func-name="main",offset="8",inst="sethi %hi(0x11800), %o2"},
	 *      ...,
	 *      {address="0x00010820",func-name="main",offset="100",inst="restore "}
	 *
	 *  [2] Mode == 1 (Mixed source and assembly code)
	 *      src_and_asm_line={
	 *          line="31",file="/dir1/dir2/basics.c",
	 *          line_asm_insn=[
	 *          {address="0x000107c0",func-name="main",offset="4",inst="mov 2, %o0"},
	 *          {address="0x000107c4",func-name="main",offset="8",inst="sethi %hi(0x11800), %o2"},
	 *          ...,
	 *          {address="0x00010820",func-name="main",offset="100",inst="restore "}
	 *          ]
	 *      },
	 *      ...,
	 *      src_and_asm_line={
	 *          line="31",file="/dir1/dir2/basics.c",
	 *          line_asm_insn=[
	 *          ...,
	 *          ]
	 *      }
	 */
	private void parseResult(MIList list, List<MIMixedInstruction> srcList, List<MIInstruction> asmList) {

		// Mixed mode (with source)
		MIResult[] results = list.getMIResults();
		if (results != null && results.length > 0) {
			for (int i = 0; i < results.length; i++) {
				String var = results[i].getVariable();
				if (var.equals("src_and_asm_line")) { //$NON-NLS-1$
					MIValue value = results[i].getMIValue();
					if (value instanceof MITuple) {
						srcList.add(new MIMixedInstruction((MITuple) value));
					}
				}
			}
			mixed = true;
		}

		// Non-mixed mode
		MIValue[] values = list.getMIValues();
		if (values != null && values.length > 0) {
			for (int i = 0; i < values.length; i++) {
				if (values[i] instanceof MITuple) {
					asmList.add(new MIInstruction((MITuple) values[i]));
				}
			}
			mixed = false;
		}

	}

}
