/*
 * 
 * Copyright 2015 Steven Eddies
 *
 * 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 uk.me.eddies.apps.spt.domain;

import java.util.Date;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import uk.me.eddies.libs.event.TestListener;

/**
 * Unit tests for the MemberManager class.
 *
 * @author Steven Eddies
 */
public class MemberManagerTest
{
	/**
	 * Tests the constructor works correctly.
	 */
	@Test
	public void test1Constructor()
	{
		MemberManager result = new MemberManager();
		
		Assert.assertEquals(0, result.list().size());
		Assert.assertEquals(0, result.listArchived().size());
	}
	
	/**
	 * Tests add() works correctly with no existing members.
	 */
	@Test
	public void test1Add()
	{
		MemberManager mm = new MemberManager();
		TestListener<MemberEvent> tl = new TestListener<MemberEvent>();
		mm.getListeners().add(tl);
		TimePeriod tp1 = new TimePeriod(new Date(), null);
		
		Member result = mm.add("Fred",
								tp1,
								"test@example.com",
								"12 Any Street\nAnytown");

		Assert.assertNotNull(result);
		Assert.assertEquals(1, mm.list().size());
		Assert.assertEquals(0, mm.listArchived().size());
		Assert.assertTrue(mm.list().contains(result));
		Assert.assertEquals(1, tl.getReceived().size());
		MemberTest.assertState(result, "Fred", tp1, false,
				"test@example.com", "12 Any Street\nAnytown");
	}
	
	/**
	 * Tests add() works correctly with existing members and sorting.
	 */
	@Test
	public void test2Add()
	{
		MemberManager mm = new MemberManager();
		TestListener<MemberEvent> tl = new TestListener<MemberEvent>();
		mm.getListeners().add(tl);
		Date now = new Date();
		Date later = new Date(now.getTime() + 1000000000);
		TimePeriod tp1 = new TimePeriod(now, later);
		
		Member m1 = mm.add("George",
							tp1,
							"test@example.com",
							"12 Any Street\nAnytown");
		Member m2 = mm.add("Fred",
							tp1,
							"me@example.com",
							"21 Any Street\nAnytown");

		Assert.assertNotNull(m2);
		Assert.assertEquals(2, mm.list().size());
		Assert.assertEquals(0, mm.listArchived().size());
		Assert.assertEquals(m2, mm.list().get(0));
		Assert.assertEquals(m1, mm.list().get(1));
		Assert.assertEquals(2, tl.getReceived().size());
		MemberTest.assertState(m1, "George", tp1, false,
				"test@example.com", "12 Any Street\nAnytown");
		MemberTest.assertState(m2, "Fred", tp1, false,
				"me@example.com", "21 Any Street\nAnytown");
	}
	
	/**
	 * Tests add() fails correctly with null parameters.
	 */
	@Test
	public void test3Add()
	{
		MemberManager mm = new MemberManager();
		TestListener<MemberEvent> tl = new TestListener<MemberEvent>();
		mm.getListeners().add(tl);
		Date now = new Date();
		Date later = new Date(now.getTime() + 1000000000);
		TimePeriod tp1 = new TimePeriod(now, later);
		
		try
		{
			mm.add(null, tp1, "test@example.com",
					"12 Any Street\nAnytown");
			Assert.fail();
		}
		catch (NullPointerException ex) { }
		
		try
		{
			mm.add("George", null, "test@example.com",
					"12 Any Street\nAnytown");
			Assert.fail();
		}
		catch (NullPointerException ex) { }
		
		try
		{
			mm.add("George", tp1, null, "12 Any Street\nAnytown");
			Assert.fail();
		}
		catch (NullPointerException ex) { }
		
		try
		{
			mm.add("George", tp1, "test@example.com", null);
			Assert.fail();
		}
		catch (NullPointerException ex) { }

		Assert.assertEquals(0, tl.getReceived().size());
		Assert.assertEquals(0, mm.list().size());
		Assert.assertEquals(0, mm.listArchived().size());
	}
	
	/**
	 * Tests edit() works correctly under normal use including sorting.
	 */
	@Test
	public void test1Edit()
	{
		MemberManager mm = new MemberManager();
		Date now = new Date();
		Date later = new Date(now.getTime() + 1000000000);
		TimePeriod tp1 = new TimePeriod(now, later);
		TimePeriod tp2 = new TimePeriod(now, null);
		
		Member m1 = mm.add("George",
							tp1,
							"test@example.com",
							"12 Any Street\nAnytown");
		Member m2 = mm.add("Fred",
							tp1,
							"me@example.com",
							"21 Any Street\nAnytown");
		TestListener<MemberEvent> tl = new TestListener<MemberEvent>();
		mm.getListeners().add(tl);
		
		mm.edit(m2, "Pete", tp2, "test2@example.com",
				"22 Any Street\nAnytown");

		Assert.assertEquals(2, mm.list().size());
		Assert.assertEquals(0, mm.listArchived().size());
		Assert.assertEquals(m1, mm.list().get(0));
		Assert.assertEquals(m2, mm.list().get(1));
		Assert.assertEquals(1, tl.getReceived().size());
		MemberTest.assertState(m1, "George", tp1, false,
				"test@example.com", "12 Any Street\nAnytown");
		MemberTest.assertState(m2, "Pete", tp2, false,
				"test2@example.com", "22 Any Street\nAnytown");
	}
	
	/**
	 * Tests edit() fails correctly with null parameter.
	 */
	@Test
	public void test2Edit()
	{
		MemberManager mm = new MemberManager();
		Date now = new Date();
		Date later = new Date(now.getTime() + 1000000000);
		TimePeriod tp1 = new TimePeriod(now, later);
		
		Member m1 = mm.add("George", tp1, "test@example.com",
				"12 Any Street\nAnytown");
		TestListener<MemberEvent> tl = new TestListener<MemberEvent>();
		mm.getListeners().add(tl);
		
		try
		{
			mm.edit(null, "Pete", tp1, "test2@example.com",
				"22 Any Street\nAnytown");
			Assert.fail();
		}
		catch (NullPointerException ex) { }
		
		try
		{
			mm.edit(m1, null, tp1, "test2@example.com",
				"22 Any Street\nAnytown");
			Assert.fail();
		}
		catch (NullPointerException ex) { }
		
		try
		{
			mm.edit(m1, "Pete", null, "test2@example.com",
				"22 Any Street\nAnytown");
			Assert.fail();
		}
		catch (NullPointerException ex) { }
		
		try
		{
			mm.edit(m1, "Pete", tp1, null, "22 Any Street\nAnytown");
			Assert.fail();
		}
		catch (NullPointerException ex) { }
		
		try
		{
			mm.edit(m1, "Pete", tp1, "test2@example.com", null);
			Assert.fail();
		}
		catch (NullPointerException ex) { }
		
		Assert.assertEquals(1, mm.list().size());
		Assert.assertEquals(0, mm.listArchived().size());
		Assert.assertEquals(0, tl.getReceived().size());
		MemberTest.assertState(m1, "George", tp1, false,
				"test@example.com", "12 Any Street\nAnytown");
	}
	
	/**
	 * Tests edit() fails correctly with nonexistent member.
	 */
	@Test
	public void test4Edit()
	{
		MemberManager mm = new MemberManager();
		Date now = new Date();
		Date later = new Date(now.getTime() + 1000000000);
		TimePeriod tp1 = new TimePeriod(now, later);
		
		Member m1 = mm.add("George", tp1, "test@example.com",
				"12 Any Street\nAnytown");
		Member m2 = new Member("George", tp1, "test@example.com",
				"12 Any Street\nAnytown");
		TestListener<MemberEvent> tl = new TestListener<MemberEvent>();
		mm.getListeners().add(tl);
		
		try
		{
			mm.edit(m2, "Pete", tp1, "test2@example.com",
				"22 Any Street\nAnytown");
			Assert.fail();
		}
		catch (IllegalStateException ex) { }
		
		Assert.assertEquals(1, mm.list().size());
		Assert.assertEquals(0, mm.listArchived().size());
		Assert.assertEquals(0, tl.getReceived().size());
		MemberTest.assertState(m1, "George", tp1, false,
				"test@example.com", "12 Any Street\nAnytown");
	}
	
	/**
	 * Tests edit() works correctly under normal use with archived member.
	 */
	@Test
	public void test5Edit()
	{
		MemberManager mm = new MemberManager();
		Date now = new Date();
		Date later = new Date(now.getTime() + 1000000000);
		TimePeriod tp1 = new TimePeriod(now, later);
		TimePeriod tp2 = new TimePeriod(now, null);
		
		Member m1 = mm.add("George",
							tp1,
							"test@example.com",
							"12 Any Street\nAnytown");
		Member m2 = mm.add("Fred",
							tp1,
							"me@example.com",
							"21 Any Street\nAnytown");
		mm.setArchived(m2, true);
		TestListener<MemberEvent> tl = new TestListener<MemberEvent>();
		mm.getListeners().add(tl);
		
		mm.edit(m2, "Pete", tp2, "test2@example.com",
				"22 Any Street\nAnytown");

		Assert.assertEquals(1, mm.list().size());
		Assert.assertEquals(1, mm.listArchived().size());
		Assert.assertEquals(m1, mm.list().get(0));
		Assert.assertEquals(m2, mm.listArchived().get(0));
		Assert.assertEquals(1, tl.getReceived().size());
		MemberTest.assertState(m1, "George", tp1, false,
				"test@example.com", "12 Any Street\nAnytown");
		MemberTest.assertState(m2, "Pete", tp2, true,
				"test2@example.com", "22 Any Street\nAnytown");
	}
	
	/**
	 * Tests delete() works correctly under normal use with a non-archived
	 * member.
	 */
	@Test
	public void test1Delete()
	{
		MemberManager mm = new MemberManager();
		Date now = new Date();
		Date later = new Date(now.getTime() + 1000000000);
		TimePeriod tp1 = new TimePeriod(now, later);
		
		Member m1 = mm.add("George",
							tp1,
							"test@example.com",
							"12 Any Street\nAnytown");
		Member m2 = mm.add("Fred",
							tp1,
							"me@example.com",
							"21 Any Street\nAnytown");
		TestListener<MemberEvent> tl = new TestListener<MemberEvent>();
		mm.getListeners().add(tl);
		
		mm.delete(m2);

		Assert.assertEquals(1, mm.list().size());
		Assert.assertEquals(0, mm.listArchived().size());
		Assert.assertEquals(m1, mm.list().get(0));
		Assert.assertEquals(1, tl.getReceived().size());
		MemberTest.assertState(m1, "George", tp1, false,
				"test@example.com", "12 Any Street\nAnytown");
	}
	
	/**
	 * Tests delete() fails correctly with null parameter.
	 */
	@Test
	public void test2Delete()
	{
		MemberManager mm = new MemberManager();
		Date now = new Date();
		Date later = new Date(now.getTime() + 1000000000);
		TimePeriod tp1 = new TimePeriod(now, later);
		
		mm.add("George", tp1, "test@example.com",
				"12 Any Street\nAnytown");
		TestListener<MemberEvent> tl = new TestListener<MemberEvent>();
		mm.getListeners().add(tl);
		
		try
		{
			mm.delete(null);
			Assert.fail();
		}
		catch (NullPointerException ex) { }
		
		Assert.assertEquals(1, mm.list().size());
		Assert.assertEquals(0, mm.listArchived().size());
		Assert.assertEquals(0, tl.getReceived().size());
	}
	
	/**
	 * Tests delete() fails correctly with nonexistent member.
	 */
	@Test
	public void test3Delete()
	{
		MemberManager mm = new MemberManager();
		Date now = new Date();
		Date later = new Date(now.getTime() + 1000000000);
		TimePeriod tp1 = new TimePeriod(now, later);
		
		mm.add("George", tp1, "test@example.com",
				"12 Any Street\nAnytown");
		
		Member m2 = new Member("Fred", tp1, "test@example.com",
				"12 Any Street\nAnytown");
		TestListener<MemberEvent> tl = new TestListener<MemberEvent>();
		mm.getListeners().add(tl);
		
		try
		{
			mm.delete(m2);
			Assert.fail();
		}
		catch (IllegalStateException ex) { }
		
		Assert.assertEquals(1, mm.list().size());
		Assert.assertEquals(0, mm.listArchived().size());
		Assert.assertEquals(0, tl.getReceived().size());
	}
	
	/**
	 * Tests delete() works correctly with an archived member.
	 */
	@Test
	public void test4Delete()
	{
		MemberManager mm = new MemberManager();
		Date now = new Date();
		Date later = new Date(now.getTime() + 1000000000);
		TimePeriod tp1 = new TimePeriod(now, later);
		
		Member m1 = mm.add("George",
							tp1,
							"test@example.com",
							"12 Any Street\nAnytown");
		mm.setArchived(m1, true);
		TestListener<MemberEvent> tl = new TestListener<MemberEvent>();
		mm.getListeners().add(tl);
		
		mm.delete(m1);

		Assert.assertEquals(0, mm.list().size());
		Assert.assertEquals(0, mm.listArchived().size());
		Assert.assertEquals(1, tl.getReceived().size());
	}
	
	/**
	 * Tests the return value from list() cannot be modified.
	 */
	@Test
	public void test1List()
	{
		MemberManager mm = new MemberManager();
		TimePeriod tp1 = new TimePeriod(new Date(), null);
		
		List<Member> result = mm.list();
		
		Assert.assertNotNull(result);
		
		try
		{
			result.add(new Member("Fred", tp1, "SS", "XX"));
			Assert.fail();
		}
		catch (UnsupportedOperationException ex)
		{
		}
	}
	
	/**
	 * Tests the return value from listArchived() cannot be modified.
	 */
	@Test
	public void test1ListArchived()
	{
		MemberManager mm = new MemberManager();
		TimePeriod tp1 = new TimePeriod(new Date(), null);
		
		List<Member> result = mm.listArchived();
		
		Assert.assertNotNull(result);
		
		try
		{
			result.add(new Member("Fred", tp1, "SS", "XX"));
			Assert.fail();
		}
		catch (UnsupportedOperationException ex)
		{
		}
	}
	
	/**
	 * Tests setArchived() works correctly under normal use.
	 */
	@Test
	public void test1SetArchived()
	{
		MemberManager mm = new MemberManager();
		Date now = new Date();
		Date later = new Date(now.getTime() + 1000000000);
		TimePeriod tp1 = new TimePeriod(now, later);
		
		Member m1 = mm.add("George",
							tp1,
							"test@example.com",
							"12 Any Street\nAnytown");
		Member m2 = mm.add("Fred",
							tp1,
							"me@example.com",
							"21 Any Street\nAnytown");
		TestListener<MemberEvent> tl = new TestListener<MemberEvent>();
		mm.getListeners().add(tl);
		
		mm.setArchived(m1, true);

		Assert.assertEquals(1, mm.list().size());
		Assert.assertEquals(1, mm.listArchived().size());
		Assert.assertEquals(m2, mm.list().get(0));
		Assert.assertEquals(m1, mm.listArchived().get(0));
		Assert.assertEquals(1, tl.getReceived().size());
		MemberTest.assertState(m1, "George", tp1, true,
				"test@example.com", "12 Any Street\nAnytown");
		MemberTest.assertState(m2, "Fred", tp1, false,
				"me@example.com", "21 Any Street\nAnytown");
		
		mm.setArchived(m1, false);

		Assert.assertEquals(2, mm.list().size());
		Assert.assertEquals(0, mm.listArchived().size());
		Assert.assertEquals(m2, mm.list().get(0));
		Assert.assertEquals(m1, mm.list().get(1));
		Assert.assertEquals(2, tl.getReceived().size());
		MemberTest.assertState(m1, "George", tp1, false,
				"test@example.com", "12 Any Street\nAnytown");
		MemberTest.assertState(m2, "Fred", tp1, false,
				"me@example.com", "21 Any Street\nAnytown");
	}

	/**
	 * Tests setArchived() works correctly and sorts members.
	 */
	@Test
	public void test2SetArchived()
	{
		MemberManager mm = new MemberManager();
		Date now = new Date();
		Date later = new Date(now.getTime() + 1000000000);
		TimePeriod tp1 = new TimePeriod(now, later);
		
		Member m1 = mm.add("George",
							tp1,
							"test@example.com",
							"12 Any Street\nAnytown");
		Member m2 = mm.add("Fred",
							tp1,
							"me@example.com",
							"21 Any Street\nAnytown");
		TestListener<MemberEvent> tl = new TestListener<MemberEvent>();
		mm.getListeners().add(tl);
		
		mm.setArchived(m1, true);
		mm.setArchived(m2, true);

		Assert.assertEquals(0, mm.list().size());
		Assert.assertEquals(2, mm.listArchived().size());
		Assert.assertEquals(m2, mm.listArchived().get(0));
		Assert.assertEquals(m1, mm.listArchived().get(1));
		Assert.assertEquals(2, tl.getReceived().size());
		MemberTest.assertState(m1, "George", tp1, true,
				"test@example.com", "12 Any Street\nAnytown");
		MemberTest.assertState(m2, "Fred", tp1, true,
				"me@example.com", "21 Any Street\nAnytown");
	}
	
	/**
	 * Tests setArchived() fails correctly with null parameter.
	 */
	@Test
	public void test3SetArchived()
	{
		MemberManager mm = new MemberManager();
		Date now = new Date();
		Date later = new Date(now.getTime() + 1000000000);
		TimePeriod tp1 = new TimePeriod(now, later);
		
		mm.add("George", tp1, "test@example.com",
				"12 Any Street\nAnytown");
		TestListener<MemberEvent> tl = new TestListener<MemberEvent>();
		mm.getListeners().add(tl);
		
		try
		{
			mm.setArchived(null, true);
			Assert.fail();
		}
		catch (NullPointerException ex) { }
		
		Assert.assertEquals(1, mm.list().size());
		Assert.assertEquals(0, mm.listArchived().size());
		Assert.assertEquals(0, tl.getReceived().size());
	}
	
	/**
	 * Tests setArchived() fails correctly with nonexistent member.
	 */
	@Test
	public void test4SetArchived()
	{
		MemberManager mm = new MemberManager();
		Date now = new Date();
		Date later = new Date(now.getTime() + 1000000000);
		TimePeriod tp1 = new TimePeriod(now, later);
		
		mm.add("George", tp1, "test@example.com",
				"12 Any Street\nAnytown");
		
		Member m2 = new Member("Fred", tp1, "test@example.com",
				"12 Any Street\nAnytown");
		TestListener<MemberEvent> tl = new TestListener<MemberEvent>();
		mm.getListeners().add(tl);
		
		try
		{
			mm.setArchived(m2, true);
			Assert.fail();
		}
		catch (IllegalStateException ex) { }
		
		Assert.assertEquals(1, mm.list().size());
		Assert.assertEquals(0, mm.listArchived().size());
		Assert.assertEquals(0, tl.getReceived().size());
	}
}
