package eu.dnetlib.data.utility.objectpackaging;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import javax.xml.ws.wsaddressing.W3CEndpointReference;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnit44Runner;

import eu.dnetlib.data.utility.objectpackaging.rmi.ObjectPackagingException;
import eu.dnetlib.enabling.resultset.rmi.ResultSetException;
import eu.dnetlib.test.utils.EPRTestUtil;

@RunWith(MockitoJUnit44Runner.class)
public class ObjectQueueTest {
	
	/**
	 * object queue factory.
	 */
	ObjectQueueFactory objectQueueFactory;
	
	W3CEndpointReference epr; 
	private String xpath_ID = "//id";
	
	@Mock
	ObjectProviderFactory objectProviderFactory;
	
	@Mock
	ObjectProvider objectProvider;
	
	/**
	 * instance under test.
	 */
	ObjectQueue objectQueue;
	
	@Before
	public void setUp() throws Exception {
		epr = EPRTestUtil.getTestEpr(); 

		when(objectProviderFactory.createObjectProvider(epr)).thenReturn(objectProvider);
		
		objectQueueFactory = new ObjectQueueFactory();
		objectQueueFactory.setObjectProviderFactory(objectProviderFactory);
	}

	@Test
	public void testObjectQueue() throws ResultSetException, ObjectPackagingException {
		when(objectProvider.getSize()).thenReturn(123);

		objectQueue = objectQueueFactory.createObjectQueue(epr, xpath_ID);
		
		assertNotNull("op is null", objectQueue);
		assertEquals("totalItems is not valid", 123, objectQueue.getTotalItems());
	}

	@Test
	public void testIsEmpty_TRUE() throws ObjectPackagingException {
		when(objectProvider.getSize()).thenReturn(0);
		
		objectQueue = objectQueueFactory.createObjectQueue(epr, xpath_ID);
		
		assertTrue("isEmpty should be TRUE", objectQueue.isEmpty()) ;
		assertEquals("totalItems is not valid", 0, objectQueue.getTotalItems());
	}
	
	@Test
	public void testIsEmpty_FALSE() throws ObjectPackagingException {
		when(objectProvider.getSize()).thenReturn(123);
		
		objectQueue = objectQueueFactory.createObjectQueue(epr, xpath_ID);
		
		assertFalse("isEmpty should be FALSE", objectQueue.isEmpty()) ;
		assertEquals("totalItems is not valid", 123, objectQueue.getTotalItems());
	}
	
	@Test
	public void testNextElement_Advanced() throws ObjectPackagingException {
		List<String> list1 = Arrays.asList("<id>01</id>|<id>02</id>|<id>03</id>|<id>04</id>|<id>05</id>|<id>06</id>|<id>07</id>|<id>08</id>|<id>09</id>|<id>10</id>".split("\\|"));
		List<String> list2 = Arrays.asList("<id>11</id>|<id>12</id>|<id>13</id>|<id>14</id>|<id>15</id>|<id>16</id>|<id>17</id>|<id>18</id>|<id>19</id>|<id>20</id>".split("\\|"));
		List<String> list3 = Arrays.asList("<id>21</id>|<id>22</id>|<id>23</id>|<id>24</id>|<id>25</id>|<id>26</id>|<id>27</id>|<id>28</id>|<id>29</id>|<id>30</id>".split("\\|"));
		List<String> list4 = Arrays.asList("<id>31</id>|<id>32</id>|<id>33</id>|<id>34</id>|<id>35</id>|<id>36</id>|<id>37</id>|<id>38</id>|<id>39</id>|<id>40</id>".split("\\|"));
		List<String> list5 = Arrays.asList("<id>41</id>|<id>42</id>|<id>43</id>|<id>44</id>|<id>45</id>|<id>46</id>|<id>47</id>".split("\\|"));
		
		when(objectProvider.getSize()).thenReturn(47);
		when(objectProvider.getElements(1, 10)).thenReturn(list1);
		when(objectProvider.getElements(11, 20)).thenReturn(list2);
		when(objectProvider.getElements(21, 30)).thenReturn(list3);
		when(objectProvider.getElements(31, 40)).thenReturn(list4);
		when(objectProvider.getElements(41, 47)).thenReturn(list5);
		
		objectQueue = objectQueueFactory.createObjectQueue(epr, xpath_ID);
		
		assertEquals(0, objectQueue.getGivenItems());
		assertEquals(47, objectQueue.getTotalItems());
		
		int i = 0;
		while (objectQueue.fetchNextElement() != null) { i += 1; }
		
		assertEquals(47, i);
		assertEquals(47, objectQueue.getGivenItems());
		assertEquals(47, objectQueue.getTotalItems());
		
		verify(objectProvider).getSize();
		verify(objectProvider).getElements(1,10);
		verify(objectProvider).getElements(11,20);
		verify(objectProvider).getElements(21,30);
		verify(objectProvider).getElements(31,40);
		verify(objectProvider).getElements(41,47);
	}
	
	@Test
	public void testNextElement_Simple() throws ObjectPackagingException {
		List<String> list1 = Arrays.asList("<id>01</id>|<id>02</id>|<id>03</id>|<id>04</id>".split("\\|"));

		when(objectProvider.getSize()).thenReturn(4);
		when(objectProvider.getElements(1, 4)).thenReturn(list1);
		
		objectQueue = objectQueueFactory.createObjectQueue(epr, xpath_ID);
		
		assertEquals(0, objectQueue.getGivenItems());
		assertEquals(4, objectQueue.getTotalItems());
		
		assertEquals(objectQueue.watchNextElementId(), objectQueue.watchNextElementId());
		assertEquals("01", objectQueue.watchNextElementId());
		assertEquals("<id>01</id>", objectQueue.fetchNextElement());

		assertEquals(objectQueue.watchNextElementId(), objectQueue.watchNextElementId());
		assertEquals("02", objectQueue.watchNextElementId());
		assertEquals("<id>02</id>", objectQueue.fetchNextElement());
		
		assertEquals(objectQueue.watchNextElementId(), objectQueue.watchNextElementId());
		assertEquals("03", objectQueue.watchNextElementId());
		assertEquals("<id>03</id>", objectQueue.fetchNextElement());
		
		assertEquals(objectQueue.watchNextElementId(), objectQueue.watchNextElementId());
		assertEquals("04", objectQueue.watchNextElementId());
		assertEquals("<id>04</id>", objectQueue.fetchNextElement());

		assertEquals(objectQueue.watchNextElementId(), objectQueue.watchNextElementId());
		assertNull(objectQueue.fetchNextElement());
		assertNull(objectQueue.watchNextElementId());

		assertEquals(4, objectQueue.getGivenItems());
		assertEquals(4, objectQueue.getTotalItems());
		
		verify(objectProvider).getSize();
		verify(objectProvider).getElements(1,4);
	}
	
	@Test
	public void testNextElement_NotSoSimple() throws ObjectPackagingException {
		String[] ids = {"abc-10329", "abc-1032", "abc-10327", "abc-10299"};
		Arrays.sort(ids);
		List<String> list1 = Arrays.asList(ids);
		System.out.println("expected order:");
		for (String id: list1){
			System.out.println(id);
		}
		List<String> list2 = new LinkedList<String>();
		for (String id: list1){
			list2.add("<id>" + id + "</id>");
		}
		for (String s: list2){
			System.out.println(s);
		}
		
		
		
		when(objectProvider.getSize()).thenReturn(4);
		when(objectProvider.getElements(1, 4)).thenReturn(list2);
		
		objectQueue = objectQueueFactory.createObjectQueue(epr, xpath_ID);
		
		assertEquals(0, objectQueue.getGivenItems());
		assertEquals(4, objectQueue.getTotalItems());
		
		assertEquals(objectQueue.watchNextElementId(), objectQueue.watchNextElementId());
		assertEquals("abc-10299", objectQueue.watchNextElementId());
		assertEquals("<id>abc-10299</id>", objectQueue.fetchNextElement());

		assertEquals(objectQueue.watchNextElementId(), objectQueue.watchNextElementId());
		assertEquals("abc-1032", objectQueue.watchNextElementId());
		assertEquals("<id>abc-1032</id>", objectQueue.fetchNextElement());
		
		assertEquals(objectQueue.watchNextElementId(), objectQueue.watchNextElementId());
		assertEquals("abc-10327", objectQueue.watchNextElementId());
		assertEquals("<id>abc-10327</id>", objectQueue.fetchNextElement());
		
		assertEquals(objectQueue.watchNextElementId(), objectQueue.watchNextElementId());
		assertEquals("abc-10329", objectQueue.watchNextElementId());
		assertEquals("<id>abc-10329</id>", objectQueue.fetchNextElement());

		assertEquals(objectQueue.watchNextElementId(), objectQueue.watchNextElementId());
		assertNull(objectQueue.fetchNextElement());
		assertNull(objectQueue.watchNextElementId()); 

		assertEquals(4, objectQueue.getGivenItems());
		assertEquals(4, objectQueue.getTotalItems());
		
		verify(objectProvider).getSize();
		verify(objectProvider).getElements(1,4);
	}

}
