/* ###
 * 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.file.formats.android.dex.format;

import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.file.formats.android.dex.util.Leb128;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.util.exception.DuplicateNameException;

import java.io.IOException;

public class DebugInfoItem implements StructConverter {

	private int lineStart;
	private int lineStartLength;// in bytes
	private int parametersSize;
	private int parametersSizeLength;// in bytes
	private int [] parameterNames;
	private int [] parameterNamesLengths;
	private byte [] stateMachineOpcodes;

	public DebugInfoItem( BinaryReader reader ) throws IOException {
		lineStart = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
		lineStartLength = Leb128.unsignedLeb128Size( lineStart );
		reader.readNextByteArray( lineStartLength );// consume leb...

		parametersSize = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
		parametersSizeLength = Leb128.unsignedLeb128Size( parametersSize );
		reader.readNextByteArray( parametersSizeLength );// consume leb...

		parameterNames = new int[ parametersSize ];
		parameterNamesLengths = new int[ parametersSize ];

		for ( int i = 0 ; i < parametersSize ; ++i ) {
			int value = Leb128.readUnsignedLeb128( reader.readByteArray( reader.getPointerIndex( ), 5 ) );
			int valueLength = Leb128.unsignedLeb128Size( value );
			reader.readNextByteArray( valueLength );// consume leb...

			parameterNames[ i ] = value - 1;// uleb128p1

			parameterNamesLengths[ i ] = valueLength;
		}

		long startIndex = reader.getPointerIndex( );
		int count = DebugInfoStateMachineReader.computeLength( reader );
		reader.setPointerIndex( startIndex );
		stateMachineOpcodes = reader.readNextByteArray( count );
	}

	/**
	 * <pre>
	 * The initial value for the state machine's line register. 
	 * Does not represent an actual positions entry.
	 * </pre>
	 */
	public int getLineStart( ) {
		return lineStart;
	}

	/**
	 * <pre>
	 * The number of parameter names that are encoded. 
	 * There should be one per method parameter, excluding an instance method's this, if any.
	 * </pre>
	 */
	public int getParametersSize( ) {
		return parametersSize;
	}

	/**
	 * <pre>
	 * String index of the method parameter name. 
	 * An encoded value of NO_INDEX indicates that no name is available for the associated parameter. 
	 * The type descriptor and signature are implied from the method descriptor and signature.
	 * </pre>
	 */
	public int [] getParameterNames( ) {
		return parameterNames;
	}

	public byte [] getStateMachineOpcodes( ) {
		return stateMachineOpcodes;
	}

	@Override
	public DataType toDataType( ) throws DuplicateNameException, IOException {
		StringBuilder builder = new StringBuilder( );
		builder.append( "debug_info_item" + "_" );
		builder.append( lineStartLength + "" );
		builder.append( parametersSizeLength + "" );
		builder.append( parametersSize + "" );
		builder.append( stateMachineOpcodes.length + "" );

		Structure structure = new StructureDataType( builder.toString( ), 0 );

		structure.add( new ArrayDataType( BYTE, lineStartLength, BYTE.getLength( ) ), "line_start", null );
		structure.add( new ArrayDataType( BYTE, parametersSizeLength, BYTE.getLength( ) ), "parameters_size", null );

		for ( int i = 0 ; i < parametersSize ; ++i ) {
			ArrayDataType dataType = new ArrayDataType( BYTE, parameterNamesLengths[ i ], BYTE.getLength( ) );
			structure.add( dataType, "parameter_" + i, null );
			builder.append( dataType.getLength( ) + "" );
		}

		ArrayDataType stateMachineArray = new ArrayDataType( BYTE, stateMachineOpcodes.length, BYTE.getLength( ) );
		structure.add( stateMachineArray, "state_machine", null );

		structure.setCategoryPath( new CategoryPath( "/dex/debug_info_item" ) );
		try {
			structure.setName( builder.toString( ) );
		}
		catch ( Exception e ) {
			// ignore
		}
		return structure;
	}
}