package smash.appointment.parse;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;

public class CellParserTest extends TestBase {
	Logger									logger = Logger.getLogger(CellParserTest.class);

	List<CellParseTestCase>	testCases;
	CellParser							parser;

	@Before
	public void setUp() {
		super.setUp();

		parser = new CellParser();
		parser.setSubjectDao(subjectDao);

		testCases = new ArrayList<CellParseTestCase>();

		testCases.add(new CellParseTestCase("Piotr Gawron level A FU V3", piotrGawron, null, new AppointmentType[] { AppointmentType.LEVEL_A }));
		testCases.add(new CellParseTestCase("09:00 Jan Kowalski-Nowak level A", janKowalskiNowak, "09:00", new AppointmentType[] { AppointmentType.LEVEL_A }));
		testCases.add(
				new CellParseTestCase(
						"ND0002 l664574645 (sms)evel BV ©  + SB ©", janKowalskiNowak, null, new AppointmentType[] { AppointmentType.LEVEL_BV, AppointmentType.LEVEL_SB }));
		testCases.add(new CellParseTestCase("ND0001 654654631 level B ©", piotrGawron, null, new AppointmentType[] { AppointmentType.LEVEL_B }));
		testCases.add(
				new CellParseTestCase(
						"John Doe BV + BG + SB", johnDoe, null, new AppointmentType[] { AppointmentType.LEVEL_BV, AppointmentType.LEVEL_BG, AppointmentType.LEVEL_SB }));
		testCases.add(new CellParseTestCase("Kowalski-Nowak m-Power", janKowalskiNowak, null, new AppointmentType[] { AppointmentType.LEVEL_B_M_POWER }));
		testCases.add(new CellParseTestCase("ND0004 Name BV ©", cateKowalsky, null, new AppointmentType[] { AppointmentType.LEVEL_BV }));
		testCases.add(
				new CellParseTestCase("ND0004 level BV (c) + SB ©", cateKowalsky, null, new AppointmentType[] { AppointmentType.LEVEL_BV, AppointmentType.LEVEL_SB }));
		testCases.add(
				new CellParseTestCase(
						"Cate Kowalsky level BV + BG + SB + m-Power", cateKowalsky, null,
						new AppointmentType[] { AppointmentType.LEVEL_BV, AppointmentType.LEVEL_BG, AppointmentType.LEVEL_SB, AppointmentType.LEVEL_B_M_POWER }));
		testCases.add(new CellParseTestCase("sb name level A", null, null, new AppointmentType[] { AppointmentType.LEVEL_A }));
		testCases.add(new CellParseTestCase("Andrew Dude level A FU V3", andrewDude, null, new AppointmentType[] { AppointmentType.LEVEL_A }));
		testCases.add(
				new CellParseTestCase(
						"Gawron Piotr BV + BG + neuro level A (FU)", piotrGawron, null,
						new AppointmentType[] { AppointmentType.LEVEL_A, AppointmentType.LEVEL_BV, AppointmentType.LEVEL_BG }));

		testCases.add(
				new CellParseTestCase(
						"Gawron Piotr level B © + level A neuro  ©", piotrGawron, null, new AppointmentType[] { AppointmentType.LEVEL_A, AppointmentType.LEVEL_B }));

		testCases.add(
				new CellParseTestCase(
						"Gawron Piotr level B ©  + BV © + SB ©", piotrGawron, null,
						new AppointmentType[] { AppointmentType.LEVEL_B, AppointmentType.LEVEL_BV, AppointmentType.LEVEL_SB }));

		testCases.add(
				new CellParseTestCase(
						"Gawron Piotr level B + BV + BG", piotrGawron, null,
						new AppointmentType[] { AppointmentType.LEVEL_B, AppointmentType.LEVEL_BV, AppointmentType.LEVEL_BG }));

		testCases.add(
				new CellParseTestCase("Gawron Piotr level B + BG", piotrGawron, null, new AppointmentType[] { AppointmentType.LEVEL_B, AppointmentType.LEVEL_BG }));
		testCases.add(
				new CellParseTestCase(
						"Gawron Piotr level B © + BV  © + SB ©", piotrGawron, null,
						new AppointmentType[] { AppointmentType.LEVEL_B, AppointmentType.LEVEL_BV, AppointmentType.LEVEL_SB }));
		testCases.add(
				new CellParseTestCase(
						"Gawron Piotr level BG + SB + M-Power", piotrGawron, null,
						new AppointmentType[] { AppointmentType.LEVEL_BG, AppointmentType.LEVEL_SB, AppointmentType.LEVEL_B_M_POWER }));

		testCases.add(
				new CellParseTestCase("Gawron Piotr level B + BV", piotrGawron, null, new AppointmentType[] { AppointmentType.LEVEL_B, AppointmentType.LEVEL_BV }));
		testCases.add(
				new CellParseTestCase(
						"Gawron Piotr level BG + SB + M-Power", piotrGawron, null,
						new AppointmentType[] { AppointmentType.LEVEL_BG, AppointmentType.LEVEL_SB, AppointmentType.LEVEL_B_M_POWER }));

	}

	@Test
	public void testExtractTime() {
		for (CellParseTestCase testCase : testCases) {
			String result = parser.extractTime(testCase.query);
			assertEquals("Invalid time parsed from query: " + testCase.query, testCase.time, result);
		}
	}

	@Test
	public void testRemoveTime() {
		for (CellParseTestCase testCase : testCases) {
			String result = parser.removeTime(testCase.query);
			if (testCase.time == null) {
				assertEquals("query after removing time should be the same for query: " + testCase.query, testCase.query, result);
			} else {
				assertFalse(testCase.query.equals(result));
			}
		}
	}

	@Test
	public void testRemoveTime2() {
		String result = parser.removeTime("09:00 John Doe level B");
		assertEquals("John Doe level B", result);
	}

	@Test
	public void testExtractAppointment() {
		String defaultTime = "23:55";
		for (CellParseTestCase testCase : testCases) {
			AppointmentEntry appointment = parser.parseAppointment(testCase.query, defaultTime);
			if (testCase.time != null) {
				assertEquals("Invalid time parsed from query: " + testCase.query, testCase.time, appointment.getTime());
			} else {
				assertEquals("Invalid time parsed from query (default value expected): " + testCase.query, defaultTime, appointment.getTime());
			}
			assertEquals("Invalid subject parsed from query: " + testCase.query, testCase.subject, appointment.getSubject());
			assertTrue(
					"Invalid type parsed from query: " + testCase.query + " expected: " + Arrays.asList(testCase.types) + "; found: " + appointment.getTypes(),
					equalTypes(Arrays.asList(testCase.types), appointment.getTypes()));
		}
	}

	private boolean equalTypes(List<AppointmentType> types, Collection<AppointmentType> types2) {
		for (AppointmentType type : types) {
			if (!types2.contains(type)) {
				return false;
			}
		}
		for (AppointmentType type : types2) {
			if (!types.contains(type)) {
				return false;
			}
		}

		return true;
	}

}