/**
 *  Copyright (C) 2002-2012   The FreeCol Team
 *
 *  This file is part of FreeCol.
 *
 *  FreeCol is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  FreeCol 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with FreeCol.  If not, see <http://www.gnu.org/licenses/>.
 */

package net.sf.freecol.client.gui.panel;

import java.awt.Color;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedHashSet;
import java.util.Set;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;

import net.miginfocom.swing.MigLayout;
import net.sf.freecol.client.FreeColClient;
import net.sf.freecol.client.gui.GUI;
import net.sf.freecol.client.gui.i18n.Messages;
import net.sf.freecol.common.model.Ability;
import net.sf.freecol.common.model.Building;
import net.sf.freecol.common.model.Colony;
import net.sf.freecol.common.model.GoodsType;
import net.sf.freecol.common.model.StringTemplate;
import net.sf.freecol.common.model.Unit;
import net.sf.freecol.common.model.UnitType;

/**
 * This panel displays the Labour Report.
 */
public final class CompactLabourReport extends ReportPanel {

    private int COLONY_COLUMN = 0;
    private int UNIT_TYPE_COLUMN = 1;
    private int WORKING_COLUMN = 2;
    private int BUILDING_COLUMN = 3;
    private int COLONIST_COLUMN = 4;
    private int COLONIST_SUMMARY_COLUMN = 5;
    private int PRODUCTION_SYMBOL_COLUMN = 6;
    private int PRODUCTION_COLUMN = 7;
    private int PRODUCTION_SUMMARY_COLUMN = 8;
    private int NETPRODUCTION_SUMMARY_COLUMN = 9;

    private static final int COLUMNS = 10;

    private LabourData labourData;

    private LabourData.UnitData unitData;

    private boolean showProduction;
    private boolean showNetProduction;
    private boolean showProductionSymbols;

    private boolean showBuildings;

    private final JPanel headerRow = new JPanel() {
        @Override
        public String getUIClassID() {
            return "ReportPanelUI";
        }
    };

    /**
     * The constructor that will add the items to this panel.
     * @param freeColClient 
     *
     * @param parent The parent of this panel.
     */
    public CompactLabourReport(FreeColClient freeColClient, GUI gui) {
        this(freeColClient, gui, null);

        labourData = new LabourData(getFreeColClient());
        initialize();
    }

    /**
     * The constructor that will add the items to this panel.
     * @param freeColClient 
     *
     * @param parent The parent of this panel.
     */
    public CompactLabourReport(FreeColClient freeColClient, GUI gui, LabourData.UnitData data) {
        super(freeColClient, gui, data == null ? Messages.message("reportLabourAction.name")
              : Messages.message("report.labour.details"));
        this.unitData = data;

        headerRow.setBorder(new EmptyBorder(20, 20, 0, 20));
        headerRow.setOpaque(true);
        scrollPane.setColumnHeaderView(headerRow);
    }

    public JButton createColonyButton(final Colony colony) {
        String text = colony.getName();
        if (!unitData.isSummary()) {
//            int unitIndex = unitData.getUnitType().getIndex();
//
//            int skillLevel = Unit.getSkillLevel(unitIndex);
//            if (skillLevel <= 0 && skillLevel > -2) {
//                //settlers and servants can be trained anywwhere a farmer can
//                unitIndex = Unit.EXPERT_FARMER;
//            }

            if (colony.canTrain(unitData.getUnitType())) {
                text = text + "*";
            }
        }

        return createButton(text, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                getGUI().showColonyPanel(colony);
            }
        });
    }


    @Override
    public void initialize() {
        if (isOverview()) {
            showProduction = true;
            showNetProduction = true;
            showProductionSymbols = true;
            showBuildings = false;
        } else {
            showProduction = unitData.showProduction();
            showNetProduction = unitData.showNetProduction();
            showProductionSymbols = false;

            GoodsType expertProduction = getGoodsType();
            showBuildings = (expertProduction != null && !expertProduction.isFarmed()) || unitData.getTotal().isTraining();
        }


        String layoutConstraints = "fill, insets 0, gap 0 0";
        StringBuilder widths = new StringBuilder();
        widths.append("[175, fill]");
        widths.append(isOverview() || !unitData.isSummary() ? "[150, fill]" : "[0!]");
        widths.append("[150, fill]");
        widths.append(showBuildings ? "[130, fill]" : "[0!]");
        widths.append("[26, fill][33, fill]");
        widths.append(showProductionSymbols ? "[50, fill]" : "[0!]");
        widths.append(showProduction ? "[30, fill][40, fill]" : "[0!][0!]");
        widths.append(showNetProduction ? "[40, fill]" : "[0!]");
        String columnConstraints = widths.toString();
        String rowConstraints = "[fill]";

        headerRow.setLayout(new MigLayout(layoutConstraints, columnConstraints, rowConstraints));
        reportPanel.setLayout(new MigLayout(layoutConstraints, columnConstraints, rowConstraints));

        addHeader();

        if (isOverview()) {
            addUnitTypes();
        } else {
            addLocations();
        }
    }

    @Override
    protected Border createBorder() {
        return new EmptyBorder(0, 20, 20, 20);
    }

    /**
     * adds the header rows
     */
    private void addHeader() {
        int row = 1;

        JLabel empty = new JLabel("");
        empty.setBorder(TOPLEFTCELLBORDER);
        headerRow.add(empty, "cell " + COLONY_COLUMN + " 1");

        if (isOverview() || !unitData.isSummary()) {
            JLabel unitType = new JLabel(Messages.message("model.unit.type"));
            unitType.setBorder(TOPCELLBORDER);
            headerRow.add(unitType, "cell " + UNIT_TYPE_COLUMN + " 1");
        }

        JLabel workingAs = new JLabel(Messages.message("model.unit.workingAs"));
        workingAs.setBorder(TOPCELLBORDER);
        headerRow.add(workingAs, "cell " + WORKING_COLUMN + " 1");

        if (showBuildings) {
            JLabel building = new JLabel(Messages.message("building"));
            building.setBorder(TOPCELLBORDER);
            headerRow.add(building, "cell " + BUILDING_COLUMN + " 1");
        }

        JLabel colonists = new JLabel(Messages.message("colonists"));
        colonists.setBorder(TOPCELLBORDER);
        headerRow.add(colonists, "cell " + COLONIST_COLUMN + " 1 2 1");

        if (isOverview()) {
            JLabel production = new JLabel(Messages.message("report.production"));
            production.setBorder(TOPCELLBORDER);
            headerRow.add(production, "cell " + PRODUCTION_SYMBOL_COLUMN + " 1 " + (COLUMNS - PRODUCTION_SYMBOL_COLUMN) + " 1");
        } else if (showProduction) {
            LabourData.UnitData unit = unitData;
            GoodsType goods = unit.getExpertProduction();

            JLabel production = new JLabel(getLibrary().getGoodsImageIcon(goods));
            production.setBorder(TOPCELLBORDER);

            headerRow.add(production, "cell " + PRODUCTION_SYMBOL_COLUMN + " 1 " + (COLUMNS - PRODUCTION_SYMBOL_COLUMN + (showNetProduction && goods.isStoredAs() ? 1 : 0)) + " 1");

            if (showNetProduction && goods.isStoredAs()) {
                JLabel netProduction = new JLabel(getLibrary().getGoodsImageIcon(goods.getStoredAs()));
                netProduction.setBorder(TOPCELLBORDER);
                headerRow.add(netProduction, "cell " + NETPRODUCTION_SUMMARY_COLUMN + " 1");
            }
        }

        if (!isSummary()) {
            ImageIcon icon = getUnitIcon(unitData.getUnitType());
            header.setIcon(icon);
            header.setIconTextGap(20);
        }
    }

    /**
     * add unit data for a given location
     *
     * @param data
     * @param row  starting row
     * @return next row to use
     */
    private int addLocationData(LabourData.LocationData data, Colony colony, int row) {
        boolean allColonists = data.getUnitData().isSummary();

        LabourData.UnitData unit = data.getUnitData();
        UnitType unitType = unit.getUnitType();
        String unitName = unit.getUnitName();

        String workingAs = null;
        Building productionBuilding = null;

        if (!allColonists) {
            workingAs = Messages.message(unitType.getWorkingAsKey());
            if (colony != null) {
                GoodsType expert = unitType.getExpertProduction();
                if (expert != null) {
                    productionBuilding = colony.getBuildingForProducing(expert);
                }
            }
        }
        addLocationSummary(data, row);

        int buildingStartRow = row;

        //details

        int otherAmateurs = data.getOtherWorkingAmateurs().getColonists();
        if (!allColonists && otherAmateurs > 0) {
            addRow(data,
                   Messages.message("report.labour.otherUnitType"),
                   workingAs,
                   createNonCountedLabel(otherAmateurs),
                   data.getOtherWorkingAmateurs().getProduction(),
                   row);
            row++;
        }

        row = addRow(data,
                     unitName,
                     allColonists ? Messages.message("report.labour.expertsWorking") : workingAs,
                     data.getWorkingProfessionals().getColonists(),
                     data.getWorkingProfessionals().getProduction(),
                     row);

        int notProducingStartRow = row;

        if (showBuildings) {
            if (productionBuilding != null && row > buildingStartRow) {
                JLabel buildingLabel = localizedLabel(productionBuilding.getNameKey());
                buildingLabel.setBorder(CELLBORDER);
                reportPanel.add(buildingLabel, "cell " + BUILDING_COLUMN + " " + buildingStartRow + " 1 " + (row - buildingStartRow));
                buildingStartRow = row;
            } else {
                reportPanel.add(createEmptyLabel(), "cell " + BUILDING_COLUMN + " " + buildingStartRow + " 1 " + (row - buildingStartRow));
            }
        }

        row = addRow(data,
                     unitName,
                     Messages.message(allColonists ?
                         "report.labour.amateursWorking" :
                         "report.labour.workingAsOther"),
                     data.getWorkingAmateurs(),
                     0, row);
        if (data.getNotWorking() > 0) {
            addRow(data,
                   unitName,
                   Messages.message("report.labour.notWorking"),
                   createNumberLabel(data.getNotWorking(), "report.labour.notWorking.tooltip"),
                   0, row);
            row++;
        }

        Building schoolhouse = colony != null && data.isTraining() ?
            colony.getBuildingWithAbility(Ability.CAN_TEACH) : null;

        if (showBuildings && schoolhouse != null && row > buildingStartRow) {
            reportPanel.add(createEmptyLabel(), "cell " + BUILDING_COLUMN + " " + buildingStartRow + " 1 " + (row - buildingStartRow));
            buildingStartRow = row;
        }

        row = addRow(data,
                     unitName,
                     Messages.message("report.labour.teacher"),
                     data.getTeachers(),
                     0, row);
        if (!allColonists) {
            row = addRow(data,
                         data.getOtherStudentsName(),
                         Messages.message(StringTemplate.template("report.labour.learning")
                                          .addName("%unit%", data.getUnitData().getUnitName())),
                         data.getOtherStudents(),
                         0, row);
        }

        int studentCount = data.getStudents();
        if (studentCount > 0) {
            if (allColonists) {
                addRow(data, null, Messages.message("report.labour.sutdent"), createNonCountedLabel(studentCount), 0, row);
            } else {
                Set<UnitType> resultOfTraining = new LinkedHashSet<UnitType>();
                if (colony != null) {
                    for (Unit teacher : colony.getTeachers()) {
                        Unit student = teacher.getStudent();
                        if (student != null && student.getType() == unitType) {
                            resultOfTraining.add(Unit.getUnitTypeTeaching(teacher.getType(), student.getType()));
                        }
                    }
                }

                String student = resultOfTraining.size() == 1 ?
                    Messages.message(StringTemplate.template("report.labour.learning")
                                     .addName("%unit%", resultOfTraining.iterator().next())) :
                    Messages.message("report.labour.learningOther");
                addRow(data,
                       data.getUnitData().getUnitName(),
                       student,
                       createNumberLabel(-studentCount, "report.labour.subtracted.tooltip"),
                       0, row);
            }
            row++;
        }

        if (showBuildings && row > buildingStartRow) {
            JLabel buildingLabel = new JLabel(schoolhouse != null ?
                                              Messages.message(schoolhouse.getNameKey()) : "");
            buildingLabel.setBorder(CELLBORDER);
            reportPanel.add(buildingLabel, "cell " + BUILDING_COLUMN + " " + buildingStartRow + " 1 " + (row - buildingStartRow));
        }

        if (data.getUnitData().showProduction() && row > notProducingStartRow) {
            reportPanel.add(createEmptyLabel(), "cell " + PRODUCTION_COLUMN + " " + notProducingStartRow + " 1 " + (row - notProducingStartRow));
        }

        return row;
    }

    private void addLocations() {
        LabourData.LocationData unitTotal = unitData.getTotal();

        int row = 1;
        JLabel summaryLabel = new JLabel(Messages.message("report.labour.summary"));
        summaryLabel.setBorder(LEFTCELLBORDER);
        reportPanel.add(summaryLabel, "cell " + COLONY_COLUMN + " " + row + " 1 " + unitTotal.getRowCount());

        row = addLocationData(unitTotal, null, row);

        for (Colony colony : getSortedColonies()) {
            LabourData.LocationData colonyData = unitData.getDetails().get(colony);
            if (colonyData != null) {
                reportPanel.add(createColonyButton(colony), "cell " + COLONY_COLUMN + " "
                                + row + " 1 " + colonyData.getRowCount());
                row = addLocationData(colonyData, colony, row);
            }
        }
        LabourData.LocationData europe = unitData.getUnitsInEurope();
        if (europe.getRowCount() > 0) {
            JButton button = createButton(Messages.message(getMyPlayer().getEurope().getNameKey()),
                                          new ActionListener() {
                                              public void actionPerformed(ActionEvent e) {
                                                  getGUI().showEuropePanel();
                                              }
                                          });
            reportPanel.add(button, "cell " + COLONY_COLUMN + " " + row + " 1 " + europe.getRowCount());
            row = addLocationData(europe, null, row);
        }
        row = addNonLinkedLocation(unitData.getUnitsOnLand(), "report.onLand", row);
        row = addNonLinkedLocation(unitData.getUnitsAtSea(), "report.atSea", row);

        reportPanel.add(new JLabel(Messages.message("report.labour.canTrain")), "cell 1 " + row + " " + COLUMNS + " 1");
    }

    /*
     * distributes {@code value}, amount the number of {@code pocketCount}
     *
     * @param value
     * @param pocketCount
     * @return distribution of {@code value}
     */
    /*
    private int[] distribute(int value, int pocketCount) {
        int[] pockets = new int[pocketCount];

        int pocketIndex = 0;
        for (int i = value; i > 0; i--) {
            pockets[pocketIndex]++;

            pocketIndex++;
            pocketIndex = pocketIndex % pocketCount;
        }
        return pockets;
    }
     */

    private void addLocationSummary(LabourData.LocationData data, int row) {
        int rows = data.getRowCount();

        JLabel colonistsLabel = createNumberLabel(data.getTotalColonists(), null);
        if (data.getUnitData().isSummary()) {
            if (isOverview()) {
                reportPanel.add(createEmptyLabel(), "cell " + UNIT_TYPE_COLUMN + " " + row + " 1 " + rows);
            }
        } else {
            colonistsLabel.setToolTipText(Messages.message(StringTemplate.template("report.labour.unitTotal.tooltip")
                                                           .addName("%unit%", data.getUnitData().getUnitName())));
        }

        reportPanel.add(colonistsLabel, "cell " + COLONIST_SUMMARY_COLUMN + " " + row + " 1 " + data.getRowCount());

        if (showProduction && !data.getUnitData().showProduction()) {
            reportPanel.add(createEmptyLabel(), "cell " + PRODUCTION_SYMBOL_COLUMN + " " + row + " 4 " + rows);
            return;
        }

        if (showProduction) {
            JLabel productionLabel = createNumberLabel(data.getTotalProduction(), "report.labour.potentialProduction.tooltip");
            if (!data.isTotal() && data.getTotalProduction() == 0) {
                productionLabel.setText("");
            }

            reportPanel.add(productionLabel, "cell " + PRODUCTION_SUMMARY_COLUMN + " " + row + " 1 " + rows);
        }

        if (showNetProduction) {
            int net = data.getNetProduction();
            JLabel netProductionLabel = createNumberLabel(net, "report.labour.netProduction.tooltip");

            if (!data.getUnitData().showNetProduction() || (!data.isTotal() && net == 0)) {
                netProductionLabel.setText("");
                netProductionLabel.setToolTipText("");
            } else if (net >= 0) {
                netProductionLabel.setText("+" + net);
            } else {
                netProductionLabel.setForeground(Color.RED);
            }
            reportPanel.add(netProductionLabel, "cell " + NETPRODUCTION_SUMMARY_COLUMN + " " + row + " 1 " + rows);
        }

        if (showProductionSymbols) {
            JLabel icon = new JLabel();
            icon.setBorder(CELLBORDER);
            GoodsType goods = data.getUnitData().getExpertProduction();
            if (goods != null) {
                icon.setIcon(getLibrary().getGoodsImageIcon(goods));
            }
            reportPanel.add(icon, "cell " + PRODUCTION_SYMBOL_COLUMN + " " + row + " 1 " + rows);
        }
    }

    private int addNonLinkedLocation(LabourData.LocationData data, String messageKey, int row) {
        int rows = data.getRowCount();
        if (rows > 0) {
            JLabel label = new JLabel(Messages.message(messageKey));
            label.setBorder(LEFTCELLBORDER);
            label.setForeground(Color.GRAY);
            reportPanel.add(label, "cell " + COLONY_COLUMN + " " + row + " 1 " + rows);
            return addLocationData(data, null, row);
        }
        return row;
    }

    private int addRow(LabourData.LocationData data, String typeName, String activity, int colonists, int production, int row) {
        if (colonists > 0) {
            addRow(data, typeName, activity, createNumberLabel(colonists, null), production, row);
            row++;
        }
        return row;
    }

    private void addRow(LabourData.LocationData data, String typeName, String activity, JLabel colonistLabel, int production, int row) {
        if (!data.getUnitData().isSummary()) {
            JLabel typeLabel = new JLabel(typeName);
            typeLabel.setBorder(CELLBORDER);
            reportPanel.add(typeLabel, "cell " + UNIT_TYPE_COLUMN + " " + row);
        }

        JLabel activityLabel = new JLabel(activity);
        activityLabel.setBorder(CELLBORDER);
        reportPanel.add(activityLabel, "cell " + WORKING_COLUMN + " " + row);

        reportPanel.add(colonistLabel, "cell " + COLONIST_COLUMN + " " + row);

        if (data.getUnitData().showProduction() && production > 0) {
            reportPanel.add(createNumberLabel(production, "report.labour.potentialProduction.tooltip"), "cell " + PRODUCTION_COLUMN + " " + row);
        }
    }

    private void addUnitTypes() {
        int row = 1;

        JButton allColonistsButton = createUnitNameButton(Messages.message("report.labour.allColonists"), labourData.getSummary());
        reportPanel.add(allColonistsButton, "cell " + COLONY_COLUMN + " " + row + " 1 " + labourData.getSummary().getUnitSummaryRowCount());

        row = addLocationData(labourData.getSummary().getTotal(), null, row);

        for (UnitType unitType : LabourData.getLabourTypes(getMyPlayer())) {
            LabourData.UnitData unitData = labourData.getUnitData(unitType);

            JButton unitButton = createUnitNameButton(unitData.getUnitName(), unitData);
            int rows = unitData.getUnitSummaryRowCount();
            reportPanel.add(unitButton, "cell " + COLONY_COLUMN + " " + row + " 1 " + rows);

            if (unitData.hasDetails()) {
                row = addLocationData(unitData.getTotal(), null, row);
            } else {
                unitButton.setEnabled(false);
                unitButton.setDisabledIcon(unitButton.getIcon());
                unitButton.setForeground(Color.GRAY);

                reportPanel.add(createEmptyLabel(), "cell " + UNIT_TYPE_COLUMN + " " + row + " " + (COLUMNS - 1) + " 1");
                row++;
            }
        }
    }

    private JButton createButton(String name, ActionListener listener) {
        JButton button = new JButton(name);
        button.setMargin(new Insets(0, 0, 0, 0));
        button.setOpaque(false);
        button.setHorizontalAlignment(SwingConstants.LEADING);
        button.setForeground(LINK_COLOR);
        button.setBorder(LEFTCELLBORDER);
        button.addActionListener(listener);
        return button;
    }

    private JLabel createEmptyLabel() {
        JLabel empty = new JLabel("");
        empty.setBorder(CELLBORDER);
        return empty;
    }

    private JLabel createNonCountedLabel(int otherAmateurs) {
        JLabel label = createNumberLabel(otherAmateurs, "report.labour.notCounted.tooltip");
        label.setForeground(Color.GRAY);
        return label;
    }

    private JLabel createNumberLabel(int number, String toolTipKey) {
        JLabel label = new JLabel(String.valueOf(number));
        label.setHorizontalAlignment(SwingConstants.TRAILING);
        label.setBorder(CELLBORDER);
        if (toolTipKey != null) {
            label.setToolTipText(Messages.message(toolTipKey));
        }
        return label;
    }

    private JButton createUnitNameButton(String name, final LabourData.UnitData unitData) {
        JButton button = createButton(name, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                getGUI().showCompactLabourReport(unitData);
            }
        });

        if (!unitData.isSummary()) {
            button.setIcon(getUnitIcon(unitData.getUnitType()));
        }

        return button;
    }

    private GoodsType getGoodsType() {
        return isSummary() ? null : unitData.getUnitType().getExpertProduction();
    }

    private ImageIcon getUnitIcon(UnitType unit) {
        Unit.Role role = Unit.Role.DEFAULT;
        if (unit.hasAbility(Ability.EXPERT_PIONEER)) {
            role = Unit.Role.PIONEER;
        } else if (unit.hasAbility(Ability.EXPERT_MISSIONARY)) {
            role = Unit.Role.MISSIONARY;
        }

        return getLibrary().getUnitImageIcon(unit, role);
    }

    /**
     * @return if this is the location summary, grouped by unit type
     */
    private boolean isOverview() {
        return unitData == null;
    }

    /**
     * @return if we are any summary
     */
    private boolean isSummary() {
        return isOverview() || unitData.isSummary();
    }


}
