/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira.drafts
 */
package net.ranides.assira.collections.map.impl;

import java.util.Objects;
import net.ranides.assira.collections.map.IndexMap;
import net.ranides.assira.collections.map.IndexQuery;
import net.ranides.assira.generic.CompareUtils;
import org.junit.Test;
import static org.junit.Assert.*;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class CIndexTreeMapTest {
    
    @Test
    public void testSetIndex() {
        
        IndexMap<Person> db = new CIndexTreeMap<>();
        
        db.index("name", (a,b) -> CompareUtils.cmp(a.name, b.name));
        db.index("surname", (a,b) -> CompareUtils.cmp(a.surname, b.surname));
        db.index("age", (a,b) -> CompareUtils.cmp(a.age, b.age));
        
        putData(db);
        
        assertQueryEquals("[{Helene C 19}, {Helene C 23}]", db.find()
            .eq("name", new Person("Helene", "", 0))
        );
        assertQueryEquals("[{Tom B 23}, {Helene C 23}, {Andrew S 23}]", db.find()
            .eq("age", new Person("", "", 23))
        );
        assertQueryEquals("[{Helene C 23}]", db.find()
            .eq("name", new Person("Helene", "", 0))
            .eq("age", new Person("", "", 23))
        );
        assertQueryEquals("[{Peter A 17}, {Tom A 17}, {George T 17}]", db.find()
            .eq("age", new Person("", "", 17))
        );
        assertQueryEquals("[{Peter A 17}, {Tom A 17}]", db.find()
            .eq("surname", new Person("", "A", 0))
            .eq("age", new Person("", "", 17))
        );
        assertQueryEquals("[{Joe A 20}, {Tom B 23}, {Helene C 19}, {Helene C 23}, {Andrew S 23}]", db.find()
            .gt("age", new Person("", "", 17))
        );
        assertQueryEquals("[]", db.find()
            .gt("age", new Person("", "", 40))
        );
        assertQueryEquals("[{Peter A 17}, {Tom A 17}, {George T 17}]", db.find()
            .lt("age", new Person("", "", 18))
        );
        assertQueryEquals("[]", db.find()
            .lt("age", new Person("", "", 17))
        );
        assertQueryEquals("[{Helene C 19}]", db.find()
            .eq("name", new Person("Helene", "", 23))
            .lt("age", new Person("Helene", "", 23))
        );
    }

    @Test
    public void testMapIndex() {
        
        IndexMap<Person> db = new CIndexTreeMap<>();
        
        db.index("name", a -> a.name);
        db.index("surname", a -> a.surname);
        db.index("age", a -> a.age);
        
        putData(db);
        
        assertQueryEquals("[{Helene C 19}, {Helene C 23}]", db.find()
            .eq("name", "Helene")
        );
        assertQueryEquals("[{Tom B 23}, {Helene C 23}, {Andrew S 23}]", db.find()
            .eq("age", 23)
        );
        assertQueryEquals("[{Helene C 23}]", db.find()
            .eq("name", "Helene")
            .eq("age", 23)
        );
        assertQueryEquals("[{Peter A 17}, {Tom A 17}, {George T 17}]", db.find()
            .eq("age", 17)
        );
        assertQueryEquals("[{Peter A 17}, {Tom A 17}]", db.find()
            .eq("surname", "A")
            .eq("age", 17)
        );
        assertQueryEquals("[{Joe A 20}, {Tom B 23}, {Helene C 19}, {Helene C 23}, {Andrew S 23}]", db.find()
            .gt("age", 17)
        );
        assertQueryEquals("[]", db.find()
            .gt("age", 40)
        );
        assertQueryEquals("[{Peter A 17}, {Tom A 17}, {George T 17}]", db.find()
            .lt("age", 18)
        );
        assertQueryEquals("[]", db.find()
            .lt("age", 17)
        );
        assertQueryEquals("[{Helene C 19}]", db.find()
            .eq("name", "Helene")
            .lt("age", 23)
        );
    }
    
    private static void assertQueryEquals(String expected, IndexQuery<Person> query) {
        assertEquals(expected, query.sort().list().toString());
    }

    private static void putData(IndexMap<Person> db) {
        db.put(new Person("Tom", "A", 17));
        db.put(new Person("Tom", "B", 23));
        db.put(new Person("Joe", "A", 20));
        db.put(new Person("Helene", "C", 19));
        db.put(new Person("Andrew", "S", 23));
        db.put(new Person("Helene", "C", 23));
        db.put(new Person("George", "T", 17));
        db.put(new Person("Peter", "A", 17));
    }
    
    private static final class Person implements Comparable<Person> {
        
        public final String name;
        public final String surname;
        public final int age;

        public Person(String name, String surname, int age) {
            this.name = name;
            this.surname = surname;
            this.age = age;
        }

        @Override
        public String toString() {
            return "{" + name + " " + surname + " " + age + "}";
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 47 * hash + Objects.hashCode(this.name);
            hash = 47 * hash + Objects.hashCode(this.surname);
            hash = 47 * hash + this.age;
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            return this == obj || ((obj instanceof Person) && 0 == compareTo((Person) obj));
        }
        
        @Override
        public int compareTo(Person that) {
            int c;
            c = CompareUtils.cmp(this.surname, that.surname);
            if(c!=0) {
                return c;
            }
            c = CompareUtils.cmp(this.name, that.name);
            if(c!=0) {
                return c;
            }
            c = CompareUtils.cmp(this.age, that.age);
            return c;
        }
        
    }
    
}
