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

import static com.planimod.ui.Localized._;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.eclipse.nebula.paperclips.core.grid.DefaultGridLook;
import org.eclipse.nebula.paperclips.core.grid.GridPrint;
import org.eclipse.nebula.paperclips.core.border.LineBorder;
import org.eclipse.nebula.paperclips.core.Print;
import org.eclipse.nebula.paperclips.core.text.TextPrint;

import com.patrikdufresne.printing.BackgroundProvider;
import com.patrikdufresne.printing.ColorUtil;
import com.patrikdufresne.printing.FontDataUtil;
import com.patrikdufresne.printing.GapPrint;
import com.patrikdufresne.printing.MinSizePrint;
import com.patrikdufresne.util.DateFormatRegistry;
import com.planimod.core.Employee;
import com.planimod.core.NonAvailability;
import com.planimod.core.Task;
import com.planimod.core.Team;
import com.planimod.core.TimeRange;
import com.planimod.core.comparators.EmployeeComparators;
import com.planimod.core.comparators.TeamComparators;
import com.planimod.core.comparators.TimeRangeComparators;
import com.planimod.ui.printing.PlanimodPrintFactory;
import com.planimod.ui.printing.PlanimodPrintUtil;

/**
 * This class is used to create a planif transfert: does a delta between two weeks.
 * 
 * @author Patrik Dufresne
 * 
 */
public class PlanifWeekTransfertPrintFactory extends PlanimodPrintFactory {

    /**
     * Define the minimum height of a section. (value 72 point = 1 inch)
     */
    private static final int MIN_HEIGHT = 36;
    /**
     * Define the cell padding (value 5).
     */
    private static final int CELL_PADDING = 5;

    /**
     * Define the first day of week
     */
    private int firstDayOfWeek;

    /**
     * List of non-availabilities.
     */
    private List<NonAvailability> nonAvailabilities;

    /**
     * Tasks to print.
     */
    private List<Task> tasks;

    /**
     * Create a new factory.
     */
    public PlanifWeekTransfertPrintFactory() {
        super(_("Programation des équipes de travail"));

        // Sets first day of week using the localized calendar. Used should
        // redefine the first day of week using the function
        // #setFirstDayOfWeek(Date).
        Calendar cal = Calendar.getInstance();
        this.firstDayOfWeek = cal.getFirstDayOfWeek();
    }

    /**
     * This implementation create a new print body.
     */
    @Override
    protected void createSections() {

        // Build the data to be printed.
        List<TimeRange> ranges = new ArrayList<TimeRange>(DataTable.computeWeekTimeRange(this.tasks, this.firstDayOfWeek));
        Collections.sort(ranges, TimeRangeComparators.byDate());

        /*
         * Loop on week time range
         */
        DataTable data1;
        DataTable data2 = new DataTable(ranges.get(0), this.tasks, Collections.EMPTY_LIST, this.nonAvailabilities);
        for (int i = 1; i < ranges.size(); i++) {
            data1 = data2;
            data2 = new DataTable(ranges.get(i), this.tasks, Collections.EMPTY_LIST, this.nonAvailabilities);

            final DataTable finalData1 = data1;
            final DataTable finalData2 = data2;
            String title = DateFormatRegistry.format(data2.getRange().getStartDate(), DateFormatRegistry.DATE | DateFormatRegistry.MEDIUM);
            title = _("Programmation des équipes de travail\r\nSemaine du %s", title);
            addSection(new PlanimodSection(title, getSubTitle()) {
                @Override
                public Print createBodyArea() {
                    return createWeekGrid(finalData1, finalData2);
                }
            });

        }

    }

    /**
     * Creation of the print for a week.
     * 
     * @param parent
     *            the parent Print.
     * @param previousData
     *            The previous week
     * @param newData
     *            The week to compare with
     */
    protected Print createWeekGrid(DataTable previousData, DataTable newData) {

        // Create a grid to hold all the data related to the time range (a
        // week)
        GridPrint weekGrid = new GridPrint();
        weekGrid.addColumn("l:d:g");
        BackgroundProvider background = new BackgroundProvider(weekGrid);
        DefaultGridLook look = new DefaultGridLook();
        LineBorder border = new LineBorder();
        border.setGapSize(0);
        look.setCellBorder(border);
        look.setBodyBackgroundProvider(background);
        look.setCellPadding(0, 0);
        weekGrid.setLook(look);

        List<Team> teams = new ArrayList<Team>(newData.listTeam());
        Collections.sort(teams, TeamComparators.byName());

        // Loop on team
        for (Team team : teams) {

            // Add a team labels to weekPrint
            TextPrint textPrint = new TextPrint(PlanimodPrintUtil.getName(team), FontDataUtil.BOLD_SMALL);
            textPrint.setForeground(ColorUtil.WHITE);
            Print print = new GapPrint(textPrint, 1, 5);
            background.setBackground(print, ColorUtil.BLACK);
            weekGrid.add(print);

            // Add the section
            GridHelper helper = new GridHelper();
            helper.addGroup(null);

            // Add the employees to the grid.
            List<Employee> employees = new ArrayList<Employee>(newData.listEmployeeByTeam(team));
            employees.removeAll(previousData.listEmployeeByTeam(team));
            Collections.sort(employees, EmployeeComparators.bySeniority());
            for (Employee e : employees) {
                boolean recallFromNonAvailability = previousData.listEmployeeWithNonAvailabilityWithoutTeam().contains(e);
                helper.addItem(PlanimodPrintUtil.getName(e) + (recallFromNonAvailability ? PlanifSummaryHelper.recall() : PlanimodPrintUtil.EMPTY));
            }

            weekGrid.add(new MinSizePrint(new GapPrint(helper.createPrint(3, 2), CELL_PADDING), 0, MIN_HEIGHT));

        }

        // Layoffs
        createLayoffs(previousData, newData, weekGrid, background);

        // Recalls
        createRecalls(previousData, newData, weekGrid, background);

        // Custom
        TextPrint textPrint = new TextPrint(_("Demande de retour au quart de travail"), FontDataUtil.BOLD_SMALL);
        textPrint.setForeground(ColorUtil.WHITE);
        Print print = new GapPrint(textPrint, 1, 5);
        background.setBackground(print, ColorUtil.BLACK);
        weekGrid.add(print);
        weekGrid.add(new MinSizePrint(new TextPrint(), 0, MIN_HEIGHT));

        return weekGrid;

    }

    private void createLayoffs(DataTable previousData, DataTable newData, GridPrint weekGrid, BackgroundProvider background) {
        TextPrint textPrint = new TextPrint(
                _("Mises à pied --- Si vous avez besoin d'un relevé d'emploi, merci d'inscrire un X à votre nom."),
                FontDataUtil.BOLD_SMALL);
        textPrint.setForeground(ColorUtil.WHITE);
        Print print = new GapPrint(textPrint, 1, 5);
        background.setBackground(print, ColorUtil.BLACK);
        weekGrid.add(print);

        GridHelper helper = new GridHelper();
        helper.addGroup(null);

        List<Employee> employees = new ArrayList<Employee>(previousData.listEmployee());
        employees.addAll(previousData.listEmployeeWithNonAvailabilityWithoutTeam());
        employees.removeAll(newData.listEmployee());
        employees.removeAll(newData.listEmployeeWithNonAvailabilityWithoutTeam());
        Collections.sort(employees, EmployeeComparators.bySeniority());
        for (Employee e : employees) {
            helper.addItem(PlanimodPrintUtil.getName(e));
        }

        weekGrid.add(new MinSizePrint(new GapPrint(helper.createPrint(3, 2), CELL_PADDING), 0, MIN_HEIGHT));

    }

    /**
     * Create the print containing the recalls.
     * 
     * @param previousData
     *            previous week data
     * @param newData
     *            new week data
     * @param weekGrid
     *            the parent grid
     * @param background
     */
    private void createRecalls(DataTable previousData, DataTable newData, GridPrint weekGrid, BackgroundProvider background) {
        // Add the headline
        TextPrint textPrint = new TextPrint(_("Rappels au travail"), FontDataUtil.BOLD_SMALL);
        textPrint.setForeground(ColorUtil.WHITE);
        Print print = new GapPrint(textPrint, 1, 5);
        background.setBackground(print, ColorUtil.BLACK);
        weekGrid.add(print);

        // Create a grid helper to print the employees
        GridHelper helper = new GridHelper();
        helper.addGroup(null);

        List<Employee> employees = new ArrayList<Employee>(newData.listEmployee());
        employees.removeAll(previousData.listEmployee());
        employees.removeAll(previousData.listEmployeeWithNonAvailabilityWithoutTeam());
        Collections.sort(employees, EmployeeComparators.bySeniority());
        for (Employee e : employees) {
            helper.addItem(PlanimodPrintUtil.getName(e, newData.getEmployeeTeam(e)));
        }

        weekGrid.add(new MinSizePrint(new GapPrint(helper.createPrint(3, 2), CELL_PADDING), 0, MIN_HEIGHT));
    }

    /**
     * Return the first day of week.
     * 
     * @return the first day of week value.
     */
    public int getFirstDayOfWeek() {
        return this.firstDayOfWeek;
    }

    /**
     * Return the non-availabilities.
     * 
     * @return
     */
    public Collection<NonAvailability> getNonAvailabilities() {
        return Collections.unmodifiableList(this.nonAvailabilities);
    }

    /**
     * Returns the tasks being printed by this factory.
     * 
     * @return
     */
    public Collection<Task> getTasks() {
        return Collections.unmodifiableCollection(this.tasks);
    }

    /**
     * Sets the first day of week value.
     * 
     * @param firstDayOfWeek
     */
    public void setFirstDayOfWeek(int firstDayOfWeek) {
        this.firstDayOfWeek = firstDayOfWeek;
    }

    /**
     * Sets the non-availabilities to be printed. There is no filter on the data provided.
     * 
     * @param nonAvailabilities
     */
    public void setNonAvailabilties(Collection<NonAvailability> nonAvailabilities) {
        this.nonAvailabilities = new ArrayList<NonAvailability>(nonAvailabilities);
    }

    /**
     * Sets the tasks to be printed.
     * <p>
     * User may sets task for a week or a single day.
     * 
     * @param tasks
     */
    public void setTasks(Collection<Task> tasks) {
        this.tasks = new ArrayList<Task>(tasks);
    }

}
