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

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

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.nebula.paperclips.core.AlignPrint;
import org.eclipse.nebula.paperclips.core.grid.CellBackgroundProvider;
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.page.PageNumber;
import org.eclipse.nebula.paperclips.core.page.PageNumberFormat;
import org.eclipse.nebula.paperclips.core.page.PageNumberPrint;
import org.eclipse.nebula.paperclips.core.Print;
import org.eclipse.nebula.paperclips.core.RotatePrint;
import org.eclipse.nebula.paperclips.core.ScalePrint;
import org.eclipse.nebula.paperclips.core.SidewaysPrint;
import org.eclipse.nebula.paperclips.core.text.StyledTextPrint;
import org.eclipse.nebula.paperclips.core.text.TextPrint;
import org.eclipse.nebula.paperclips.core.text.TextStyle;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.RGB;

import com.patrikdufresne.printing.ColorUtil;
import com.patrikdufresne.printing.FontDataUtil;
import com.patrikdufresne.printing.GapPrint;
import com.patrikdufresne.util.DateFormatRegistry;
import com.planimod.core.Employee;
import com.planimod.core.EmployeePreference;
import com.planimod.core.Position;
import com.planimod.core.Qualification;
import com.planimod.core.Section;
import com.planimod.core.comparators.EmployeePreferenceComparators;
import com.planimod.core.comparators.PositionComparators;
import com.planimod.core.comparators.SectionComparators;
import com.planimod.ui.printing.PlanimodPrintFactory;
import com.planimod.ui.printing.PlanimodPrintUtil;

/**
 * This class create a Employee Print Factory. It create a print listing employees.
 * 
 * @author patapouf
 * 
 */
public class QualificationPrintFactory extends PlanimodPrintFactory {

    private static class PrivateDataTable {

        Collection<EmployeePreference> preferences;

        Collection<Qualification> qualifications;

        public PrivateDataTable(Collection<EmployeePreference> preferences, Collection<Qualification> qualifications) {
            this.preferences = preferences;
            this.qualifications = qualifications;
        }

        /**
         * Check if the employee has the qualification
         * 
         * @param employee
         *            the employee
         * @param position
         *            the position
         * @return
         */
        public boolean isQualify(Employee employee, Position position) {
            Qualification qualif;
            Iterator<Qualification> iter = this.qualifications.iterator();
            while (iter.hasNext()) {
                qualif = iter.next();
                if (qualif.getEmployee().equals(employee) && qualif.getPosition().equals(position)) {
                    return true;
                }
            }
            return false;
        }

        public Collection<EmployeePreference> listEmployeePreferences() {
            return this.preferences;
        }

        /**
         * List the positions.
         * 
         * @return
         */
        public Collection<Position> listPositions() {
            Set<Position> set = new HashSet<Position>();
            for (Qualification qualif : this.qualifications) {
                if (qualif.getPosition() != null) {
                    set.add(qualif.getPosition());
                }
            }
            for (EmployeePreference p : this.preferences) {
                if (p.getPreferredPosition() != null) {
                    set.add(p.getPreferredPosition());
                }
            }
            return set;
        }

        /**
         * List the sections
         * 
         * @return
         */
        public Collection<? extends Section> listSections() {
            Set<Section> set = new HashSet<Section>();
            for (Position pos : listPositions()) {
                set.add(pos.getSection());
            }
            return set;
        }

    }

    /**
     * Define the orientation. of the SWT.HORIZONTAL or SWT.VERTICAL constant;
     */
    private int orientation;

    /**
     * The preferences.
     */
    private Collection<EmployeePreference> preferences;

    /**
     * Hold employee's qualification.
     */
    private Collection<Qualification> qualifications;

    /**
     * The revision number.
     */
    private String revision;

    /**
     * Create a new Factory.
     * 
     * @param jobName
     */
    public QualificationPrintFactory() {
        super(_("Grille de formation"));
        this.orientation = SWT.HORIZONTAL;
        setTitle(_("Grille de formation"));
    }

    /**
     * Override the implementation to avoid create titles.
     */
    @Override
    protected void createSections() {
        addSection(new PlanimodSection(null, null) {
            @Override
            public Print createBodyArea() {
                return QualificationPrintFactory.this.createBodyArea();
            }
        });
    }

    /**
     * This implementation create a employees print.
     */
    @Override
    protected Print createBodyArea() {

        PrivateDataTable data = new PrivateDataTable(this.preferences, this.qualifications);

        // Get sections
        List<Section> sections = new ArrayList<Section>(data.listSections());
        Collections.sort(sections, this.orientation == SWT.HORIZONTAL ? Collections.reverseOrder(SectionComparators.byName()) : SectionComparators.byName());

        // Get Positions
        List<Position> positions = new ArrayList<Position>(data.listPositions());
        Collections.sort(positions, this.orientation == SWT.HORIZONTAL ? Collections.reverseOrder(PositionComparators.byClassifiedName()) : PositionComparators
                .byClassifiedName());

        // Create Grid holding the qualifications
        GridPrint grid = new GridPrint();

        // Set the Grid Look
        DefaultGridLook look = new DefaultGridLook();
        look.setBodyBackgroundProvider(new CellBackgroundProvider() {
            @Override
            public RGB getCellBackground(int row, int column, int colspan) {
                return column % 2 == 0 ? ColorUtil.LIGHT_GRAY : ColorUtil.WHITE;
            }
        });
        look.setHeaderBackgroundProvider(new CellBackgroundProvider() {
            @Override
            public RGB getCellBackground(int row, int column, int colspan) {
                return row > 0 && column % 2 == 0 ? ColorUtil.LIGHT_GRAY : ColorUtil.WHITE;
            }
        });
        LineBorder border = new LineBorder();
        border.setGapSize(0);
        border.setLineWidth(1);
        look.setCellBorder(border);
        look.setCellPadding(1, 1);
        grid.setLook(look);
        grid.setCellClippingEnabled(false);

        /*
         * Create the grid header
         */
        createGridHeader(grid, sections, positions);

        // Create a row for each employee
        createGridContent(grid, sections, positions, data);

        return new RotatePrint(new ScalePrint(grid));
    }

    protected void createGridContent(GridPrint grid, List<Section> sections, List<Position> positions, PrivateDataTable data) {

        // Get employees
        List<EmployeePreference> preferences = new ArrayList<EmployeePreference>(data.listEmployeePreferences());
        Collections.sort(preferences, EmployeePreferenceComparators.bySeniority());

        // Loop on employees
        for (EmployeePreference pref : preferences) {

            // According to orientation
            if (this.orientation == SWT.VERTICAL) {
                // Create the employee cell in the first column
                grid.add(new TextPrint(PlanimodPrintUtil.getName(pref.getEmployee()), FontDataUtil.DEFAULT));
            }

            for (Section section : sections) {
                for (Position position : positions) {
                    if (section.equals(position.getSection())) {
                        boolean qualify = data.isQualify(pref.getEmployee(), position);
                        boolean preferredPosition = position.equals(pref.getPreferredPosition());

                        // According to orientation
                        String text = (qualify ? PlanimodPrintUtil.X : PlanimodPrintUtil.EMPTY);
                        Print cell = new TextPrint(text, FontDataUtil.DEFAULT, SWT.CENTER);
                        if (preferredPosition) {
                            ((TextPrint) cell).setText(" X ");
                            ((TextPrint) cell).setForeground(new RGB(255, 255, 255));
                            ((TextPrint) cell).setBackground(new RGB(0, 0, 0));
                        }
                        if (this.orientation == SWT.HORIZONTAL) {
                            cell = new SidewaysPrint(cell, -90);
                        }
                        grid.add(SWT.CENTER, SWT.CENTER, cell);
                    }
                }
            }

            // According to orientation
            if (this.orientation == SWT.HORIZONTAL) {
                // Create the employee cell in the last column
                grid.add(new GapPrint(new TextPrint(PlanimodPrintUtil.getName(pref.getEmployee()), FontDataUtil.DEFAULT), 4, 0));
            }

        }
    }

    /**
     * Create the header of the grid. This function create one column for each position.
     * 
     * @param grid
     *            the grid
     * @param positions
     *            the list of positions
     */
    protected void createGridHeader(GridPrint grid, Collection<Section> sections, Collection<Position> positions) {

        // According to the orientation
        if (this.orientation == SWT.VERTICAL) {
            // Create the first column (to hold the employees)
            grid.addColumn("left:default");
            grid.addHeader(new TextPrint("", FontDataUtil.DEFAULT));
            grid.addHeader(new TextPrint("", FontDataUtil.DEFAULT));
        }

        // The creation of the header for position is done in two phase, to
        // create the first section headers and then the position headers
        int colspan;
        for (Section section : sections) {
            colspan = 0;
            for (Position position : positions) {
                if (position.getSection().equals(section)) {
                    colspan++;
                    grid.addColumn("center:12");
                }
            }
            grid.addHeader(new ScalePrint(new TextPrint(PlanimodPrintUtil.getName(section), FontDataUtil.DEFAULT)), colspan);
        }

        // According to the orientation
        if (this.orientation == SWT.HORIZONTAL) {
            // Create the last column (to hold the employees)
            grid.addColumn("left:default");
            grid.addHeader(new TextPrint("", FontDataUtil.DEFAULT));
        }

        // Second phase
        int angle = this.orientation == SWT.HORIZONTAL ? 270 : 90;
        for (Section section : sections) {
            for (Position position : positions) {
                if (position.getSection().equals(section)) {
                    Print cell = new SidewaysPrint(new TextPrint(PlanimodPrintUtil.getName(position), FontDataUtil.DEFAULT), angle);
                    grid.addHeader(cell);
                }
            }
        }

        // According to the orientation
        if (this.orientation == SWT.HORIZONTAL) {
            // Create the last column (to hold the employees)
            GridPrint titleGrid = new GridPrint();
            titleGrid.addColumn("center:preferred");
            if (super.getTitle() != null) {
                titleGrid.add(new TextPrint(super.getTitle(), new TextStyle().font(FontDataUtil.HEADER1)));
            }
            if (super.getSubTitle() != null) {
                titleGrid.add(new TextPrint(super.getSubTitle(), new TextStyle().font(FontDataUtil.HEADER2)));
            }
            grid.addHeader(new AlignPrint(new SidewaysPrint(titleGrid, angle), SWT.RIGHT, SWT.DEFAULT));
        }

    }

    public Collection<EmployeePreference> getEmployeePreferences() {
        return Collections.unmodifiableCollection(this.preferences);
    }

    /**
     * Return the grid orientation (either SWT.HORIZONTAL or SWT.VERTICAL).
     * 
     * @return
     */
    public int getGridOrientation() {
        return this.orientation;
    }

    /**
     * Returns the employee's qualification to be print by this factory.
     * 
     * @return qualification list or null
     */
    public Collection<Qualification> getQualifications() {
        return Collections.unmodifiableCollection(this.qualifications);
    }

    /**
     * Sets the employees preferences.
     * 
     * @param preferences
     */
    public void setEmployeePreferences(Collection<EmployeePreference> preferences) {
        this.preferences = new ArrayList<EmployeePreference>(preferences);
    }

    /**
     * Sets the grid orientation (either SWT.HORIZONTAL or SWT.VERTICAL).
     * 
     * @param orientation
     */
    public void setGridOrientation(int orientation) {
        if (this.orientation == SWT.VERTICAL) {
            this.orientation = SWT.VERTICAL;
        } else {
            this.orientation = SWT.HORIZONTAL;
        }
    }

    /**
     * Sets the list of qualification to print.
     * 
     * @param qualifications
     *            a list or null
     */
    public void setQualifications(Collection<Qualification> qualifications) {
        this.qualifications = new ArrayList<Qualification>(qualifications);
    }

    /**
     * Sets the revision number.
     * 
     * @param revision
     *            the revision (or null or empty).
     */
    public void setRevision(String revision) {
        this.revision = revision;
    }

    @Override
    protected Print createRightFooterPrint(PageNumber pageNumber) {
        return new TextPrint(
                _("Par :") + "______________________________\n" + _("Form.: 18.04") + " " + _("Rev. :") + this.revision,
                FontDataUtil.SMALL,
                SWT.RIGHT);
    }

    @Override
    protected Print createMiddleFooterPrint(PageNumber pageNumber) {
        StyledTextPrint text = new StyledTextPrint();
        text.setStyle(new TextStyle().font(FontDataUtil.SMALL));
        text.append(_("X : Formé pour le poste")).newline().append(
                " X ",
                new TextStyle().background(new RGB(0, 0, 0)).foreground(new RGB(255, 255, 255)).font(FontDataUtil.SMALL)).append(_(": Détient le poste"));
        return text;
    }

    @Override
    protected Print createLeftFooterPrint(PageNumber pageNumber) {
        PageNumberPrint pageNumberPrint = new PageNumberPrint(pageNumber, FontDataUtil.SMALL, SWT.LEFT);
        pageNumberPrint.setPageNumberFormat(new PageNumberFormat() {
            @Override
            public String format(PageNumber pn) {
                return _("Imprimer le %s par Planimod", DateFormatRegistry.format(getPrintTime(), DateFormatRegistry.DATETIME | DateFormatRegistry.ISO))
                        + "\n"
                        + _("%d de %d", Integer.valueOf(pn.getPageNumber() + 1), Integer.valueOf(pn.getPageCount()));
            }
        });
        return pageNumberPrint;
    }

}
