package soot.util;

/*-
 * #%L
 * Soot - a J*va Optimization Framework
 * %%
 * Copyright (C) 1997 - 2018 Raja Vallée-Rai and others
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 2.1 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 Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
 * #L%
 */

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

import java.util.Arrays;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Queue;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class LargePriorityQueueTest {

  Integer[] universe1;
  Integer[] clone;
  Queue<Integer> q;
  
  @Before
  public void initUniverse() {
    universe1 = new Integer[MediumPriorityQueue.MAX_CAPACITY+64];
    for (int j = 0; j < universe1.length; j++)
      universe1[j] = j;
    
    clone = Arrays.copyOf(universe1, universe1.length);
  }

  @After
  public void postconditionUniverseNotModified() {
    assertArrayEquals(clone, universe1);
  }
  
  @After
  public void postconditionInstanceOfLargePriorityQueue() {
    assertTrue(q instanceof LargePriorityQueue);
  }
  
  @Test
  public void testDeleteFirst() {
    q = PriorityQueue.of(universe1);
    assertTrue(q.remove(universe1[0]));
    assertEquals(q.peek(), universe1[1]);
    assertEquals(q.poll(), universe1[1]);
  }

  @Test
  public void testNew() {
    q = PriorityQueue.of(universe1);
    assertEquals(universe1.length, q.size());
    assertFalse(q.isEmpty());
  }
  
  @Test
  public void testPollAll() {
    q = PriorityQueue.of(universe1);
    
    int i = 0;
    while (!q.isEmpty())
      assertEquals(universe1[i++], q.poll());
  } 

  @Test
  public void testPoll2() {
    q = PriorityQueue.noneOf(universe1);
    for (int i=0;i<universe1.length;i+=3)
      q.add(universe1[i]);

    int i = -3;
    while (!q.isEmpty()) {
      Object e = universe1[i+=3];
      assertEquals(e, q.peek());
      assertEquals(e, q.poll());
    }
  } 

  @Test
  public void testPeekPollAll() {
    q = PriorityQueue.of(universe1);
    
    while (!q.isEmpty())
      assertEquals(q.peek(), q.poll());
  } 
  
  
  @Test
  public void testOffer() {
    q = PriorityQueue.of(universe1);
    
    int i = 0;
    assertEquals(universe1[i++], q.poll());
    assertEquals(universe1[i++], q.poll());
    assertEquals(universe1[i++], q.poll());
    assertEquals(universe1[i++], q.poll());
    q.add(universe1[i=0]);
    assertEquals(universe1[i++], q.poll());
  } 
  
  @Test
  public void testMixedAddDelete() {
    q = PriorityQueue.noneOf(universe1);

    Integer z = universe1[0];
    Integer x = universe1[666];
    
    assertTrue(q.add(z));
    assertFalse(q.offer(z));
    assertTrue(q.contains(z));
    assertTrue(q.add(x));
    
    for (Integer i : universe1)
      assertEquals((i == z || i == x), q.contains(i));
    
    assertTrue(q.remove(z));    
    
    for (Integer i : universe1)
      assertEquals(i == x, q.contains(i));
    
    assertEquals(x, q.peek());
    assertEquals(x, q.poll());
  }
  

  @Test
  public void testOfferAlreadyInQueue() {
    q = PriorityQueue.of(universe1);
    for ( Integer i : universe1 ) {
      assertFalse(q.offer(i));
    }
  } 
  

  @Test
  public void testClear() {
    q = PriorityQueue.of(universe1);
    q.clear();
    assertEquals(q.size(), 0);
    assertTrue(q.isEmpty());
    assertNull(q.peek());
    assertNull(q.poll());
    for (Integer i : universe1) 
      assertFalse(q.contains(i));
  } 

  @Test(expected=NoSuchElementException.class)
  public void testOfferNotInUniverse() {
    q = PriorityQueue.of(universe1);
    q.offer(-999);
  }

  @Test(expected=NullPointerException.class)
  public void testOfferNull() {
    q = PriorityQueue.of(universe1);
    q.offer(null);
  }

  @Test
  public void testRemoveNull() {
    q = PriorityQueue.of(universe1);
    assertFalse(q.remove(null));
  }

  @Test
  public void testIteratorAll() {
    q = PriorityQueue.of(universe1);
    int j = 0;
    for (Integer i : q) {
      assertEquals(universe1[j++], i);
    }
  }

  @Test(expected=ConcurrentModificationException.class)
  public void testIteratorDelete() {
    q = PriorityQueue.of(universe1);
    int j = 0;
    for (Integer i : q) {
      assertEquals(universe1[j++], i);
      assertTrue(q.remove(universe1[universe1.length-1]));
    }
  } 

  @Test
  public void testIteratorRemove() {
    q = PriorityQueue.of(universe1);
    
    Iterator<Integer> it = q.iterator();
    
    while (it.hasNext()) {
      Integer i = it.next();
      assertTrue(q.contains(i));
      it.remove();
      assertFalse(q.contains(i));
    }
  } 
  
  @Test(expected=NoSuchElementException.class)
  public void testIteratorOutOfBounds() {
    q = PriorityQueue.of(universe1);
    
    Iterator<Integer> it = q.iterator();
    
    while (it.hasNext()) {
      it.next();
    }
    it.next();
  } 
  
  @Test(expected=IllegalStateException.class)
  public void testIteratorDoubleRemove() {
    q = PriorityQueue.of(universe1);
    
    Iterator<Integer> it = q.iterator();
    
    while (it.hasNext()) {
      it.next();
      it.remove();
      it.remove();
    }
  }   
  
  @Test(expected=IllegalStateException.class)
  public void testIteratorBeforeFirst() {
    q = PriorityQueue.of(universe1);
    
    Iterator<Integer> it = q.iterator();
    it.remove();
  } 
  


  @Test()
  public void testIteratorHole1() {
    q = PriorityQueue.of(universe1);
    int hole = 7;
    assertTrue(q.remove(universe1[hole]));
    assertFalse(q.contains(universe1[hole]));
    int j = 0;
    for (Integer i : q) {
      if (j==hole)j++;
      assertEquals(universe1[j++], i);
    }
  }
  

}
