/**
 * 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 java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;

import com.patrikdufresne.managers.H2DBConfigurations;
import com.patrikdufresne.managers.IManagerObserver;
import com.patrikdufresne.managers.ManagerEvent;
import com.patrikdufresne.managers.ManagerException;
import com.planimod.core.planif.GeneratePlanifContext;
import com.planimod.core.planif.MockGeneratePlanifMonitor;

public abstract class ManagerTestCase {

    final static class PlanifEventPredicate implements Predicate {

        private Employee[] emp;
        private Date end;
        private Position[] pos;
        private ProductionEvent[] prodEvent;
        private Date start;

        /**
         * Create a new predicate.
         * 
         * @param prodEvent
         * @param start
         * @param end
         * @param emp
         * @param pos
         */
        private PlanifEventPredicate(ProductionEvent prodEvent, Date start, Date end, Employee emp, Position pos) {
            this(prodEvent, start, end, pos, emp);
        }

        /**
         * Create a new predicate.
         * 
         * @param prodEvent
         * @param start
         * @param end
         * @param emp
         * @param empOperator
         * @param pos
         */
        public PlanifEventPredicate(ProductionEvent prodEvent, Date start, Date end, Position pos, Employee... emp) {
            this.prodEvent = new ProductionEvent[] { prodEvent };
            this.emp = emp;
            this.pos = new Position[] { pos };
            this.start = start;
            this.end = end;
        }

        /**
         * Create a new predicate
         * 
         * @param prodEvent
         * @param emp
         * @param pos
         */
        public PlanifEventPredicate(ProductionEvent prodEvent, Employee emp, Position pos) {
            this(prodEvent, prodEvent.getShift().getStartDate(), prodEvent.getShift().getEndDate(), emp, pos);
        }

        public PlanifEventPredicate(ProductionEvent prodEvent, Position pos, Employee... emp) {
            this(prodEvent, prodEvent.getShift().getStartDate(), prodEvent.getShift().getEndDate(), pos, emp);
        }

        public PlanifEventPredicate(ProductionEvent[] prodEvent, Date start, Date end, Position[] pos, Employee[] emp) {
            this.prodEvent = prodEvent;
            this.emp = emp;
            this.pos = pos;
            this.start = start;
            this.end = end;
        }

        private boolean equals(Object o1, Object o2) {
            if (o1 == null && o2 == null) {
                return true;
            } else if (o1 == null || o2 == null) {
                return false;
            }
            return o1.equals(o2);
        }

        @Override
        public boolean evaluate(Object o1) {
            if (!(o1 instanceof Task)) return false;
            Task e = (Task) o1;
            // Check prodEvent
            int i = 0;
            while (i < prodEvent.length && !equals(e.getProductionEvent(), prodEvent[i])) {
                i++;
            }
            if (i >= prodEvent.length) {
                return false;
            }
            // Check position
            if (pos != null) {
                i = 0;
                while (i < pos.length && !equals(e.getPosition(), pos[i])) {
                    i++;
                }
                if (i >= pos.length) {
                    return false;
                }
            }
            if (!(start == null || e.getStartDate().getTime() == start.getTime()) || !(end == null || e.getEndDate().getTime() == end.getTime())) {
                return false;
            }
            // Check employee
            if (emp != null) {
                i = 0;
                while (i < emp.length && !equals(e.getEmployee(), emp[i])) {
                    i++;
                }
                return i < emp.length;
            }
            return true;
        }
    }

    /**
     * Add an employee to the database and return the newly created object.
     * 
     * @return the newly created employee or null if it failed.
     */
    public static Employee addEmployee(PlanimodManagers managers, String name) {

        Employee employee = new Employee();
        employee.setFirstname(name);
        employee.setHireDate(null);
        try {
            managers.getEmployeeManager().add(Arrays.asList((employee)));
        } catch (ManagerException e) {
            fail("Fail creating employee object", e);
        }
        return employee;
    }

    public static Employee addEmployee(PlanimodManagers managers, String name, Date hireDate) {
        Employee employee = new Employee();
        employee.setFirstname(name);
        employee.setHireDate(hireDate);
        try {
            managers.getEmployeeManager().add(Arrays.asList((employee)));
        } catch (ManagerException e) {
            fail("Fail creating employee object", e);
        }
        return employee;
    }

    public static Employee addEmployee(PlanimodManagers managers, String refId, String firstname, String lastname, Date hireDate) {
        Employee employee = new Employee();
        employee.setRefId(refId);
        employee.setFirstname(firstname);
        employee.setLastname(lastname);
        employee.setHireDate(hireDate);
        try {
            managers.getEmployeeManager().add(Arrays.asList((employee)));
        } catch (ManagerException e) {
            fail("Fail creating employee object", e);
        }
        return employee;
    }

    public static NonAvailability addNonAvailability(PlanimodManagers managers, Employee employee, Date start, Date end) {

        // Add an event entry
        NonAvailability event = new NonAvailability();
        event.setEmployee(employee);
        event.setStartDate(start);
        event.setEndDate(end);

        try {
            managers.getNonAvailabilityManager().add(Arrays.asList(event));
        } catch (ManagerException e) {
            fail("Error creating the non-availability event.", e);
        }

        return event;
    }

    /**
     * Add a position to the database and return the newly created object.
     * 
     * @param managers
     * @param section
     *            a section or null.
     * @return
     */
    public static Position addPosition(PlanimodManagers managers, Section section, String name, boolean classified) {
        return addPosition(managers, section, name, classified, false);
    }

    public static Position addPosition(PlanimodManagers managers, Section section, String name, boolean classified, boolean swappable) {
        // Add a section
        if (section == null) {
            section = addSection(managers, "Section");
        }

        // Add a position
        Position p = new Position();
        p.setSection(section);
        p.setName(name);
        p.setClassified(classified);
        p.setSwappable(swappable);
        try {
            managers.getPositionManager().add(Arrays.asList(p));
        } catch (ManagerException e) {
            fail("Fail adding position", e);
        }
        return p;
    }

    /**
     * Add a product to the database and return the newly create object.
     * 
     * @return
     */
    public static Product addProduct(PlanimodManagers managers, String name) {
        return addProduct(managers, null, name, null);
    }

    public static Product addProduct(PlanimodManagers managers, String refId, String name, String familly) {
        Product p = new Product();
        if (name == null) {
            name = "default";
        }
        p.setName(name);
        if (refId != null) {
            p.setRefId(refId);
        }
        if (familly != null) {
            p.setFamily(familly);
        }
        try {
            managers.getProductManager().add(Arrays.asList(p));
        } catch (ManagerException e) {
            fail("Fail adding product", e);
        }
        return p;
    }

    /**
     * Add a new production event to the database.
     * 
     * @param managers
     *            the managers
     * @param planif
     *            the planifciation (or the calendar entry)
     * @param product
     *            the product associated with the event
     * @param start
     *            the start time
     * @param end
     *            the end time
     * @return
     */
    public static ProductionEvent addProductionEvent(PlanimodManagers managers, Product product, Shift shiftEvent) {
        ProductionEvent event = new ProductionEvent();
        event.setShift(shiftEvent);
        event.setProduct(product);
        try {
            managers.getProductionEventManager().add(Arrays.asList(event));
        } catch (ManagerException e) {
            fail("Fail adding production event", e);
        }
        return event;
    }

    /**
     * Add a new ProductPosition to the database.
     * 
     * @param managers
     *            the managers
     * @param product
     *            the product
     * @param position
     *            the position
     * @return the product position
     */
    public static ProductPosition addProductPosition(PlanimodManagers managers, Product product, Position position, int number) {
        ProductPosition proPos = new ProductPosition();
        proPos.setProduct(product);
        proPos.setPosition(position);
        proPos.setNumber(number);
        try {
            managers.getProductPositionManager().add(Arrays.asList(proPos));
        } catch (ManagerException e) {
            fail("Fail adding product position", e);
        }
        return proPos;
    }

    /**
     * Add multiple product position for each given position.
     * 
     * @param managers
     * @param product
     * @param positions
     * @return
     */
    public static void addProductPosition(PlanimodManagers managers, Product product, Position... positions) {
        Map<Position, Integer> table = new HashMap<Position, Integer>();
        for (Position pos : positions) {
            Integer count = table.get(pos);
            table.put(pos, Integer.valueOf(count != null ? count.intValue() + 1 : 1));
        }

        List<ProductPosition> list = new ArrayList<ProductPosition>();
        for (Entry<Position, Integer> e : table.entrySet()) {
            ProductPosition proPos = new ProductPosition();
            proPos.setProduct(product);
            proPos.setPosition(e.getKey());
            proPos.setNumber(e.getValue());
            list.add(proPos);
        }

        try {
            managers.getProductPositionManager().add(list);
        } catch (ManagerException e) {
            fail("Fail adding product position", e);
        }

    }

    /**
     * Add qualification to an employee.
     * 
     * @param managers
     * @param employee
     *            the employee
     * @param position
     *            the new qualification
     * @return the qualification object
     */
    public static void addQualification(PlanimodManagers managers, Employee employee, Position... positions) {

        for (Position position : positions) {
            addQualification(managers, employee, position);
        }

    }

    public static Qualification addQualification(PlanimodManagers managers, Employee employee, Position position) {
        Qualification qualif = new Qualification();
        qualif.setEmployee(employee);
        qualif.setPosition(position);
        try {
            managers.getQualificationManager().add(Arrays.asList(qualif));
        } catch (ManagerException e) {
            fail("Fail adding qualification", e);
        }
        return qualif;
    }

    /**
     * Add a section to the database and return the newly created object.
     * 
     * @param managers
     * @return
     */
    public static Section addSection(PlanimodManagers managers, String name) {
        Section section = new Section();
        section.setName(name);
        try {
            managers.getSectionManager().add(Arrays.asList((section)));
        } catch (ManagerException e) {
            fail("Fail adding section", e);
        }
        return section;
    }

    /**
     * Add a calendar event to a calendar entry.
     * 
     * @param managers
     *            the managers
     * @param entry
     *            the calendar entry
     * @param start
     *            the start date of the new event
     * @param end
     *            the end date of the new event
     * @return the newly created event
     */
    public static Shift addShift(PlanimodManagers managers, Team team, Date start, Date end) {

        Shift event = new Shift();
        event.setTeam(team);
        event.setStartDate(start);
        event.setEndDate(end);

        try {
            managers.getShiftManager().add(Arrays.asList(event));
        } catch (ManagerException e) {
            fail("error creating a calendar event.", e);
            return null;
        }
        return event;
    }

    public static Task addTask(PlanimodManagers managers, ProductionEvent prod, Position position) {
        return addTask(managers, prod, position, null, null);
    }

    /**
     * Create a locked task.
     * 
     * @param managers
     * @param produit
     * @param shift
     * @param position
     * @param employee
     * @return
     * @throws ManagerException
     */
    public static void setTaskLocked(PlanimodManagers managers, final Product produit, final Shift shift, final Position position, Employee employee)
            throws ManagerException {

        // Find the production event
        Collection<ProductionEvent> events = (Collection<ProductionEvent>) CollectionUtils
                .select(managers.getProductionEventManager().listByShift(shift), new Predicate() {
                    @Override
                    public boolean evaluate(Object object) {
                        ProductionEvent e = (ProductionEvent) object;
                        return e.getShift().equals(shift) && e.getProduct().equals(produit);
                    }
                });
        if (events.size() == 0) {
            Assert.fail("Can't find a production event.");
        }

        // Find a tasks
        Collection<Task> tasks = (Collection<Task>) CollectionUtils.select(managers.getTaskManager().listByProductionEvents(events), new Predicate() {
            @Override
            public boolean evaluate(Object object) {
                Task t = (Task) object;
                return t.getLocked() == false && t.getPosition().equals(position);
            }
        });

        if (tasks.size() == 0) {
            Assert.fail("Can't find a production event.");
        }

        Task task = tasks.iterator().next();
        task.setLocked(true);
        task.setEmployee(employee);
        managers.getTaskManager().update(Arrays.asList(task));

    }

    public static Task addTask(PlanimodManagers managers, ProductionEvent prod, Position position, Date start, Date end) {
        Task event = new Task();
        event.setStartDate(start != null ? start : prod.getShift().getStartDate());
        event.setEndDate(end != null ? end : prod.getShift().getEndDate());
        event.setProductionEvent(prod);
        event.setPosition(position);

        try {
            managers.getTaskManager().add(Arrays.asList(event));
        } catch (ManagerException e) {
            fail("Fail to create the planif event", e);
        }
        return event;
    }

    /**
     * Add a shift to the database and return the newly create object.
     * 
     * @param managers
     * @return
     */
    public static Team addTeam(PlanimodManagers managers, String name) {
        Team shift = new Team();
        shift.setName(name);
        try {
            managers.getTeamManager().add(Arrays.asList((shift)));
        } catch (ManagerException e) {
            fail("Fail adding shift", e);
        }
        return shift;
    }

    public static void assertAssignment(
            int min,
            int max,
            Collection<Task> events,
            Collection<ProductionEvent> prodEvent,
            Date start,
            Date end,
            Collection<Position> pos,
            Collection<Employee> emp) {
        ProductionEvent[] a1 = new ProductionEvent[prodEvent.size()];

        int count = CollectionUtils
                .countMatches(
                        events,
                        new PlanifEventPredicate(
                                prodEvent.toArray(a1),
                                start,
                                end,
                                pos != null ? pos.toArray(new Position[pos.size()]) : null,
                                emp != null ? emp.toArray(new Employee[emp.size()]) : null));
        if (count < min || count > max) {
            StringBuilder buf = new StringBuilder();
            if (prodEvent.size() > 1) {
                buf.append(prodEvent);
            } else {
                buf.append(prodEvent.iterator().next());
            }
            buf.append("@");
            buf.append("[");
            boolean first = true;
            if (emp != null) {
                for (Employee e : emp) {
                    if (first) {
                        first = false;
                    } else {
                        buf.append(", ");
                    }
                    boolean hasName = false;
                    if (e != null && e.getFirstname() != null) {
                        buf.append(e.getFirstname());
                        hasName = true;
                    }
                    if (e != null && e.getLastname() != null) {
                        buf.append(e.getLastname());
                        hasName = true;
                    }
                    if (!hasName) {
                        buf.append("employee id=");
                        buf.append(e != null ? e.getId() : e);
                    }
                }
            }
            buf.append("]");
            buf.append(" expected=");
            if (min != max) {
                buf.append("[");
                buf.append(min);
                buf.append("..");
                buf.append(max);
                buf.append("]");
            } else {
                buf.append(min);
            }
            buf.append(" actual=");
            buf.append(count);
            Assert.fail(buf.toString());
        }
    }

    public static void assertAssignment(
            int min,
            int max,
            Collection<Task> events,
            Collection<ProductionEvent> prodEvent,
            Collection<Position> pos,
            Employee emp) {
        assertAssignment(min, max, events, prodEvent, pos, Arrays.asList(emp));
    }

    public static void assertAssignment(
            int min,
            int max,
            Collection<Task> events,
            Collection<ProductionEvent> prodEvent,
            Collection<Position> pos,
            Collection<Employee> emp) {
        assertAssignment(min, max, events, prodEvent, null, null, pos, emp);
    }

    public static void assertAssignment(int min, int max, Collection<Task> events, Collection<ProductionEvent> prodEvent, Position pos, Employee emp) {
        assertAssignment(min, max, events, prodEvent, pos != null ? Arrays.asList(pos) : null, Arrays.asList(emp));
    }

    public static void assertAssignment(int expected, Collection<Task> events, Collection<ProductionEvent> prodEvent, Collection<Position> pos, Employee emp) {
        assertAssignment(expected, events, prodEvent, pos, Arrays.asList(emp));
    }

    public static void assertAssignment(
            int expected,
            Collection<Task> events,
            Collection<ProductionEvent> prodEvent,
            Collection<Position> pos,
            Collection<Employee> emp) {
        assertAssignment(expected, expected, events, prodEvent, null, null, pos, emp);
    }

    public static void assertAssignment(int expected, Collection<Task> events, Collection<ProductionEvent> prodEvent, Position pos, Employee emp) {
        assertAssignment(expected, events, prodEvent, pos != null ? Arrays.asList(pos) : null, Arrays.asList(emp));
    }

    public static void assertAssignment(int expected, Collection<Task> events, Collection<ProductionEvent> prodEvent, Position pos) {
        assertAssignment(expected, events, prodEvent, pos != null ? Arrays.asList(pos) : null, (Collection<Employee>) null);
    }

    public static void assertAssignment(int expected, Collection<Task> events, Collection<ProductionEvent> prodEvent, Position pos, Collection<Employee> emp) {
        assertAssignment(expected, expected, events, prodEvent, null, null, Arrays.asList(pos), emp);
    }

    public static void assertAssignment(int expected, Collection<Task> events, ProductionEvent prodEvent, Collection<Position> pos, Employee emp) {
        assertAssignment(expected, expected, events, Arrays.asList(prodEvent), null, null, pos, Arrays.asList(emp));
    }

    public static void assertAssignment(int expected, Collection<Task> events, ProductionEvent prodEvent, Collection<Position> pos, Collection<Employee> emp) {
        assertAssignment(expected, expected, events, Arrays.asList(prodEvent), null, null, pos, emp);
    }

    public static void assertAssignment(int min, int max, Collection<Task> events, ProductionEvent prodEvent, Position pos, Employee emp) {
        assertAssignment(min, max, events, Arrays.asList(prodEvent), Arrays.asList(pos), Arrays.asList(emp));
    }

    public static void assertAssignment(int expected, Collection<Task> events, ProductionEvent prodEvent, Position pos, Employee emp) {
        assertAssignment(expected, events, Arrays.asList(prodEvent), Arrays.asList(pos), Arrays.asList(emp));
    }

    public static void assertAssignment(int expected, Collection<Task> events, ProductionEvent prodEvent, Position pos) {
        assertAssignment(expected, events, Arrays.asList(prodEvent), Arrays.asList(pos), (Collection) null);
    }

    public static void assertAssignment(int expected, Collection<Task> events, ProductionEvent prodEvent, Position pos, Collection<Employee> emp) {
        assertAssignment(expected, events, Arrays.asList(prodEvent), Arrays.asList(pos), emp);
    }

    public static void assertAssignment(Collection<Task> events, Collection<ProductionEvent> prodEvent, Position pos, Employee emp) {
        assertAssignment(1, events, prodEvent, Arrays.asList(pos), Arrays.asList(emp));
    }

    public static void assertAssignment(Collection<Task> events, ProductionEvent prodEvent, Date start, Date end, Position pos, Employee emp) {
        assertAssignment(1, 1, events, Arrays.asList(prodEvent), start, end, Arrays.asList(pos), Arrays.asList(emp));
    }

    public static void assertAssignment(Collection<Task> events, ProductionEvent prodEvent, Date start, Date end, Position pos, Collection<Employee> emp) {
        assertAssignment(1, 1, events, Arrays.asList(prodEvent), start, end, Arrays.asList(pos), emp);
    }

    public static void assertAssignment(Collection<Task> events, ProductionEvent prodEvent, Collection<Position> pos, Employee emp) {
        assertAssignment(1, events, Arrays.asList(prodEvent), pos, Arrays.asList(emp));
    }

    public static void assertAssignment(Collection<Task> events, ProductionEvent prodEvent, Position pos, Employee emp) {
        assertAssignment(1, events, Arrays.asList(prodEvent), Arrays.asList(pos), Arrays.asList(emp));
    }

    public static void assertAssignment(Collection<Task> events, ProductionEvent prodEvent, Position pos, Collection<Employee> emp) {
        assertAssignment(1, events, Arrays.asList(prodEvent), Arrays.asList(pos), emp);
    }

    /**
     * Assert the given manager event exists.
     * 
     * @param managerEvents
     *            the collection of manager events.
     * @param eventType
     *            the vent type
     * @param entity
     *            the entity object
     */
    public static void assertManagerEvent(int expected, List<ManagerEvent> managerEvents, final int eventType, final Object entity) {
        int count = CollectionUtils.countMatches(managerEvents, new Predicate() {
            @Override
            public boolean evaluate(Object object) {
                return object instanceof ManagerEvent
                        && ((ManagerEvent) object).type == eventType
                        && (entity == null || ((ManagerEvent) object).objects.contains(entity));
            }
        });
        Assert.assertEquals("Wrong number of manaver event.", expected, count);
    }

    /**
     * Fail with exception display
     * 
     * @param message
     * @param e
     */
    public static void fail(String message, Exception e) {
        StringWriter writer = new StringWriter();
        PrintWriter pw = new PrintWriter(writer);
        e.printStackTrace(pw);
        pw.flush();
        Assert.fail(message + "\r\n" + writer.toString());
    }

    public static GeneratePlanifContext generatePlanif(PlanimodManagers managers, Date date) throws ManagerException {
        // Generate the planif
        GeneratePlanifContext context;
        context = managers.getTaskManager().createGeneratePlanifContext();
        context.setWeek(date);
        context.searchSolution(new MockGeneratePlanifMonitor());
        return context;
    }

    public static void setPreferredPosition(PlanimodManagers managers, Employee employee, Position preferredPosition, Team prefferedPositionShift) {
        boolean toUpdate = true;
        EmployeePreference pref = null;
        try {
            pref = managers.getEmployeePreferenceManager().getByEmployee(employee);
        } catch (ManagerException e) {
            fail("Error selecting the existing employee preferences", e);
        }
        if (pref == null) {
            pref = new EmployeePreference();
            pref.setEmployee(employee);
            toUpdate = false;
        }
        pref.setPreferredPosition(preferredPosition);
        pref.setPreferredPositionTeam(prefferedPositionShift);
        try {
            if (toUpdate) {
                managers.getEmployeePreferenceManager().update(Arrays.asList(pref));
            } else {
                managers.getEmployeePreferenceManager().add(Arrays.asList(pref));
            }

        } catch (ManagerException e) {
            fail("Error setting the employee preference.", e);
        }
    }

    public static void setEmployeePreference(
            PlanimodManagers managers,
            Employee employee,
            Position preferredPosition,
            Team prefferedPositionShift,
            Section section,
            List<Team> teams)
            throws ManagerException {

        boolean toUpdate = true;
        EmployeePreference pref = null;
        try {
            pref = managers.getEmployeePreferenceManager().getByEmployee(employee);
        } catch (ManagerException e) {
            fail("Error selecting the existing employee preferences", e);
        }
        if (pref == null) {
            pref = new EmployeePreference();
            toUpdate = false;
        }
        pref.setEmployee(employee);
        pref.setPreferredPosition(preferredPosition);
        pref.setPreferredPositionTeam(prefferedPositionShift);
        pref.setPreferredSection(section);
        pref.setPreferredTeam(teams);
        if (toUpdate) {
            managers.getEmployeePreferenceManager().update(Arrays.asList(pref));
        } else {
            managers.getEmployeePreferenceManager().add(Arrays.asList(pref));
        }

    }

    public static void setEmployeePreferredSeniority(PlanimodManagers managers, Employee employee, boolean preferencialSeniority) {

        employee.setPreferencialSeniority(preferencialSeniority);

        try {
            managers.getEmployeeManager().update(Arrays.asList(employee));
        } catch (ManagerException e) {
            fail("Error setting the employee seniority.", e);
        }

    }

    /**
     * Sets the first day of week
     * 
     * @param managers
     * @param date
     */
    public static void setFirstDayOfWeek(PlanimodManagers managers, int day) {
        try {
            managers.getApplicationSettingManager().setFirstDayOfWeek(day);
        } catch (ManagerException e) {
            fail("Fail setting first day of week", e);
        }
    }

    public static void setHireDate(PlanimodManagers managers, Employee employee, Date date) {
        employee.setHireDate(date);
        try {
            managers.getEmployeeManager().update(Arrays.asList((employee)));
        } catch (ManagerException e) {
            fail("Fail updating the hire date", e);
        }
    }

    /**
     * Sets the planif events with a given employee.
     * 
     * @param managers
     *            the managers to use
     * @param event
     *            the event to update
     * @param employee
     *            the employee value
     */
    public static void setTaskEmployee(PlanimodManagers managers, Task event, Employee employee) {
        event.setEmployee(employee);
        try {
            managers.getTaskManager().update(Arrays.asList(event));
        } catch (ManagerException e) {
            fail("Error setting the planif event employee.", e);
        }
    }

    /**
     * Sets the planif event to locked with the given employee.
     * 
     * @param managers
     *            the manager to use
     * @param event
     *            the planif event to be locked
     * @param employee
     *            the employee value
     */
    public static void setTaskLocked(PlanimodManagers managers, Task event, Employee employee) {
        event.setEmployee(employee);
        event.setLocked(true);
        try {
            managers.getTaskManager().update(Arrays.asList(event));
        } catch (ManagerException e) {
            fail("Error setting the planif event in locked state.", e);
        }
    }

    private boolean clearDb;

    private List<ManagerEvent> managerEvents;

    private IManagerObserver managerObserver;

    protected PlanimodManagers managers;

    private String name;

    private boolean createNewDb;

    public ManagerTestCase() {
        this("./unittest", true, true);
    }

    /**
     * Create a new test case.
     * 
     * @param name
     *            the database filename to open
     * @param createNewDb
     *            True to delete the existing database.
     * @param clearDb
     *            True to delete the database after the test.
     */
    public ManagerTestCase(String name, boolean createNewDb, boolean clearDb) {
        this.name = name;
        this.createNewDb = createNewDb;
        this.clearDb = clearDb;
    }

    public void addManagerObserver(int eventType, Class cls) {

        this.managers.addObserver(eventType, cls, getManagerObserver());
    }

    @After
    public void clearDatabase() throws Exception {
        if (managers != null) {
            managers.dispose();
        }
    }

    public void clearEvents() {
        if (this.managerEvents != null) {
            this.managerEvents.clear();
        }
    }

    @Before
    public void createDatabase() throws Exception {
        String url = this.name;
        if (clearDb) {
            // If the database is cleared, create it in memory
            url = "jdbc:h2:mem:" + this.name;
        }
        managers = new PlanimodManagers(H2DBConfigurations.create(url, createNewDb, false));
    }

    /**
     * Return the received events.
     * 
     * @return
     */
    public List<ManagerEvent> getManagerEvents() {
        if (managerEvents == null) {
            return Collections.EMPTY_LIST;
        }
        return this.managerEvents;
    }

    /**
     * Return the manager observer.
     * 
     * @return
     */
    public IManagerObserver getManagerObserver() {
        if (this.managerObserver == null) {
            this.managerObserver = new IManagerObserver() {
                @Override
                public void handleManagerEvent(ManagerEvent event) {
                    if (managerEvents == null) {
                        managerEvents = new ArrayList<ManagerEvent>();
                    }
                    managerEvents.add(event);
                }
            };
        }
        return this.managerObserver;
    }

    public String toString(List<Task> events) {
        StringBuffer buf = new StringBuffer();
        for (Task event : events) {
            buf
                    .append(
                            String
                                    .format(
                                            "%1$ta %1$tR - %2$ta %2$tR, %3$s, %4$s, %5$s",
                                            ((Task) event).getStartDate(),
                                            ((Task) event).getEndDate(),
                                            ((Task) event).getPosition().getName(),
                                            ((Task) event).getEmployee() != null ? ((Task) event).getEmployee().getFirstname() : "",
                                            ((Task) event).getEmployee() != null ? ((Task) event).getEmployee().getLastname() : ""));
            buf.append("\r\n");
        }
        return buf.toString();
    }

}
