package it.unibz.inf.ontop.protege.query;

/*
 * #%L
 * ontop-protege4
 * %%
 * Copyright (C) 2009 - 2013 KRDB Research Centre. Free University of Bozen Bolzano.
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import it.unibz.inf.ontop.shaded.com.google.common.collect.ImmutableSet;
import it.unibz.inf.ontop.spec.mapping.PrefixManager;
import it.unibz.inf.ontop.protege.core.OntologyPrefixManager;
import it.unibz.inf.ontop.protege.utils.OntopAbstractAction;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static it.unibz.inf.ontop.protege.utils.DialogUtils.*;
import static java.awt.event.KeyEvent.*;

public class SelectPrefixesDialog extends JDialog {

	private static final long serialVersionUID = -8277829841902027620L;

	private final Map<String, String> prefixMap;
	private final ArrayList<JCheckBox> checkboxes = new ArrayList<>();

	private String directives;

	/**
	 * Extract existing prefixes (using regex) from queryString
	 * and generates additional selected prefixes if OK is chosen
	 *
	 * @param prefixManager
	 * @param queryString query
	 */

	public SelectPrefixesDialog(OntologyPrefixManager prefixManager, String queryString) {
		prefixMap = prefixManager.getPrefixMap();

		setTitle("Select Prefixes for the Query");
		setModal(true);

		setLayout(new GridBagLayout());

		JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
		controlPanel.add(getButton(selectAllAction));
		controlPanel.add(getButton(selectNoneAction));
		JButton acceptButton = getButton(acceptAction);
		controlPanel.add(acceptButton);
		add(controlPanel,
				new GridBagConstraints(0, 2, 1, 1, 0, 0,
						GridBagConstraints.SOUTHEAST, GridBagConstraints.NONE,
						new Insets(5, 5, 5, 5), 0, 0));

		JPanel prefixPanel = new JPanel(new GridBagLayout());

		ImmutableSet<String> presentPrefixes = getPresentPrefixes(queryString);

		int gridYIndex = 1;
		for (Map.Entry<String, String> e : prefixMap.entrySet()) {
			if (e.getKey().equals("version"))
				continue;

			boolean isDefaultPrefix = e.getKey().equals(PrefixManager.DEFAULT_PREFIX);
			JCheckBox checkbox = new JCheckBox(e.getKey());
			checkbox.setFont(checkbox.getFont().deriveFont(Font.BOLD));
			if (presentPrefixes.contains(e.getKey())) {
				checkbox.setSelected(true);
				checkbox.setEnabled(false);
			}
			prefixPanel.add(checkbox,
					new GridBagConstraints(0, isDefaultPrefix ? 0 : gridYIndex, 1, 1, 0, 0,
							GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
							new Insets(1, 2, 1, 2), 0, 0));

			JLabel label = new JLabel("<" + e.getValue() + ">");
			prefixPanel.add(label,
					new GridBagConstraints(1, isDefaultPrefix ? 0 : gridYIndex, 1, 1, 1, 0,
							GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
							new Insets(1, 2, 1, 2), 0, 0));

			if (isDefaultPrefix)
				checkboxes.add(0, checkbox); // default prefix at the top
			else
				checkboxes.add(checkbox);

			gridYIndex++;
		}

		prefixPanel.add(new JPanel(), // to gobble up the vertical space
				new GridBagConstraints(1, gridYIndex, 1, 1, 1, 1,
						GridBagConstraints.WEST, GridBagConstraints.BOTH,
						new Insets(0, 0, 0, 0), 0, 0));

		add(new JScrollPane(prefixPanel),
				new GridBagConstraints(0, 1, 1, 1, 1, 1,
						GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH,
						new Insets(5, 10, 5, 10), 0, 0));

		setUpAccelerator(getRootPane(), selectAllAction);
		setUpAccelerator(getRootPane(), selectNoneAction);
		setUpAccelerator(getRootPane(), cancelAction);
		getRootPane().setDefaultButton(acceptButton);
	}

	private final OntopAbstractAction acceptAction = new OntopAbstractAction(OK_BUTTON_TEXT,
			null,
			"Add selected prefixes to the query",
			null) {
		@Override
		public void actionPerformed(ActionEvent e) {
			directives = getDirectives();
			dispatchEvent(new WindowEvent(SelectPrefixesDialog.this, WindowEvent.WINDOW_CLOSING));
		}
	};

	private final OntopAbstractAction cancelAction = getStandardCloseWindowAction(CANCEL_BUTTON_TEXT, this);

	private final OntopAbstractAction selectAllAction = new OntopAbstractAction(
			"Select All",
			null,
			"Select all shown prefixes",
			getKeyStrokeWithCtrlMask(VK_A)) {
		@Override
		public void actionPerformed(ActionEvent e) {
			checkboxes.forEach(c -> c.setSelected(true));
		}
	};

	private final OntopAbstractAction selectNoneAction = new OntopAbstractAction(
			"Select None",
			null,
			"Deselect all shown prefixes",
			getKeyStrokeWithCtrlMask(VK_N)) {
		@Override
		public void actionPerformed(ActionEvent e) {
			checkboxes.stream()
					.filter(Component::isEnabled)
					.forEach(c -> c.setSelected(false));
		}
	};



	public Optional<String> getPrefixDirectives() {
		return directives == null || directives.isEmpty() ? Optional.empty() : Optional.of(directives);
	}

	private String getDirectives() {
		return checkboxes.stream()
				.filter(AbstractButton::isSelected)
				.filter(Component::isEnabled)
				.map(AbstractButton::getText)
				.map(p -> "PREFIX " + p + " <" + prefixMap.get(p) + ">\n")
				.collect(Collectors.joining());
	}

	private static final Pattern PREFIX_PATTERN = Pattern.compile("^\\s*PREFIX\\s+([a-zA-Z0-9-_.]*:)", Pattern.MULTILINE);

	private static ImmutableSet<String> getPresentPrefixes(String sparql) {
		Matcher matcher = PREFIX_PATTERN.matcher(sparql);
		ImmutableSet.Builder<String> builder = ImmutableSet.builder();
		while (matcher.find()) {
			String prefix = matcher.group(1);
			builder.add(prefix);
		}
		return builder.build();
	}
}
