/**
 * Copyright (C) 2019 Patrik Dufresne Service Logiciel <info@patrikdufresne.com>
 *
 * 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 3 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package com.planimod.core;

import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.junit.Assert;
import org.junit.Test;

import com.patrikdufresne.managers.ManagerException;
import com.patrikdufresne.util.BidiMultiMap;
import com.planimod.core.Employee;
import com.planimod.core.Position;
import com.planimod.core.Qualification;
import com.planimod.core.Section;

/**
 * Test case for the qualification objects.
 * 
 * @author patapouf
 * 
 */
public class QualificationManagerTest extends ManagerTestCase {

    /**
     * Test adding a qualification.
     */
    @Test
    public void AddQualification() {
        // Create a new employee.
        Employee e = addEmployee(managers, null);

        // Create a new position.
        Position p = addPosition(managers, null, null, false);

        // Add a new qualification
        Qualification q = new Qualification();
        q.setPosition(p);
        q.setEmployee(e);
        try {
            managers.getQualificationManager().add(Arrays.asList(q));
        } catch (ManagerException e1) {
            fail("Error adding qualification", e1);
        }
    }

    /**
     * Test adding a qualification object without an employee.
     */
    @Test
    public void AddQualificationWithoutEmployee() {
        // Create a new position.
        Position p = addPosition(managers, null, null, false);

        // Add a new qualification
        Qualification q = new Qualification();
        q.setPosition(p);
        q.setEmployee(null);
        try {
            managers.getQualificationManager().add(Arrays.asList(q));
            Assert.fail("Error adding qualification without employee should fail.");
        } catch (ManagerException e1) {
            // Expected result
        }
    }

    /**
     * Test adding a qualification object without a position.
     */
    @Test
    public void AddQualificationWithoutPosition() {
        // Create a new employee.
        Employee e = addEmployee(managers, null);

        // Add a new qualification
        Qualification q = new Qualification();
        q.setPosition(null);
        q.setEmployee(e);
        try {
            managers.getQualificationManager().add(Arrays.asList(q));
            Assert.fail("Error adding qualification without a position should fail.");
        } catch (ManagerException e1) {
            // Expected result
        }
    }

    /**
     * Check if the qualification table is working properly.
     * 
     * @throws ManagerException
     */
    @Test
    public void GetQualificationTable() throws ManagerException {

        Employee emp1 = addEmployee(managers, "employee1");
        Employee emp2 = addEmployee(managers, "employee2");
        Employee emp3 = addEmployee(managers, "employee2");
        Section sec1 = addSection(managers, "Section");
        Position pos1 = addPosition(managers, sec1, "pos1", false);
        Position pos2 = addPosition(managers, sec1, "pos2", false);
        Position pos3 = addPosition(managers, sec1, "pos3", false);
        Position pos4 = addPosition(managers, sec1, "pos4", false);
        Position pos5 = addPosition(managers, sec1, "pos5", false);

        addQualification(managers, emp1, pos1, pos2, pos4);
        addQualification(managers, emp2, pos1, pos3);

        BidiMultiMap<Employee, Position> table = managers.getQualificationManager().getQualificationTableByPositions(
                Arrays.asList(pos1, pos2, pos3, pos4, pos5));

        assertEquals(5, table.size());
        assertEquals(2, table.keySet().size());
        assertEquals(4, table.valueSet().size());

        assertTrue(table.containsEntry(emp1, pos1));
        assertTrue(table.containsEntry(emp1, pos2));
        assertTrue(table.containsEntry(emp1, pos4));
        assertTrue(table.containsEntry(emp2, pos1));
        assertTrue(table.containsEntry(emp2, pos3));

        assertFalse(table.containsEntry(emp1, pos3));
        assertFalse(table.containsEntry(emp1, pos5));
        assertFalse(table.containsEntry(emp2, pos2));
        assertFalse(table.containsEntry(emp2, pos4));
        assertFalse(table.containsEntry(emp2, pos5));

    }

    /**
     * Check if the qualification table does not include the archived employee.
     * 
     * @throws ManagerException
     */
    @Test
    public void GetQualificationTable_WithArchivedEmployee() throws ManagerException {

        Employee emp1 = addEmployee(managers, "employee1");
        Employee emp2 = addEmployee(managers, "employee2");
        Employee emp3 = addEmployee(managers, "employee2");

        Section sec1 = addSection(managers, "Section");
        Position pos1 = addPosition(managers, sec1, "pos1", false);
        Position pos2 = addPosition(managers, sec1, "pos2", false);
        Position pos3 = addPosition(managers, sec1, "pos3", false);
        Position pos4 = addPosition(managers, sec1, "pos4", false);
        Position pos5 = addPosition(managers, sec1, "pos5", false);

        addQualification(managers, emp1, pos1, pos2, pos4);
        addQualification(managers, emp2, pos1, pos3);
        addQualification(managers, emp3, pos1, pos2);

        // Archive employee 3
        managers.archiveAll(Arrays.asList(emp3));

        // Query qualification
        BidiMultiMap<Employee, Position> table = managers.getQualificationManager().getQualificationTableByPositions(
                Arrays.asList(pos1, pos2, pos3, pos4, pos5));

        assertEquals(5, table.size());
        assertEquals(2, table.keySet().size());
        assertEquals(4, table.valueSet().size());

        assertTrue(table.containsEntry(emp1, pos1));
        assertTrue(table.containsEntry(emp1, pos2));
        assertTrue(table.containsEntry(emp1, pos4));
        assertTrue(table.containsEntry(emp2, pos1));
        assertTrue(table.containsEntry(emp2, pos3));

        assertFalse(table.containsEntry(emp1, pos3));
        assertFalse(table.containsEntry(emp1, pos5));
        assertFalse(table.containsEntry(emp2, pos2));
        assertFalse(table.containsEntry(emp2, pos4));
        assertFalse(table.containsEntry(emp2, pos5));

    }

    /**
     * Check if the qualification table is working properly.
     * 
     * @throws ManagerException
     */
    @Test
    public void GetQualificationTable_WithNoEmployee() throws ManagerException {

        Employee emp1 = addEmployee(managers, "employee1");
        Employee emp2 = addEmployee(managers, "employee2");
        Employee emp3 = addEmployee(managers, "employee2");
        Section sec1 = addSection(managers, "Section");
        Position pos1 = addPosition(managers, sec1, "pos1", false);
        Position pos2 = addPosition(managers, sec1, "pos2", false);
        Position pos3 = addPosition(managers, sec1, "pos3", false);
        Position pos4 = addPosition(managers, sec1, "pos4", false);
        Position pos5 = addPosition(managers, sec1, "pos5", false);

        addQualification(managers, emp1, pos1, pos2, pos4);
        addQualification(managers, emp2, pos1, pos3);

        BidiMultiMap<Employee, Position> table = managers.getQualificationManager().getQualificationTableByPositions(Collections.EMPTY_SET);
        assertEquals(0, table.size());

    }

    /**
     * Check if the listByPositionAndEmployee select the right qualification object.
     */
    @Test
    public void ListByPositionAndEmployee() {

        Employee emp1 = addEmployee(managers, "employee1");
        Employee emp2 = addEmployee(managers, "employee2");
        Section sec1 = addSection(managers, "Section");
        Position pos1 = addPosition(managers, sec1, "pos1", false);
        Position pos2 = addPosition(managers, sec1, "pos2", false);

        /* Qualification qualif11 = */addQualification(managers, emp1, pos1);
        /* Qualification qualif12 = */addQualification(managers, emp1, pos2);
        Qualification qualif21 = addQualification(managers, emp2, pos1);
        /* Qualification qualif22 = */addQualification(managers, emp2, pos2);

        Qualification q;
        try {
            q = managers.getQualificationManager().listByPositionAndEmployee(pos1, emp2);
        } catch (ManagerException e) {
            fail("Error while listing qualifications", e);
            return;
        }

        assertEquals("Wrong element", qualif21, q);

    }

    /**
     * Test remove a qualification.
     */
    @Test
    public void RemoveQualification() {
        // Create a new employee.
        Employee e = addEmployee(managers, null);
        // Create a new position.
        Position p = addPosition(managers, null, null, false);
        // Add a new qualification
        Qualification q = new Qualification();
        q.setPosition(p);
        q.setEmployee(e);
        try {
            managers.getQualificationManager().add(Arrays.asList(q));
        } catch (ManagerException e1) {
            fail("Error adding qualification", e1);
        }

        // Keep id for references
        int id = q.getId();
        // Remove qualifications
        try {
            managers.getQualificationManager().remove(Arrays.asList(q));
        } catch (ManagerException e1) {
            fail("Error removing object", e1);
        }

        // Check if removed
        try {
            List<Qualification> list = managers.getQualificationManager().list();

            for (Qualification q1 : list) {
                if (q1.getId() == id) {
                    Assert.fail("Object still exists");
                }
            }

        } catch (ManagerException e1) {
            fail("Error listing object", e1);
        }
    }

}
