/*
 * Copyright (c) 2011, Patrik Dufresne. All rights reserved.
 * Patrik Dufresne PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.planimod.ui.views.planif.printing;

import static com.planimod.ui.views.planif.printing.PlanifSummaryHelper.getEmployeeName;
import static com.planimod.ui.views.planif.printing.PlanifSummaryHelper.getNonAvailabilityDesc;
import static com.planimod.ui.views.planif.printing.PlanifSummaryHelper.getVacantDesc;

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

import net.sf.paperclips.DefaultGridLook;
import net.sf.paperclips.GridPrint;
import net.sf.paperclips.LineBorder;
import net.sf.paperclips.Print;
import net.sf.paperclips.TextPrint;

import com.patrikdufresne.printing.BackgroundProvider;
import com.patrikdufresne.printing.ColorUtil;
import com.patrikdufresne.printing.FontDataUtil;
import com.patrikdufresne.printing.GapPrint;
import com.patrikdufresne.util.BidiMultiMap;
import com.patrikdufresne.util.DateFormatRegistry;
import com.patrikdufresne.util.Localized;
import com.planimod.core.Employee;
import com.planimod.core.EmployeePreference;
import com.planimod.core.NonAvailability;
import com.planimod.core.Position;
import com.planimod.core.Section;
import com.planimod.core.Shift;
import com.planimod.core.Task;
import com.planimod.core.Team;
import com.planimod.core.TimeRange;
import com.planimod.core.comparators.EmployeePreferenceComparators;
import com.planimod.core.comparators.NonAvailabilityComparators;
import com.planimod.core.comparators.SectionComparators;
import com.planimod.core.comparators.TeamComparators;
import com.planimod.ui.printing.PlanimodPrintFactory;
import com.planimod.ui.printing.PlanimodPrintUtil;

/**
 * This class is used to generate a planif report. This report intend to
 * generalized the solution.
 * 
 * @author Patrik Dufresne
 * 
 */
public class PlanifWeekSummaryPrintFactory extends PlanimodPrintFactory {

	/**
	 * Used for localization.
	 */
	static Localized localized = Localized
			.load(PlanifWeekSummaryPrintFactory.class);

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

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

	/**
	 * List of employee preferences.
	 */
	private List<EmployeePreference> preferences;

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

	/**
	 * Create a new factory.
	 */
	public PlanifWeekSummaryPrintFactory() {
		super(Localized.get(PlanifWeekSummaryPrintFactory.class,
				"PlanifWeekSummaryPrintFactory.jobName")); //$NON-NLS-1$

		// 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.
		Set<TimeRange> ranges = DataTable.computeWeekTimeRange(this.tasks,
				this.firstDayOfWeek);

		// Create print to hold the pages content
		GridPrint topGrid = new GridPrint();
		topGrid.addColumn("c:d:g"); //$NON-NLS-1$

		/*
		 * Loop on week time range
		 */
		for (TimeRange range : ranges) {

			// Create data structure from the list of task for the current week
			final DataTable data = new DataTable(range, this.tasks,
					this.preferences, this.nonAvailabilities);
			String title = DateFormatRegistry.format(data.getRange()
					.getStartDate(), DateFormatRegistry.DATE
					| DateFormatRegistry.MEDIUM);
			title = localized.format(
					"PlanifWeekSummaryPrintFactory.title", title); //$NON-NLS-1$
			addSection(new PlanimodSection(title, getSubTitle()) {
				@Override
				public Print createBodyArea() {
					return createWeekGrid(data);
				}
			});
		}

	}

	/**
	 * Creating a print for a week.
	 * 
	 * @param parent
	 *            the parent Print.
	 * @param data
	 *            the data associated to the week.
	 */
	protected Print createWeekGrid(DataTable data) {

		// Create a grid to hold all the data related to the time range (a
		// week)
		GridPrint weekGrid = new GridPrint();
		weekGrid.addColumn("l:d:g"); //$NON-NLS-1$
		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>(data.listTeam());
		Collections.sort(teams, TeamComparators.byName());

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

			// Add a team labels to weekPrint
			int countEmployee = data.listEmployeeByTeam(team).size();
			TextPrint textPrint = new TextPrint(PlanimodPrintUtil.getName(team,
					countEmployee), 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();

			/*
			 * Create a print group for each Section.
			 */
			List<Section> sections = new ArrayList<Section>(
					data.listSectionByTeam(team));
			Collections.sort(sections, SectionComparators.byName());
			for (Section s : sections) {
				List<Employee> employees = new ArrayList<Employee>(
						data.listEmployeeByTeamAndSection(team, s));
				if (employees.size() > 0) {
					// Add the section
					helper.addGroup(PlanimodPrintUtil.getName(s).toUpperCase());
					// Sort Employee by seniority.
					Collections.sort(employees,
							EmployeePreferenceComparators
									.byPreferredSectionAndSeniority(s,
											this.preferences));
					int count = 1;
					for (Employee employee : employees) {
						boolean classified = data
								.listEmployeeWithClassifiedPositionByTeam(team)
								.contains(employee);
						helper.addItem(getEmployeeName(count, employee,
								classified, false));
						count++;
					}
				}
			}

			/*
			 * Create vacant group
			 */
			Collection<Task> vacants = data.listVacantTasksByTeam(team);
			if (vacants.size() > 0) {
				helper.addGroup(
						(vacants.size() == 1 ? localized
								.get("PlanifSummaryPrintFactory.vacant") //$NON-NLS-1$
								: localized
										.get("PlanifSummaryPrintFactory.vacants")) //$NON-NLS-1$
								.toUpperCase(), true);
				BidiMultiMap<Position, Task> table = DataTable
						.groupByPosition(vacants);
				for (Position pos : table.keySet()) {
					helper.addItem(getVacantDesc(table.valueSet(pos)));
				}
			}

			/*
			 * Create non-availability group
			 */
			List<NonAvailability> list = new ArrayList<NonAvailability>(
					data.listVisibleNonAvailabilityByTeam(team));
			if (list.size() > 0) {
				helper.addGroup(
						localized
								.get("PlanifSummaryPrintFactory.nonAvailabilities.team"), //$NON-NLS-1$
						true);
				Collection<Shift> shifts = data.listShiftByTeam(team);
				Collections.sort(list,
						NonAvailabilityComparators.byEmployeeName());
				for (NonAvailability na : list) {
					helper.addItem(getNonAvailabilityDesc(na, shifts));
				}
			}

			// Create the print and add it to the weekGrid.
			weekGrid.add(new GapPrint(helper.createPrint(5, 2), 5, 5, 12, 5));

		}

		/*
		 * Print non-availabilities
		 */
		List<NonAvailability> currentNonAvailabilities = new ArrayList<NonAvailability>(
				data.listVisibleNonAvailabilityWithoutTeam());
		if (currentNonAvailabilities.size() > 0) {

			// Add a team labels to weekPrint
			TextPrint textPrint = new TextPrint(
					localized.get("PlanifSummaryPrintFactory.nonAvailabilities"), //$NON-NLS-1$
					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(""); //$NON-NLS-1$

			// Sort non availability by enployee's name
			Collections.sort(currentNonAvailabilities,
					NonAvailabilityComparators.byEmployeeName());
			for (NonAvailability na : currentNonAvailabilities) {
				helper.addItem(getNonAvailabilityDesc(data.getRange(), na));
			}

			weekGrid.add(new GapPrint(helper.createPrint(4, 2), 5));
		}

		return weekGrid;
	}

	/**
	 * Returns the employees preference list.
	 * 
	 * @return employees list or null
	 */
	public Collection<EmployeePreference> getEmployeePreferences() {
		return Collections.unmodifiableList(this.preferences);
	}

	/**
	 * 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 list of employees preferences.
	 * 
	 * @param prefs
	 *            a list or null
	 */
	public void setEmployeePreferences(Collection<EmployeePreference> prefs) {
		this.preferences = new ArrayList<EmployeePreference>(prefs);
	}

	/**
	 * 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);
	}

}
