package jrummikub.model;

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

import jrummikub.util.Pair;

import org.junit.*;
import static org.junit.Assert.*;

public class StoneTrayTest {
	class Thing implements Sizeable {
		private float width;
		private float height;

		public Thing(float width, float height) {
			this.width = width;
			this.height = height;
		}

		@Override
		public float getWidth() {
			return width;
		}

		@Override
		public float getHeight() {
			return height;
		}
	}

	private StoneTray<Thing> testTray;

	@Before
	public void createTray() {
		testTray = new StoneTray<Thing>();
	}

	@Test
	public void testDrop() {
		Thing firstThing = new Thing(3, 4);
		testTray.drop(firstThing, new Position(5, 23));
		Thing secondThing = new Thing(5, 8);
		testTray.drop(secondThing, new Position(42, 8.5f));
		Position firstPosition = testTray.getPosition(firstThing);
		Position secondPosition = testTray.getPosition(secondThing);
		assertEquals(5, firstPosition.getX(), 0.00001);
		assertEquals(23, firstPosition.getY(), 0.00001);
		assertEquals(42, secondPosition.getX(), 0.00001);
		assertEquals(8.5, secondPosition.getY(), 0.00001);
	}

	@Test
	public void testDropNull() {
		testTray.drop(null, new Position(0, 0));
		assertFalse(testTray.iterator().hasNext());
	}

	// Leftshift
	@Test
	public void testLeftDrop() {
		Thing firstThing = new Thing(5, 5);
		testTray.drop(firstThing, new Position(0, 0));
		Thing secondThing = new Thing(3, 3);
		testTray.drop(secondThing, new Position(4, 1));
		Position firstPosition = testTray.getPosition(firstThing);
		Position secondPosition = testTray.getPosition(secondThing);
		assertEquals(-1, firstPosition.getX(), 0.00001);
		assertEquals(0, firstPosition.getY(), 0.00001);
		assertEquals(4, secondPosition.getX(), 0.00001);
		assertEquals(1, secondPosition.getY(), 0.00001);
	}

	// Rightshift
	@Test
	public void testRightDrop() {
		Thing firstThing = new Thing(5, 5);
		testTray.drop(firstThing, new Position(0, 0));
		Thing secondThing = new Thing(3, 3);
		testTray.drop(secondThing, new Position(-2, 1));
		Position firstPosition = testTray.getPosition(firstThing);
		Position secondPosition = testTray.getPosition(secondThing);
		assertEquals(1, firstPosition.getX(), 0.00001);
		assertEquals(0, firstPosition.getY(), 0.00001);
		assertEquals(-2, secondPosition.getX(), 0.00001);
		assertEquals(1, secondPosition.getY(), 0.00001);
	}

	// Upshift
	@Test
	public void testUpDrop() {
		Thing firstThing = new Thing(5, 5);
		testTray.drop(firstThing, new Position(0, 0));
		Thing secondThing = new Thing(3, 3);
		testTray.drop(secondThing, new Position(1, 4));
		Position firstPosition = testTray.getPosition(firstThing);
		Position secondPosition = testTray.getPosition(secondThing);
		assertEquals(0, firstPosition.getX(), 0.00001);
		assertEquals(-1, firstPosition.getY(), 0.00001);
		assertEquals(1, secondPosition.getX(), 0.00001);
		assertEquals(4, secondPosition.getY(), 0.00001);
	}

	// Downshift
	@Test
	public void testDownDrop() {
		Thing firstThing = new Thing(5, 5);
		testTray.drop(firstThing, new Position(0, 0));
		Thing secondThing = new Thing(3, 3);
		testTray.drop(secondThing, new Position(1, -2));
		Position firstPosition = testTray.getPosition(firstThing);
		Position secondPosition = testTray.getPosition(secondThing);
		assertEquals(0, firstPosition.getX(), 0.00001);
		assertEquals(1, firstPosition.getY(), 0.00001);
		assertEquals(1, secondPosition.getX(), 0.00001);
		assertEquals(-2, secondPosition.getY(), 0.00001);
	}

	@Test
	public void testDoubleShift() {
		Thing firstThing = new Thing(5, 5);
		testTray.drop(firstThing, new Position(0, 0));

		Thing secondThing = new Thing(5, 0.1f);
		testTray.drop(secondThing, new Position(5, 0));

		Thing thirdThing = new Thing(3, 3);
		testTray.drop(thirdThing, new Position(-2, 1));
		Position firstPosition = testTray.getPosition(firstThing);
		Position secondPosition = testTray.getPosition(secondThing);
		Position thirdPosition = testTray.getPosition(thirdThing);
		assertEquals(1, firstPosition.getX(), 0.00001);
		assertEquals(0, firstPosition.getY(), 0.00001);
		assertEquals(6, secondPosition.getX(), 0.00001);
		assertEquals(0, secondPosition.getY(), 0.00001);
		assertEquals(-2, thirdPosition.getX(), 0.00001);
		assertEquals(1, thirdPosition.getY(), 0.00001);
	}

	@Test
	public void testWrongPickUp() {
		Thing firstThing = new Thing(5, 5);
		testTray.drop(firstThing, new Position(0, 0));
		Position testPosition = new Position(-2, -2);
		assertNull(testTray.pickUp(testPosition));
	}

	@Test
	public void testPickUpByObject() {
		Thing firstThing = new Thing(5, 5);
		testTray.drop(firstThing, new Position(0, 0));
		Thing secondThing = new Thing(5, 5);
		testTray.drop(secondThing, new Position(5, 0));
		testTray.pickUp(firstThing);
		for (Pair<Thing, Position> i : testTray) {
			assertSame(i.getFirst(), secondThing);
		}
		assertTrue(testTray.iterator().hasNext());
	}

	@Test
	public void testRightPickUp() {
		Thing firstThing = new Thing(5, 5);
		testTray.drop(firstThing, new Position(0, 0));
		Thing secondThing = new Thing(3, 3);
		testTray.drop(secondThing, new Position(-5, -5));
		Position testPosition = new Position(3, 3);
		assertSame(testTray.pickUp(testPosition), firstThing);
		assertNull(testTray.pickUp(testPosition));
	}

	@Test
	public void testIterate() {
		List<Thing> testThings = new ArrayList<Thing>();
		List<Position> testPositions = new ArrayList<Position>();
		for (int i = 0; i < 4; i++) {
			Thing newThing = new Thing(1, 1);
			Position newPosition = new Position(i, i);
			testThings.add(newThing);
			testPositions.add(newPosition);
			testTray.drop(newThing, newPosition);
		}
		for (Pair<Thing, Position> i : testTray) {
			int index = testThings.indexOf(i.getFirst());
			assertFalse(index == -1);
			assertEquals(i.getSecond(), testPositions.get(index));
			testThings.remove(index);
			testPositions.remove(index);
		}
		assertTrue(testThings.isEmpty());
		assertTrue(testPositions.isEmpty());
	}

	@Test
	public void testClone() {
		Thing firstThing = new Thing(5, 5);
		testTray.drop(firstThing, new Position(0, 0));
		Thing secondThing = new Thing(3, 3);
		testTray.drop(secondThing, new Position(-5, -5));
		IStoneTray<Thing> trayCopy = testTray.clone();

		testTray.pickUp(firstThing);
		for (Pair<Thing, Position> i : testTray) {
			assertSame(i.getFirst(), secondThing);
		}
		assertTrue(testTray.iterator().hasNext());

		trayCopy.pickUp(secondThing);
		for (Pair<Thing, Position> i : trayCopy) {
			assertSame(i.getFirst(), firstThing);
		}
		assertTrue(trayCopy.iterator().hasNext());
	}
}