package be.msyben.vinylManager.gui.list.leftList;

import be.msyben.vinylManager.data.Data;
import be.msyben.vinylManager.entities.Album;
import be.msyben.vinylManager.entities.Collection;
import be.msyben.vinylManager.properties.Preferences;
import be.msyben.vinylManager.tools.Sort;
import be.msyben.vinylManager.tools.internalEntities.TreeSortColumn;
import java.awt.Component;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;


/**
 * Affichage de la liste de gauche sous forme d'arbre.
 * Les options de l'arbre sont définies dans l'écran de gestion des préférences.
 * @author Mathieu Syben
 */
public class VinylManagerTree extends AbstractLeftList {

    /** uid     */
    private static final long serialVersionUID = 8469562624388733L;
    /**
     * L'arbre.
     */
    private JTree tree;
    /**
     * Le panel.
     */
    private JScrollPane panel;
    /**
     * Le noeud principal.
     */
    private MyTreeNode root;

    /**
     * Constrcuteur.
     */
    public VinylManagerTree() {
        root = new MyTreeNode("Collection");
        tree = new JTree(root);
        tree.setRootVisible(true);
        tree.setShowsRootHandles(true);
        tree.setEditable(false);
        tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);

        tree.addTreeSelectionListener(new TreeSelectionListener() {

            @Override
            public void valueChanged(TreeSelectionEvent e) {
                TreePath path = tree.getSelectionPath();
                if (path == null) {
                    selection(new ArrayList<Album>());
                } else {
                    MyTreeNode node = (MyTreeNode) tree.getLastSelectedPathComponent();
                    nodeSelection(node);
                }
            }
        });
        panel = new JScrollPane(tree);
    }

    /**
     * Sélection d'un noeud.
     * @param node Le noeud sélectionné.
     */
    private void nodeSelection(MyTreeNode node) {
        if (node.isLeaf()) {
            selection(node.getAlbums());
        } else {
            if (node.isRoot()) {
                if (Preferences.getInstance().get(Preferences.TREE_DISPLAY_ROOT, "N").equals("Y")) {
                    selection(node.getAllAlbumsWithSubs());
                } else {
                    selection(node.getAlbums());
                }

            } else {
                if (Preferences.getInstance().get(Preferences.TREE_DISPLAY_SUB, "N").equals("Y")) {
                    selection(node.getAllAlbumsWithSubs());
                } else {
                    selection(node.getAlbums());
                }
            }
        }
    }

    @Override
    public void updateList() {
        updateList(null);
    }

    @Override
    public void updateList(Album selectedAlbum) {
        Collection c = Data.getInstance().getCollection();
        List<Album> all = c.getAlbums();
        String critere1Sort = TreeSortColumn.getFieldNameForSorting(Integer.parseInt(Preferences.getInstance().get(Preferences.TREE_CRITERE_1, "0")));
        String critere2Sort = TreeSortColumn.getFieldNameForSorting(Integer.parseInt(Preferences.getInstance().get(Preferences.TREE_CRITERE_2, "0")));
        String critere3Sort = TreeSortColumn.getFieldNameForSorting(Integer.parseInt(Preferences.getInstance().get(Preferences.TREE_CRITERE_3, "0")));
        if (critere3Sort != null && !critere3Sort.isEmpty()) {
            Sort.sort(all, Preferences.getInstance().get(Preferences.SORT_ARTIST_DESC, "N").equals("Y"), critere1Sort, critere2Sort, critere3Sort);
        } else if (critere2Sort != null && !critere2Sort.isEmpty()) {
            Sort.sort(all, Preferences.getInstance().get(Preferences.SORT_ARTIST_DESC, "N").equals("Y"), critere1Sort, critere2Sort);
        } else if (critere1Sort != null && !critere1Sort.isEmpty()) {
            Sort.sort(all, Preferences.getInstance().get(Preferences.SORT_ARTIST_DESC, "N").equals("Y"), critere1Sort);
        } else {
            Sort.sort(all, "artisteForSorting");
        }


        root.clear();

        MyTreeNode selection = null;
        for (Album album : all) {
            MyTreeNode node = addAlbum(album);
            if (selectedAlbum != null && selection == null && selectedAlbum.equals(album) && node != null) {
                selection = node;
            }
        }

        tree.expandRow(0);

        tree.updateUI();

        panel.repaint();

        if (selection != null) {
            tree.setSelectionPath(new TreePath(selection.getPath()));
            setScrollPosition();
        }

    }

    /**
     * Ajoute un album à l'arbre.
     * @param album l'album à ajouter.
     * @return Le noeud contenant l'album.
     */
    private MyTreeNode addAlbum(Album album) {

        MyTreeNode toAdd = null;

        int critere1;
        int critere2;
        int critere3;
        try {
            critere1 = Integer.parseInt(Preferences.getInstance().get(Preferences.TREE_CRITERE_1));
        } catch (NumberFormatException ex) {
            critere1 = 1;
        }
        try {
            critere2 = Integer.parseInt(Preferences.getInstance().get(Preferences.TREE_CRITERE_2));
        } catch (NumberFormatException ex) {
            critere2 = 0;
        }
        try {
            critere3 = Integer.parseInt(Preferences.getInstance().get(Preferences.TREE_CRITERE_3));
        } catch (NumberFormatException ex) {
            critere3 = 0;
        }

        if (critere1 > 0) {
            String value1 = TreeSortColumn.getDisplay(album, Integer.parseInt(Preferences.getInstance().get(Preferences.TREE_CRITERE_1, "0")));
            int idx = root.getIndex(value1);
            if (idx >= 0) {
                toAdd = (MyTreeNode) root.getChildAt(idx);
            } else {
                toAdd = new MyTreeNode(value1);
                root.addChild(toAdd);
            }
        }
        if (critere2 > 0) {
            String value2 = TreeSortColumn.getDisplay(album, Integer.parseInt(Preferences.getInstance().get(Preferences.TREE_CRITERE_2, "0")));
            int idx = toAdd.getIndex(value2);
            if (idx >= 0) {
                toAdd = (MyTreeNode) toAdd.getChildAt(idx);
            } else {
                MyTreeNode node = new MyTreeNode(value2);
                toAdd.addChild(node);
                toAdd = node;
            }
        }
        if (critere3 > 0) {
            String value3 = TreeSortColumn.getDisplay(album, Integer.parseInt(Preferences.getInstance().get(Preferences.TREE_CRITERE_3, "0")));
            int idx = toAdd.getIndex(value3);
            if (idx >= 0) {
                toAdd = (MyTreeNode) toAdd.getChildAt(idx);
            } else {
                MyTreeNode node = new MyTreeNode(value3);
                toAdd.addChild(node);
                toAdd = node;
            }
        }

        if (toAdd != null) {
            toAdd.addAlbum(album);
        }
        return toAdd;

    }

    @Override
    public void updateListAfterDeleteAlbum(Album deletedAlbum) {
        MyTreeNode node = (MyTreeNode) tree.getLastSelectedPathComponent();
        updateList();
        if (node.getAlbums().contains(deletedAlbum)) {
            MyTreeNode[] path = node.getPath();

            MyTreeNode toSelect = root;
            for (int i = 1; i < path.length; i++) {
                int idx = toSelect.getIndex(path[i].getDisplayText());
                if (idx >= 0) {
                    MyTreeNode nn = (MyTreeNode) toSelect.getChildAt(idx);
                    if (nn != null) {
                        toSelect = nn;
                    } else {
                        break;
                    }
                } else {
                    break;
                }
            }
            tree.setSelectionPath(new TreePath(toSelect.getPath()));
        } else {
            tree.setSelectionPath(new TreePath(root));
        }
        setScrollPosition();
    }

    /**
     * Fait le scroll du panel jusqu'à la sélection.
     */
    private void setScrollPosition() {
        if (tree.getRowCount() > 0) {
            double position = (tree.getMinSelectionRow() * 1.0) / (tree.getRowCount() * 1.0);
            int positionNeeded = (int) (panel.getVerticalScrollBar().getMaximum() * position);
            panel.getVerticalScrollBar().setValue(positionNeeded);
        }
    }

    @Override
    public Component getComponent() {
        return panel;
    }

    @Override
    public void setEnabled(boolean enabled) {
        panel.setEnabled(enabled);
        tree.setEnabled(enabled);
    }

    @Override
    public String getTitle() {
        return "";
    }
}
/**
 * Classe représentant un noeud.
 * @author Mathieu Syben
 */
class MyTreeNode implements TreeNode, Serializable {

    /** uid     */
    private static final long serialVersionUID = 6987336633L;
    /** Le parent */
    private MyTreeNode parent;
    /** Le texte à afficher */
    private String displayText;
    /** Les fils */
    private List<MyTreeNode> subNodes = new ArrayList<MyTreeNode>();
    /** Les albums du noeud */
    private List<Album> albums = new ArrayList<Album>();

    /**
     * Constructeur.
     */
    private MyTreeNode() {
    }

    /**
     * Constructeur.
     * @param displayText Le texte à afficher.
     */
    public MyTreeNode(String displayText) {
        this.displayText = displayText;
    }

    /**
     * Récupère la liste des albums.
     * @return la liste des albums.
     */
    public List<Album> getAlbums() {
        return this.albums;
    }

    /**
     * Ajoute un fils.
     * @param node le fils.
     */
    public void addChild(MyTreeNode node) {
        node.setParent(this);
        subNodes.add(node);
    }

    /**
     * Ajoute un album.
     * @param album l'album.
     */
    public void addAlbum(Album album) {
        albums.add(album);
    }

    /**
     * Définit le parent.
     * @param parent le parent.
     */
    public void setParent(MyTreeNode parent) {
        this.parent = parent;
    }

    @Override
    public String toString() {
        return displayText;
    }

    @Override
    public TreeNode getChildAt(int childIndex) {
        return subNodes.get(childIndex);
    }

    @Override
    public int getChildCount() {
        return subNodes.size();
    }

    @Override
    public TreeNode getParent() {
        return parent;
    }

    @Override
    public int getIndex(TreeNode node) {
        if (node instanceof MyTreeNode) {
            MyTreeNode mnode = (MyTreeNode) node;
            for (int i = 0; i < subNodes.size(); i++) {
                MyTreeNode sub = subNodes.get(i);
                if (sub.getDisplayText().equalsIgnoreCase(mnode.getDisplayText())) {
                    return i;
                }
            }
            return -1;
        } else {
            return -1;
        }
    }

    /**
     * Récupère l'index en fonction du texte à afficher.
     * @param display le texte.
     * @return l'index. -1 si inconnu.
     */
    public int getIndex(String display) {

        for (int i = 0; i < subNodes.size(); i++) {
            MyTreeNode sub = subNodes.get(i);
            if (sub.getDisplayText().equalsIgnoreCase(display)) {
                return i;
            }
        }
        return -1;
    }

    @Override
    public boolean getAllowsChildren() {
        return true;
    }

    @Override
    public boolean isLeaf() {
        return getChildCount() == 0;
    }

    /**
     * Indique si racine
     * @return true si racine, false sinon.
     */
    public boolean isRoot() {
        return parent == null;
    }

    @Override
    public Enumeration<? extends TreeNode> children() {
        return Collections.enumeration(subNodes);
    }

    /**
     * Vide.
     */
    public void clear() {
        subNodes.clear();
        albums.clear();
    }

    /**
     * Récupère le texte à afficher.
     * @return le texte à afficher.
     */
    public String getDisplayText() {
        return this.displayText;
    }

    /**
     * Récupère la path.
     * @return Le path depuis la racine jusqu'au noeud courant.
     */
    public MyTreeNode[] getPath() {
        if (parent == null) {
            return new MyTreeNode[]{this};
        } else {
            MyTreeNode[] ancestor = parent.getPath();
            MyTreeNode[] current = new MyTreeNode[ancestor.length + 1];
            System.arraycopy(ancestor, 0, current, 0, ancestor.length);
            current[current.length - 1] = this;
            return current;
        }
    }

    /**
     * Renvoie la liste de tous les albums, inclus ceux des sous-noeuds.
     * @return la liste de tous les albums, inclus ceux des sous-noeuds.
     */
    public List<Album> getAllAlbumsWithSubs() {
        List<Album> ret = new ArrayList<>();
        ret.addAll(getAlbums());
        for (MyTreeNode node : subNodes) {
            ret.addAll(node.getAllAlbumsWithSubs());
        }
        return ret;
    }
}
