package net.sf.jabref.gui.importer.actions;

import java.awt.Font;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.swing.BoxLayout;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import net.sf.jabref.Globals;
import net.sf.jabref.gui.BasePanel;
import net.sf.jabref.gui.customentrytypes.CustomEntryTypesManager;
import net.sf.jabref.logic.importer.ParserResult;
import net.sf.jabref.logic.l10n.Localization;
import net.sf.jabref.model.EntryTypes;
import net.sf.jabref.model.database.BibDatabaseMode;
import net.sf.jabref.model.entry.CustomEntryType;
import net.sf.jabref.model.entry.EntryType;

/**
 * This action checks whether any new custom entry types were loaded from this
 * BIB file. If so, an offer to remember these entry types is given.
 */
public class CheckForNewEntryTypesAction implements PostOpenAction {

    @Override
    public boolean isActionNecessary(ParserResult parserResult) {
        return !getListOfUnknownAndUnequalCustomizations(parserResult).isEmpty();
    }

    @Override
    public void performAction(BasePanel panel, ParserResult parserResult) {

        BibDatabaseMode mode = getBibDatabaseModeFromParserResult(parserResult);

        List<EntryType> typesToStore = determineEntryTypesToSave(panel, getListOfUnknownAndUnequalCustomizations(parserResult), mode);

        if (!typesToStore.isEmpty()) {
            typesToStore.forEach(type -> EntryTypes.addOrModifyCustomEntryType((CustomEntryType) type, mode));
            CustomEntryTypesManager.saveCustomEntryTypes(Globals.prefs);
        }
    }

    private List<EntryType> getListOfUnknownAndUnequalCustomizations(ParserResult parserResult) {
        BibDatabaseMode mode = getBibDatabaseModeFromParserResult(parserResult);

        return parserResult.getEntryTypes().values().stream()
                .filter(type ->
                    (!EntryTypes.getType(type.getName(), mode).isPresent())
                        || !EntryTypes.isEqualNameAndFieldBased(type, EntryTypes.getType(type.getName(), mode).get()))
                .collect(Collectors.toList());
    }

    private List<EntryType> determineEntryTypesToSave(BasePanel panel, List<EntryType> allCustomizedEntryTypes, BibDatabaseMode databaseMode) {
        List<EntryType> newTypes = new ArrayList<>();
        List<EntryType> differentCustomizations = new ArrayList<>();

        for (EntryType customType : allCustomizedEntryTypes) {
            if (!EntryTypes.getType(customType.getName(), databaseMode).isPresent()) {
                newTypes.add(customType);
            } else {
                EntryType currentlyStoredType = EntryTypes.getType(customType.getName(), databaseMode).get();
                if (!EntryTypes.isEqualNameAndFieldBased(customType, currentlyStoredType)) {
                    differentCustomizations.add(customType);
                }
            }
        }

        Map<EntryType, JCheckBox> typeCheckBoxMap = new HashMap<>();

        JPanel checkboxPanel = createCheckBoxPanel(newTypes, differentCustomizations, typeCheckBoxMap);

        int answer = JOptionPane.showConfirmDialog(panel.frame(),
                checkboxPanel,
                Localization.lang("Custom entry types"),
                JOptionPane.OK_CANCEL_OPTION,
                JOptionPane.QUESTION_MESSAGE);

        if (answer == JOptionPane.YES_OPTION) {
            return typeCheckBoxMap.entrySet().stream().filter(entry -> entry.getValue().isSelected())
                    .map(Map.Entry::getKey).collect(Collectors.toList());
        } else {
            return Collections.emptyList();
        }

    }

    private JPanel createCheckBoxPanel(List<EntryType> newTypes, List<EntryType> differentCustomizations,
            Map<EntryType, JCheckBox> typeCheckBoxMap) {
        JPanel checkboxPanel = new JPanel();
        checkboxPanel.setLayout(new BoxLayout(checkboxPanel, BoxLayout.PAGE_AXIS));

        JLabel customFoundLabel = new JLabel(Localization.lang("Custom entry types found in file")+".");
        Font boldStandardFont = new Font(customFoundLabel.getFont().getFontName(), Font.BOLD, customFoundLabel.getFont().getSize());
        customFoundLabel.setFont(boldStandardFont);
        checkboxPanel.add(customFoundLabel);

        JLabel selectLabel = new JLabel(Localization.lang("Select all customized types to be stored in local preferences")+":");
        selectLabel.setFont(boldStandardFont);
        checkboxPanel.add(selectLabel);

        checkboxPanel.add(new JLabel(" "));

        // add all unknown types:
        if (!newTypes.isEmpty()) {
            checkboxPanel.add(new JLabel(Localization.lang("Currently unknown") + ":"));
            for (EntryType type : newTypes) {
                JCheckBox box = new JCheckBox(type.getName(), true);
                checkboxPanel.add(box);
                typeCheckBoxMap.put(type, box);
            }
        }

        // add all different customizations
        if (!differentCustomizations.isEmpty()) {
            checkboxPanel.add(new JLabel(Localization.lang("Different customization, current settings will be overwritten") + ":"));
            for (EntryType type : differentCustomizations) {
                JCheckBox box = new JCheckBox(type.getName(), true);
                checkboxPanel.add(box);
                typeCheckBoxMap.put(type, box);
            }
        }
        return checkboxPanel;
    }

    private BibDatabaseMode getBibDatabaseModeFromParserResult(ParserResult parserResult) {
        return parserResult.getMetaData().getMode().orElse(Globals.prefs.getDefaultBibDatabaseMode());
    }
}
