/* ###
 * 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.merge.datatypes;

import static org.junit.Assert.*;

import javax.swing.JLabel;

import org.junit.Test;

import generic.test.TestUtils;
import ghidra.framework.main.*;
import ghidra.program.database.OriginalProgramModifierListener;
import ghidra.program.database.ProgramDB;
import ghidra.program.model.data.*;

/**
 * More data type merge tests.
 * 
 * 
 */
public class DataTypeMerge8Test extends AbstractDataTypeMergeTest {

	@Test
	public void testConflictFixUpForNonFittingStruct() throws Exception {

		final CategoryPath miscPath = new CategoryPath("/MISC");
		final CategoryPath rootPath = new CategoryPath("/");

		FrontEndTool frontEndTool = mtf.getTestEnvironment().showFrontEndTool();

		mtf.initialize("notepad2", new OriginalProgramModifierListener() {
			@Override
			public void modifyOriginal(ProgramDB program) throws Exception {
				DataTypeManager dtm = program.getDataTypeManager();

				Structure xyz = new StructureDataType("XYZ", 0);
				xyz.add(new WordDataType());
				// struct XYZ {
				//      word
				// } (size=2)

				Structure abc = new StructureDataType("ABC", 0);
				abc.add(new ByteDataType());
				abc.add(new ByteDataType());
				abc.add(new WordDataType());
				abc.add(xyz);
				abc.add(DataType.DEFAULT);
				abc.add(new ByteDataType());
				// struct ABC {
				//     byte
				//     byte
				//     word
				//     XYZ (size=2)
				//     default
				//     byte
				// } (size=8)

				Category miscCategory = dtm.getCategory(miscPath);
				miscCategory.addDataType(abc, null);

				abc = (Structure) dtm.getDataType(miscPath, "ABC");
				xyz = (Structure) dtm.getDataType(rootPath, "XYZ");

				assertNotNull(abc);
				assertNotNull(xyz);
			}

			@Override
			public void modifyLatest(ProgramDB program) {
				DataTypeManager dtm = program.getDataTypeManager();
				Structure abc = (Structure) dtm.getDataType(miscPath, "ABC");
				Structure xyz = (Structure) dtm.getDataType(rootPath, "XYZ");

				// Change first byte in ABC to a char.
				abc.replace(0, new CharDataType(), 1);

				// Remove the XYZ data type.
				dtm.remove(xyz);
			}

			@Override
			public void modifyPrivate(ProgramDB program) {
				DataTypeManager dtm = program.getDataTypeManager();
				Structure abc = (Structure) dtm.getDataType(miscPath, "ABC");
				Structure xyz = (Structure) dtm.getDataType(rootPath, "XYZ");

				// Change second byte in ABC to a char.
				abc.replace(1, new CharDataType(), 1);

				// Increase the size of XYZ data type so it won't fit in its component.
				xyz.add(new FloatDataType());
			}
		});

		frontEndTool.clearStatusInfo();

		executeMerge();

		// choose MY for Foo conflict
		chooseOption(DataTypeMergeManager.OPTION_MY);
		// choose MY for Bar conflict
		chooseOption(DataTypeMergeManager.OPTION_MY);

		pressButtonByName(waitForWindow("Structure Update Failed"), "OK"); // expected dependency error on ABC

		waitForCompletion();

		FrontEndPlugin frontEndPlugin = getPlugin(frontEndTool, FrontEndPlugin.class);
		LogPanel logPanel = (LogPanel) TestUtils.getInstanceField("statusPanel", frontEndPlugin);
		JLabel label = (JLabel) TestUtils.getInstanceField("label", logPanel);
		String statusText = label.getText();
		String expectedText =
			"Structure Update Failed: Some of your changes to ABC cannot be merged. " +
				"Problem: Not enough undefined bytes to fit /XYZ in structure /MISC/ABC at " +
				"offset 0x4. It needs 3 more byte(s) to be  able to fit.";
		assertTrue("Wrong status text: " + statusText, statusText.contains(expectedText));
	}
}
