/*******************************************************************************
 * JetUML - A desktop application for fast UML diagramming.
 *
 * Copyright (C) 2020 by the contributors of the JetUML project.
 *     
 * See: https://github.com/prmr/JetUML
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *******************************************************************************/
package ca.mcgill.cs.jetuml.viewers.nodes;

import static ca.mcgill.cs.jetuml.testutils.GeometryUtils.osDependent;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

import java.lang.reflect.Method;
import java.util.stream.Stream;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import ca.mcgill.cs.jetuml.JavaFXLoader;
import ca.mcgill.cs.jetuml.diagram.nodes.ClassNode;
import ca.mcgill.cs.jetuml.diagram.nodes.InterfaceNode;
import ca.mcgill.cs.jetuml.diagram.nodes.TypeNode;
import ca.mcgill.cs.jetuml.geom.Point;
import ca.mcgill.cs.jetuml.geom.Rectangle;

public class TestTypeNodeViewer
{
	private static final TypeNodeViewer aViewer = new TypeNodeViewer();
	private final Method aMethodNameBoxHeight;
	
	public TestTypeNodeViewer() throws ReflectiveOperationException
	{
		aMethodNameBoxHeight = TypeNodeViewer.class.getDeclaredMethod("nameBoxHeight", 
				TypeNode.class, int.class, int.class);
		aMethodNameBoxHeight.setAccessible(true);
	}
	
	private int callNameBoxHeight(TypeNode pNode, int pAttributeBoxHeight, int pMethodBoxHeight)
	{
		try
		{
			return (int) aMethodNameBoxHeight.invoke(aViewer, pNode, pAttributeBoxHeight, pMethodBoxHeight);
		}
		catch( ReflectiveOperationException e )
		{
			fail("Reflection problem: " + e.getMessage());
			return -1;
		}
	}
	
	@BeforeAll
	public static void setupClass()
	{
		JavaFXLoader.load();
	}
	
	@ParameterizedTest
	@MethodSource("provideArgumentsForTestBounds")
	public void testBounds(TypeNode pNode, Rectangle pOracle)
	{
		assertEquals(pOracle, aViewer.getBounds(pNode));
	}
	
	private static Stream<Arguments> provideArgumentsForTestBounds() {
	    return Stream.of(
	      createClassNode1(),
	      createClassNode2(),
	      createClassNode3(),
	      createClassNode4(),
	      createClassNode5(),
	      createClassNode6()
	    );
	}
	
	// At (0,0); name is just the interface prototype, no methods
	private static Arguments createClassNode1()
	{
		return Arguments.of(new ClassNode(), 
				new Rectangle(0,0, 100, 60)); // Default width and height
	}
	
	// At (10,20); name is just the interface prototype, no methods
	private static Arguments createClassNode2()
	{
		ClassNode node = new ClassNode();
		node.moveTo(new Point(10,20));
		return Arguments.of(node, 
				new Rectangle(10, 20, 100, 60)); // Default width and height, translated
	}
	
	// At (0,0), name is a single line
	private static Arguments createClassNode3()
	{
		ClassNode node = new ClassNode();
		node.setName(node.getName() + "NAME");
		return Arguments.of(node, 
				new Rectangle(0, 0, 100, 60)); // Default width and height
	}
	
	// At (0,0) name is two lines, no methods
	private static Arguments createClassNode4()
	{
		ClassNode node = new ClassNode();
		node.setName(node.getName() + "NAME");
		return Arguments.of(node, 
				new Rectangle(0, 0, 100, 60)); // Default width and height
	}
	
	// At (0,0) name is four lines, no methods
	private static Arguments createClassNode5()
	{
		ClassNode node = new ClassNode();
		node.setName("NAME1\nNAME2\nNAME3\nNAME4");
		return Arguments.of(node, 
				new Rectangle(0, 0, 100, osDependent(70, 65, 65))); // Default width and additional height
	}

	// Name is just the interface prototype, one methods
	private static Arguments createClassNode6()
	{
		ClassNode node = new ClassNode();
		node.setMethods("METHODS");
		return Arguments.of(node, 
				new Rectangle(0, 0, 100, 60)); // Default width and height
	}
	
	@Test
	public void testNameBoxHeight_OneLineName()
	{
		assertEquals(60, callNameBoxHeight(new InterfaceNode(), 0, 0));
	}
	
	@Test
	public void testNameBoxHeight_MultiLineName()
	{
		InterfaceNode node = new InterfaceNode();
		node.setName("X\nX\nX\nX\nX");
		assertTrue(callNameBoxHeight(node, 0, 0) > 60);
	}
	
	@Test
	public void testNameBoxHeight_OneLineNameAndAttribute()
	{
		ClassNode node = new ClassNode();
		assertEquals(40, callNameBoxHeight(node, 20, 0));
	}
	
	@Test
	public void testNameBoxHeight_OneLineNameAndAttributeAndMethods()
	{
		ClassNode node = new ClassNode();
		assertEquals(20, callNameBoxHeight(node, 20, 40));
	}
}