/**
 * 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.planif;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;

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

import com.planimod.core.ManagerTestCase;
import com.planimod.core.Position;
import com.planimod.core.Product;
import com.planimod.core.ProductionEvent;
import com.planimod.core.ShiftUtils;
import com.planimod.core.Task;
import com.planimod.core.Team;

/**
 * Test case for {@link Table}.
 * 
 * <ul>
 * <li>Position #1 is swappable</li>
 * <li>Position #2 is not-swappable</li>
 * </ul>
 * 
 * <h4>Production Events</h4>
 * <table border=1>
 * <tr>
 * <th>Day \ Shift</th>
 * <th>weekDay</th>
 * <th>weekEvening</th>
 * </tr>
 * <tr>
 * <td>mon</td>
 * <td>product#1</td>
 * <td>product#3</td>
 * </tr>
 * <tr>
 * <td>tue</td>
 * <td>product#1</td>
 * <td>product#3</td>
 * </tr>
 * <tr>
 * <td>wed</td>
 * <td>product#1</td>
 * <td></td>
 * </tr>
 * <tr>
 * <td>thu</td>
 * <td>product#2</td>
 * <td>product#3</td>
 * </tr>
 * </table>
 * 
 * <h4>Expected results</h4>
 * <table border=1>
 * <tr>
 * <th>Day \ Shift</th>
 * <th >weekDay</th>
 * <th >weekDayRep</th>
 * </tr>
 * <tr>
 * <td>mon</td>
 * <td>position#1</td>
 * <td>position#2</td>
 * </tr>
 * <tr>
 * <td></td>
 * <td>position#1</td>
 * <td>position#2</td>
 * </tr>
 * <tr>
 * <td></td>
 * <!-- Day -->
 * <td>position#2</td>
 * <td></td>
 * </tr>
 * <tr>
 * <td>tue</td>
 * <td>position#1</td>
 * <td>position#2</td>
 * </tr>
 * <tr>
 * <td></td>
 * <td>position#1</td>
 * <td>position#2</td>
 * </tr>
 * <tr>
 * <td></td>
 * <td>position#2</td>
 * <td></td>
 * </tr>
 * <tr>
 * <td>wed</td>
 * <td>position#1</td>
 * <td></td>
 * </tr>
 * <tr>
 * <td></td>
 * <td>position#1</td>
 * <td></td>
 * </tr>
 * <tr>
 * <td></td>
 * <td>position#2</td>
 * <td></td>
 * </tr>
 * <tr>
 * <td>thu</td>
 * <td>position#2</td>
 * <td>position#2</td>
 * </tr>
 * <tr>
 * <td></td>
 * <td>position#2</td>
 * <td>position#2</td>
 * </tr>
 * <tr>
 * <td></td>
 * <td>position#2</td>
 * <td></td>
 * </tr>
 * </table>
 * 
 * 
 * @author ikus060
 * 
 */
public class TableTest extends ManagerTestCase {

    private static final Double ONE = Double.valueOf(1);

    /**
     * Class holding all the shift event
     */
    protected ShiftUtils shifts;

    Task[] t;

    List<Task> tasks;

    Table table;

    /**
     * Create the required shifts.
     */
    @Before
    public void createTable() {
        shifts = new ShiftUtils();
        shifts.addShifts(managers);
        shifts.addExtendedShifts(managers);

        Position pos1 = addPosition(managers, null, "position#1", false, true);
        Position pos2 = addPosition(managers, null, "position#2", false, false);

        Product pro1 = addProduct(managers, "product1");
        addProductPosition(managers, pro1, pos1, 2);
        addProductPosition(managers, pro1, pos2, 1);

        Product pro2 = addProduct(managers, "product2");
        addProductPosition(managers, pro2, pos2, 3);

        Product pro3 = addProduct(managers, "product3");
        addProductPosition(managers, pro3, pos2, 2);

        ProductionEvent event1 = addProductionEvent(managers, pro1, shifts.monDay);
        ProductionEvent event2 = addProductionEvent(managers, pro3, shifts.monRep);
        ProductionEvent event3 = addProductionEvent(managers, pro1, shifts.tueDay);
        ProductionEvent event4 = addProductionEvent(managers, pro3, shifts.tueRep);
        ProductionEvent event5 = addProductionEvent(managers, pro1, shifts.wedDay);
        ProductionEvent event7 = addProductionEvent(managers, pro2, shifts.thuDay);
        ProductionEvent event8 = addProductionEvent(managers, pro3, shifts.thuRep);

        t = new Task[18];
        t[0] = addTask(managers, event1, pos1);
        t[1] = addTask(managers, event1, pos1);
        t[2] = addTask(managers, event1, pos2);
        t[3] = addTask(managers, event2, pos2);
        t[4] = addTask(managers, event2, pos2);
        t[5] = addTask(managers, event3, pos1);
        t[6] = addTask(managers, event3, pos1);
        t[7] = addTask(managers, event3, pos2);
        t[8] = addTask(managers, event4, pos2);
        t[9] = addTask(managers, event4, pos2);
        t[10] = addTask(managers, event5, pos1);
        t[11] = addTask(managers, event5, pos1);
        t[12] = addTask(managers, event5, pos2);
        t[13] = addTask(managers, event7, pos2);
        t[14] = addTask(managers, event7, pos2);
        t[15] = addTask(managers, event7, pos2);
        t[16] = addTask(managers, event8, pos2);
        t[17] = addTask(managers, event8, pos2);

        /*
         * Create the Table.
         */
        this.tasks = Arrays.asList(t);
        this.table = Table.create(shifts.getShifts(), tasks);

    }

    /**
     * test the function {@link Table#teamIntersect(List)}.
     */
    @Test
    public void testTeams() {
        Collection<Team> teams = table.teams();
        assertEquals(2, teams.size());
        assertTrue(teams.contains(shifts.weekDay));
        assertTrue(teams.contains(shifts.weekDayRep));
    }

    @Test
    public void testGl() {

        // Check size
        assertEquals(8, table.gl().size());

        // Check content of the gl
        List<List<Task>> expectedGls = Arrays.asList(
                Arrays.asList(t[0], t[1], t[2]),
                Arrays.asList(t[0], t[1], t[3], t[4]),
                Arrays.asList(t[5], t[6], t[7]),
                Arrays.asList(t[5], t[6], t[8], t[9]),
                Arrays.asList(t[10], t[11], t[12]),
                Arrays.asList(t[10], t[11]),
                Arrays.asList(t[13], t[14], t[15]),
                Arrays.asList(t[16], t[17]));
        for (List<Task> expectedGl : expectedGls) {
            Double[] coefs = new Double[tasks.size()];
            Arrays.fill(coefs, Double.valueOf(0));
            for (Task t : expectedGl) {
                coefs[tasks.indexOf(t)] = Double.valueOf(1);
            }

            assertTrue(table.gl().contains(Arrays.asList(coefs)));
        }

    }

    /**
     * Test the function {@link Table#glIntersectTask(int)} with swappable task.
     */
    @Test
    public void testGlIntersectTask_withSwappableTask() {

        // Check number of gls intersecting
        Collection<List<Integer>> gls = table.glIntersectTask(tasks.indexOf(t[1]));
        assertEquals(2, gls.size());

        // Check content of the gls.
        List<List<Task>> expectedGls = Arrays.asList(Arrays.asList(t[0], t[1], t[2]), Arrays.asList(t[0], t[1], t[3], t[4]));
        for (List<Task> expectedGl : expectedGls) {
            Double[] coefs = new Double[tasks.size()];
            Arrays.fill(coefs, Double.valueOf(0));
            for (Task t : expectedGl) {
                coefs[tasks.indexOf(t)] = Double.valueOf(1);
            }

            assertTrue(table.gl().contains(Arrays.asList(coefs)));
        }

    }

    /**
     * Test the function {@link Table#glIntersectTask(int)}.
     */
    @Test
    public void testGlIntersectTask_withNotSwappableTask() {

        // Check number of gls intersecting
        Collection<List<Integer>> gls = table.glIntersectTask(tasks.indexOf(t[2]));
        assertEquals(1, gls.size());

        // Check content of the gls.
        List<List<Task>> expectedGls = Arrays.asList(Arrays.asList(t[0], t[1], t[2]));
        for (List<Task> expectedGl : expectedGls) {
            Double[] coefs = new Double[tasks.size()];
            Arrays.fill(coefs, Double.valueOf(0));
            for (Task t : expectedGl) {
                coefs[tasks.indexOf(t)] = Double.valueOf(1);
            }
            assertTrue(table.gl().contains(Arrays.asList(coefs)));
        }

    }

    /**
     * Test the function {@link Table#kInTj(int, int)}
     */
    @Test
    public void testKInTj() {

        /*
         * Check team 1
         */
        for (Task k : Arrays.asList(t[0], t[1], t[2], t[5], t[6], t[7], t[10], t[11], t[12], t[13], t[14], t[15])) {
            assertTrue(table.kInTj(tasks.indexOf(k), table.indexOf(shifts.weekDay)));
        }
        for (Task k : Arrays.asList(t[3], t[4], t[8], t[9], t[16], t[17])) {
            assertTrue(!table.kInTj(tasks.indexOf(k), table.indexOf(shifts.weekDay)));
        }

        /*
         * Check team2
         */
        for (Task k : Arrays.asList(t[0], t[1], t[3], t[4], t[5], t[6], t[8], t[9], t[10], t[11], t[16], t[17])) {
            assertTrue(table.kInTj(tasks.indexOf(k), table.indexOf(shifts.weekDayRep)));
        }
        for (Task k : Arrays.asList(t[2], t[7], t[12], t[13], t[14], t[15])) {
            assertTrue(!table.kInTj(tasks.indexOf(k), table.indexOf(shifts.weekDayRep)));
        }

    }

    /**
     * Test the function {@link Table#kInRTj(int, int)}
     */
    @Test
    public void testKInRTj() {

        /*
         * Check team 1
         */
        for (Task k : Arrays.asList(t[0], t[1], t[2], t[5], t[6], t[7], t[10], t[11], t[12], t[13], t[14], t[15])) {
            assertTrue(table.kInRTj(tasks.indexOf(k), table.indexOf(shifts.weekDay)));
        }
        for (Task k : Arrays.asList(t[3], t[4], t[8], t[9], t[16], t[17])) {
            assertTrue(!table.kInRTj(tasks.indexOf(k), table.indexOf(shifts.weekDay)));
        }

        /*
         * Check team2
         */
        for (Task k : Arrays.asList(t[3], t[4], t[8], t[9], t[16], t[17])) {
            assertTrue(table.kInRTj(tasks.indexOf(k), table.indexOf(shifts.weekDayRep)));
        }
        for (Task k : Arrays.asList(t[0], t[1], t[2], t[5], t[6], t[7], t[10], t[11], t[12], t[13], t[14], t[15])) {
            assertTrue(!table.kInRTj(tasks.indexOf(k), table.indexOf(shifts.weekDayRep)));
        }

    }

    /**
     * Test the tj() function.
     */
    @Test
    public void testTj() {

        // Check number of Tj
        assertEquals(2, table.tj().size());

        // Check content of Tj
        // Check content of the gls.
        List<List<Task>> expectedTjs = Arrays.asList(
        // Team 1
                Arrays.asList(t[0], t[1], t[2], t[5], t[6], t[7], t[10], t[11], t[12], t[13], t[14], t[15]),
                // Team 2
                Arrays.asList(t[0], t[1], t[3], t[4], t[5], t[6], t[8], t[9], t[10], t[11], t[16], t[17]));
        for (List<Task> expectedTj : expectedTjs) {
            Double[] coefs = new Double[tasks.size()];
            Arrays.fill(coefs, Double.valueOf(0));
            for (Task t : expectedTj) {
                coefs[tasks.indexOf(t)] = Double.valueOf(1);
            }

            assertTrue(table.tj().contains(Arrays.asList(coefs)));
        }
    }

    /**
     * Test the rtj() function.
     */
    @Test
    public void testRTj() {

        // Check number of Tj
        assertEquals(2, table.rtj().size());

        // Check content of Tj
        // Check content of the gls.
        List<List<Task>> expectedTjs = Arrays.asList(
        // Team 1
                Arrays.asList(t[0], t[1], t[2], t[5], t[6], t[7], t[10], t[11], t[12], t[13], t[14], t[15]),
                // Team 2
                Arrays.asList(t[3], t[4], t[8], t[9], t[16], t[17]));
        for (List<Task> expectedTj : expectedTjs) {
            Double[] coefs = new Double[tasks.size()];
            Arrays.fill(coefs, Double.valueOf(0));
            for (Task t : expectedTj) {
                coefs[tasks.indexOf(t)] = Double.valueOf(1);
            }
            assertTrue(table.rtj().contains(Arrays.asList(coefs)));
        }
    }

}
