import java.util.ArrayList;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.jdo.Transaction;

import org.junit.Test;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

import com.fiveorbs.shared.models.testtoremove.Player;
import com.fiveorbs.shared.models.testtoremove.Ranking;

/** TODO: remove datanucleus-* from classPath */
public class TestBasicPersistence extends BaseTest {

    @Test
    public void testBasicPlayerRankingPersistence() {
        PersistenceManager pm = this.newPersistenceManager();

        // Create the first instance. In our case, when the player creates an
        // account.
        final Player player = new Player("hop");
        final Player testPlayer = pm.makePersistent(player);
        final Player detached = pm.detachCopy(testPlayer);
        pm.close();

        assertNotNull("Player ID should not be null", detached.getId());

        pm = this.newPersistenceManager();

        // After the match, the player ranking is created or updated. An
        // alternative could possibly be to modify the existing ranking?
        final Ranking ranking = new Ranking(1000, 200);
        detached.setRanking(ranking);
        final Player testPlayer2 = pm.makePersistent(detached);
        final Player detached2 = pm.detachCopy(testPlayer2);
        pm.close();

        assertNotNull("testPlayer2 ID should not be null", detached2.getId());
    }

    @Test
    public void testBasicPlayerRankingPersistence2() {
        final Player player = new Player("Modnar");
        player.setRanking(new Ranking());

        final PersistenceManager pm = this.newPersistenceManager();
        pm.makePersistent(player);
        pm.close();
    }

    @Test
    public void testBasicPlayerRankingPersistence3() {
        PersistenceManager pm = this.newPersistenceManager();

        final Player player = new Player("hop");
        final Player testPlayer = pm.makePersistent(player);
        pm.close();

        assertNotNull("Player ID should not be null", testPlayer.getId());

        pm = this.newPersistenceManager();

        final Ranking ranking = new Ranking(1000, 200);
        testPlayer.setRanking(ranking);
        final Player testPlayer2 = pm.makePersistent(testPlayer);
        pm.close();

        assertNotNull("testPlayer2 ID should not be null", testPlayer2.getId());
    }

    @Test
    public void testLoadByQuery() {
        // Populate the DB
        PersistenceManager pm = this.newPersistenceManager();
        final Player player = new Player("Modnar");
        player.setRanking(new Ranking());
        pm.makePersistent(player);
        pm.close();

        // Load from the DB
        pm = this.newPersistenceManager();
        final Query query = pm.newQuery(Player.class);
        final List<Player> attached = (List<Player>) query.execute();
        final List<Player> result = new ArrayList<Player>(pm.detachCopyAll(attached));
        query.closeAll();
        pm.close();

        assertNotNull(result);
        assertNotNull(result.get(0).getRanking());
    }

    @Test
    public void testBasicRankingUpdate() {
        PersistenceManager pm = this.newPersistenceManager();

        // Create the first instance with a basic ranking
        final Player player = new Player("hop");
        final Ranking ranking = new Ranking(1000, 200);
        player.setRanking(ranking);
        final Player testPlayer = pm.makePersistent(player);
        final Player detached = pm.detachCopy(testPlayer);
        pm.close();

        // Check proper creation
        assertNotNull("Player ID should not be null", detached.getId());
        assertEquals("Incorrect ranking", Double.valueOf(1000), detached.getRanking().getMean());

        pm = this.newPersistenceManager();

        // After the match, the player ranking is updated
        final Ranking updatedRanking = new Ranking(1300, 186);
        detached.setRanking(updatedRanking);
        final Object[] testObjects = pm.makePersistentAll(detached);
        final Player testPlayer2 = (Player) testObjects[0];
        final Player detached2 = pm.detachCopy(testPlayer2);
        pm.close();

        assertNotNull("testPlayer2 ID should not be null", detached2.getId());
        assertEquals("Incorrect ranking", Double.valueOf(1300), detached.getRanking().getMean());
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testFullRankingUpdate() {
        PersistenceManager pm = this.newPersistenceManager();

        // Create the first instance with a basic ranking
        final Player player = new Player("hop");
        final Ranking ranking = new Ranking(1000, 200);
        player.setRanking(ranking);
        final Player testPlayer = pm.makePersistent(player);
        final Player detached = pm.detachCopy(testPlayer);
        pm.close();

        // Check proper creation
        assertNotNull("Player ID should not be null", detached.getId());
        assertEquals("Incorrect ranking", Double.valueOf(1000), detached.getRanking().getMean());

        pm = this.newPersistenceManager();

        final Transaction tx = pm.currentTransaction();
        tx.begin();
        // After the match, the player ranking is updated
        final Ranking updatedRanking = new Ranking(1300, 186);
        detached.setRanking(updatedRanking);
        final Object[] testObjects = pm.makePersistentAll(detached);
        final Player testPlayer2 = (Player) testObjects[0];
        final Player detached2 = pm.detachCopy(testPlayer2);
        tx.commit();
        pm.close();

        assertNotNull("testPlayer2 ID should not be null", detached2.getId());
        assertEquals("Incorrect ranking", Double.valueOf(1300), detached2.getRanking().getMean());

        // Check persistence of the ranking
        pm = this.newPersistenceManager();
        final String updatedRankingId = updatedRanking.getId();
        final Key k = KeyFactory.stringToKey(updatedRankingId);
        final Ranking rankingById = pm.getObjectById(Ranking.class, k);
        assertEquals("Incorrect ranking", Double.valueOf(1300), rankingById.getMean());
        pm.close();

        // Check with a load player by id
        final String playerId = detached2.getId();
        pm = this.newPersistenceManager();
        final Key k2 = KeyFactory.stringToKey(playerId);
        final Player playerById = pm.getObjectById(Player.class, k2);
        assertEquals("Incorrect ranking", Double.valueOf(1300), playerById.getRanking().getMean());
        pm.close();

        // And load it from the persistant store
        pm = this.newPersistenceManager();
        final Query query = pm.newQuery(Player.class);
        query.setFilter("name == nameParam");
        query.declareParameters("String nameParam");

        final List<Player> results = (List<Player>) query.execute("hop");
        assertNotNull(results);
        assertTrue(results.size() > 0);
        final Player attached = results.get(0);

        assertNotNull("ID should not be null", attached.getId());
        assertEquals("Incorrect ranking", Double.valueOf(1300), attached.getRanking().getMean());

        final Player result = pm.detachCopy(attached);

        assertNotNull("ID should not be null", result.getId());
        assertEquals("Incorrect ranking", Double.valueOf(1300), result.getRanking().getMean());
    }
}
