package cz.spiffyk.flpmanager.application.controls.tags;

import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import cz.spiffyk.flpmanager.data.Tag;
import cz.spiffyk.flpmanager.util.FXUtils;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import lombok.Getter;
import lombok.NonNull;

/**
 * A HBox showing tags in the given model
 * @author spiffyk
 */
public class TagsViewer extends HBox implements ListChangeListener<Tag> {
	
	/**
	 * The {@link Tag}s to render
	 */
	@Getter private ObservableList<Tag> tags;
	
	/**
	 * The list of {@link Label}s representing the {@link Tag}s
	 */
	private List<TagLabel> labels = new ArrayList<>();
	
	
	
	/**
	 * Creates a new tags viewer
	 */
	public TagsViewer() {
		super();
		this.getStyleClass().add("tags-viewer");
		this.setSpacing(3);
	}
	
	
	
	/**
	 * Sets the list of {@link Tag}s that this viewer should observe. If a list is already being observed, the viewer
	 * is automatically unregistered as a listener from that list.
	 * @param tags The list to observe
	 */
	public void setTags(ObservableList<Tag> tags) {
		if (this.tags != null) {
			this.tags.removeListener(this);
		}
		
		this.tags = tags;
		tags.addListener(this);
		updateLabels();
	}
	
	/**
	 * Updates labels
	 */
	private void updateLabels() {
		labels.clear();
		for (Tag tag : tags) {
			// TODO optimize this
			labels.add(new TagLabel(tag));
		}
		labels.sort((a, b) -> a.getTag().getName().compareTo(b.getTag().getName()));
		this.getChildren().clear();
		this.getChildren().addAll(labels);
	}
	
	@Override
	public void onChanged(javafx.collections.ListChangeListener.Change<? extends Tag> c) {
		updateLabels();
	}
	
	/**
	 * A label observing a {@link Tag}
	 * @author spiffyk
	 */
	private class TagLabel extends Label implements Observer {
		
		/**
		 * The {@link Tag} this label is observing
		 */
		@Getter private final Tag tag;
		
		
		
		/**
		 * Creates a new tag label
		 * @param tag
		 */
		TagLabel(@NonNull Tag tag) {
			this.tag = tag;
			this.getStyleClass().add("tag");
			tag.addObserver(this);
			update();
		}
		
		
		
		@Override
		public void update(Observable o, Object arg) {
			update();
		}
		
		/**
		 * Updates the label
		 */
		private void update() {
			this.setText(tag.getName());
			this.setStyle(FXUtils.getTagStyle(tag.getColor()));
		}
	}
}

@@ -1423,16 +1423,74 @@
 els() %7B%0A
+%09%09for (TagLabel label : labels) %7B%0A%09%09%09label.cleanUp();%0A%09%09%7D%0A
 %09%09labels
@@ -1499,16 +1499,19 @@
 lear();%0A
+%09%09%0A
 %09%09for (T
@@ -1531,33 +1531,8 @@
 ) %7B%0A
-%09%09%09// TODO optimize this%0A
 %09%09%09l
@@ -2494,13 +2494,176 @@
 ));%0A%09%09%7D%0A
+%09%09%0A%09%09/**%0A%09%09 * Stops observing the %7B@link Tag%7D and prepares for cleanup%0A%09%09 */%0A%09%09public void cleanUp() %7B%0A%09%09%09tag.deleteObserver(this);%0A%09%09%09this.setVisible(false);%0A%09%09%7D%0A
 %09%7D%0A%7D%0A

Fix TagsViewer memory leak