/*
 * This file is part of NixNote/NeighborNote 
 * Copyright 2009,2010 Randy Baumgarte
 * Copyright 2010 Hiroshi Miura
 * 
 * This file may be licensed under the terms of of the
 * GNU General Public License Version 2 (the ``GPL'').
 *
 * Software distributed under the License is distributed
 * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
 * express or implied. See the GPL for the specific language
 * governing rights and limitations.
 *
 * You should have received a copy of the GPL along with this
 * program. If not, go to http://www.gnu.org/licenses/gpl.html
 * or write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
*/

package cx.fbn.nevernote.gui;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import com.evernote.edam.type.Note;
import com.evernote.edam.type.Tag;
import com.trolltech.qt.core.QByteArray;
import com.trolltech.qt.core.QMimeData;
import com.trolltech.qt.core.Qt;
import com.trolltech.qt.core.Qt.MatchFlag;
import com.trolltech.qt.core.Qt.MatchFlags;
import com.trolltech.qt.core.Qt.SortOrder;
import com.trolltech.qt.gui.QAbstractItemView;
import com.trolltech.qt.gui.QAction;
import com.trolltech.qt.gui.QBrush;
import com.trolltech.qt.gui.QColor;
import com.trolltech.qt.gui.QContextMenuEvent;
import com.trolltech.qt.gui.QDragEnterEvent;
import com.trolltech.qt.gui.QDragMoveEvent;
import com.trolltech.qt.gui.QHeaderView;
import com.trolltech.qt.gui.QIcon;
import com.trolltech.qt.gui.QMenu;
import com.trolltech.qt.gui.QMouseEvent;
import com.trolltech.qt.gui.QTreeWidget;
import com.trolltech.qt.gui.QTreeWidgetItem;

import cx.fbn.nevernote.Global;
import cx.fbn.nevernote.filters.TagCounter;
import cx.fbn.nevernote.signals.NoteSignal;
import cx.fbn.nevernote.signals.TagSignal;
import cx.fbn.nevernote.sql.DatabaseConnection;

public class TagTreeWidget extends QTreeWidget {
	private QAction editAction;
	private QAction deleteAction;
	private QAction addAction;
	private QAction iconAction;
	private QAction mergeAction;
	public TagSignal tagSignal;
	public NoteSignal noteSignal;
	private boolean showAllTags;
	private final DatabaseConnection db;
	private HashMap<String, QIcon>	icons;
	public Signal0 selectionSignal;
	public String selectedTag;
	private boolean rightButtonClicked;
	private List<TagCounter> lastCount;
	
	
	public TagTreeWidget(DatabaseConnection d) {
		List<String> headers = new ArrayList<String>();
		headers.add(tr("Tags"));
		headers.add("");
		showAllTags = true;
		setAcceptDrops(true);
		setDragEnabled(true);
		setColumnCount(2);
		header().setResizeMode(0, QHeaderView.ResizeMode.ResizeToContents);
		header().setResizeMode(1, QHeaderView.ResizeMode.Stretch);
		header().setMovable(false);
		header().setStyleSheet("QHeaderView::section {border: 0.0em;}");
		db = d;
		selectionSignal = new Signal0();
		tagSignal = new TagSignal();
		noteSignal = new NoteSignal();
		setDragDropMode(QAbstractItemView.DragDropMode.DragDrop);
    	setHeaderLabels(headers);

//    	setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection);
    	setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection);
    	
    	selectedTag = "";
    	itemClicked.connect(this, "itemClicked()");
		int width = Global.getColumnWidth("tagTreeName");
		if (width>0)
			setColumnWidth(0, width);

		
	}
	
	public void setEditAction(QAction e) {
		editAction = e;
	}
	public void setDeleteAction(QAction d) {
		deleteAction = d;
	}
	public void setAddAction(QAction a) {
		addAction = a;
	}
	public void setIconAction(QAction i) {
		iconAction = i;
	}
	public void setMergeAction(QAction i) { 
		mergeAction = i; }
	
	// Insert a new tag into the tree.  This is used when we dynamically add a 
	// new tag after the full tag tree has been built.  It only adds to the
	// top level.
	public void insertTag(String name, String guid) {
    	String iconPath = new String("classpath:cx/fbn/nevernote/icons/");
		QIcon icon = new QIcon(iconPath+"tag.png");
		NTreeWidgetItem child;
		Qt.Alignment ra = new Qt.Alignment(Qt.AlignmentFlag.AlignRight);
		
		// Build new tag & add it
		child = new NTreeWidgetItem();
		child.setText(0, name);
		child.setIcon(0,icon);
		child.setText(2, guid);
		child.setTextAlignment(1, ra.value());
		addTopLevelItem(child);
		
		// Resort the list
		resizeColumnToContents(0);
    	resizeColumnToContents(1);
    	sortItems(0, SortOrder.AscendingOrder);
	}
	
	private QIcon findDefaultIcon(String guid) {
    	String iconPath = new String("classpath:cx/fbn/nevernote/icons/");
		QIcon icon = new QIcon(iconPath+"tag.png");
		QIcon linkedIcon = new QIcon(iconPath+"tag-orange.png");

		if (db.getTagTable().getNotebookGuid(guid) == null || 
				db.getTagTable().getNotebookGuid(guid).equals(""))
			return icon;
		else
			return linkedIcon;
	}
	
	List<String> findExpandedTags(QTreeWidgetItem item) {
		List<String> list = new ArrayList<String>();
		if (item.isExpanded()) 
			list.add(item.text(0));
		for (int i=0; i<item.childCount(); i++) {
			List<String> childrenList = findExpandedTags(item.child(i));
			for (int j=0; j<childrenList.size(); j++) {
				list.add(childrenList.get(j));
			}
		}
		
		return list;
	}
	
	void expandTags(QTreeWidgetItem item, List<String> expandedTags) {
		for (int i=0; i<item.childCount(); i++) {
			expandTags(item.child(i), expandedTags);
		}
		
		for (int i=0; i<expandedTags.size(); i++) {
			if (expandedTags.get(i).equalsIgnoreCase(item.text(0))) {
				expandItem(item);
				i=expandedTags.size();
			}
		}
	}
	
	public void load(List<Tag> tags) {
    	Tag tag;
    	List<NTreeWidgetItem> index = new ArrayList<NTreeWidgetItem>();
    	NTreeWidgetItem child;
    	  	   	
    	/* First, let's find out which stacks are expanded */
    	QTreeWidgetItem root = 	invisibleRootItem();
    	List<String> expandedTags = findExpandedTags(root);


    	
    	//Clear out the tree & reload
    	clear();
    	String iconPath = new String("classpath:cx/fbn/nevernote/icons/");
		QIcon icon = new QIcon(iconPath+"tag.png");
    	
		Qt.Alignment ra = new Qt.Alignment(Qt.AlignmentFlag.AlignRight);
	
		// Create a copy.  We delete them out as they are found
		List<Tag> tempList = new ArrayList<Tag>();
		for (int i=0; i<tags.size(); i++) {
			tempList.add(tags.get(i));
		}
		
    	while (tempList.size() > 0) {
    		for (int i=0; i<tempList.size(); i++) {
    			tag = tempList.get(i);
    			if (tag.getParentGuid()==null || tag.getParentGuid().equals("")) {
    				child = new NTreeWidgetItem();
    				child.setText(0, tag.getName());
   		    		if (icons != null && !icons.containsKey(tag.getGuid())) {
   		    			child.setIcon(0, findDefaultIcon(tag.getGuid()));
   		    		} else {
   		    			child.setIcon(0, icons.get(tag.getGuid()));
   		    		}

    				child.setText(2, tag.getGuid());
    				child.setTextAlignment(1, ra.value());
    				index.add(child);
    				addTopLevelItem(child);
    				tempList.remove(i);
    			} else {
    				// We need to find the parent
    				for (int j=0; j<index.size(); j++) {
    					if (index.get(j).text(2).equals(tag.getParentGuid())) {
    	    				child = new NTreeWidgetItem();
    	    				child.setText(0, tag.getName());
    	    				child.setIcon(0, icon);
    	    				child.setText(2, tag.getGuid());
    	    				child.setTextAlignment(1, ra.value());
    	   		    		if (icons != null && !icons.containsKey(tag.getGuid())) {
    	   		    			child.setIcon(0, findDefaultIcon(tag.getGuid()));
    	   		    		} else {
    	   		    			child.setIcon(0, icons.get(tag.getGuid()));
    	   		    		}
    	    				tempList.remove(i);
    	    				index.add(child);    						
    	    				index.get(j).addChild(child);
    					}
    				}
    			}
    		} 
    	}
    	resizeColumnToContents(0);
    	resizeColumnToContents(1);
    	sortItems(0, SortOrder.AscendingOrder);
    	
    	expandTags(invisibleRootItem(), expandedTags);
    	if (lastCount != null)
    		updateCounts(lastCount);
	}
	// Show (unhide) all tags
	public void showAllTags(boolean value) {
		showAllTags = value;
	}

	// update the display with the current number of notes
	public void updateCounts(List<TagCounter> counts) {
				
		lastCount = counts;
		MatchFlags flags = new MatchFlags();
		flags.set(MatchFlag.MatchWildcard);
		flags.set(MatchFlag.MatchRecursive);
//		List<QTreeWidgetItem> children = new ArrayList<QTreeWidgetItem>();
		List <QTreeWidgetItem>	children = findItems("*", flags);
		
		QBrush black = new QBrush();
		black.setColor(QColor.black);
		QBrush blue = new QBrush();
		blue.setColor(QColor.blue);
		if (!Global.tagBehavior().equalsIgnoreCase("ColorActive"))
			blue.setColor(QColor.black);
		
		for (int i=0; i<children.size(); i++) {
			children.get(i).setText(1,"0");
			children.get(i).setForeground(0, black);			
			children.get(i).setForeground(1, black);
			if (!showAllTags && (Global.tagBehavior().equalsIgnoreCase("HideInactiveCount") || Global.tagBehavior().equalsIgnoreCase("NoHideInactiveCount")))
				children.get(i).setHidden(true);
			else
				children.get(i).setHidden(false);
			if (children.get(i).isSelected())
				children.get(i).setHidden(false);
		}
		for (int i=0; i<counts.size(); i++) {
			for (int j=0; j<children.size(); j++) {
				String guid = children.get(j).text(2);
				if (counts.get(i).getGuid().equals(guid)) {
					children.get(j).setText(1, new Integer(counts.get(i).getCount()).toString());
					if (counts.get(i).getCount() > 0 || children.get(j).isSelected()) {
						children.get(j).setForeground(0, blue);			
						children.get(j).setForeground(1, blue);
						QTreeWidgetItem parent = children.get(j);
						while (parent != null) {
							parent.setForeground(0, blue);			
							parent.setForeground(1, blue);
							parent.setHidden(false);
							parent = parent.parent();
						}
					}
				}
			}
		}
	}

	
	public boolean selectGuid(String guid) {
		MatchFlags flags = new MatchFlags();
		flags.set(MatchFlag.MatchWildcard);
		flags.set(MatchFlag.MatchRecursive);
//		List<QTreeWidgetItem> children = new ArrayList<QTreeWidgetItem>();
		List <QTreeWidgetItem>	children = findItems("*", flags);

		for (int i=0; i<children.size(); i++) {
			if (children.get(i).text(2).equals(guid)) {
				children.get(i).setSelected(true);
				return true;
			}
		}
		return false;
	}
	
	 @Override
	 protected void dragMoveEvent(QDragMoveEvent event) {
		if (event.mimeData().hasFormat("application/x-nevernote-note")) {
			if (event.answerRect().intersects(childrenRect()))
				event.acceptProposedAction();
			return;
		}
	 }

	
	@Override
	public void dragEnterEvent(QDragEnterEvent event) {
		if (event.mimeData().hasFormat("application/x-nevernote-note")) {
			event.accept();
			return;
		}
		if (event.source() == this) {
			if (Global.tagBehavior().equals("HideInactiveCount")) {
				event.ignore();
				return;
			}
			event.mimeData().setData("application/x-nevernote-tag", new QByteArray(currentItem().text(2)));
			event.accept();
			return;
		}
		event.ignore();
	}

	@Override
	public boolean dropMimeData(QTreeWidgetItem parent, int index, QMimeData data, Qt.DropAction action) {
		if (data.hasFormat("application/x-nevernote-tag")) {
			QByteArray d = data.data("application/x-nevernote-tag");
			String current = d.toString();
			
			// Check we don't do a dumb thing like move a parent to a child of itself
			if (!checkParent(parent, current))
				return false;
			QTreeWidgetItem newChild;
			if (parent == null) {
//				tagSignal.changeParent.emit(current, "");
				db.getTagTable().updateTagParent(current, "");
				newChild = new QTreeWidgetItem(this);
			} else {
//				tagSignal.changeParent.emit(current, parent.text(2));
				db.getTagTable().updateTagParent(current, parent.text(2));
				newChild = new QTreeWidgetItem(parent);
			}
			copyTreeItem(currentItem(), newChild);
			currentItem().setHidden(true);
			sortItems(0, SortOrder.AscendingOrder);
			return true;
		}
		
		// If we are dropping a note
		if (data.hasFormat("application/x-nevernote-note")) {
			String notebookGuid = db.getTagTable().getNotebookGuid(parent.text(2));
			QByteArray d = data.data("application/x-nevernote-note");
			String s = d.toString();
			String noteGuidArray[] = s.split(" ");
			for (String element : noteGuidArray) {
				Note n = db.getNoteTable().getNote(element.trim(), false, false, false, false, false);
				
				// Check that...
				// 1.) Check that tag isn't already assigned to that note
				// 2.) Check that that tag is valid for that notebook or the tag isn't notebook specific
				// 3.) Check that the notebook isn't read only.
				if (!db.getNoteTable().noteTagsTable.checkNoteNoteTags(element.trim(), parent.text(2)) &&
						(notebookGuid == null || n.getNotebookGuid().equalsIgnoreCase(notebookGuid) || notebookGuid.equals("")) &&
						!db.getNotebookTable().isReadOnly(n.getNotebookGuid())) {
					db.getNoteTable().noteTagsTable.saveNoteTag(element.trim(), parent.text(2), true);
					noteSignal.tagsAdded.emit(element.trim(), parent.text(2));
				}
			}
			//tagSignal.listChanged.emit();
			
			return true;
		}
		return false;
	}
	
	@Override
	public void contextMenuEvent(QContextMenuEvent event) {
		QMenu menu = new QMenu(this);
		menu.addAction(addAction);
		menu.addAction(editAction);
		menu.addAction(deleteAction);
		menu.addAction(mergeAction);
		menu.addSeparator();
		menu.addAction(iconAction);
		menu.exec(event.globalPos());
	}
	
	public void setIcons(HashMap<String, QIcon> i) {
		icons = i;
	}
	
	// Copy an individual item within the tree.  I need to do this because
	// Qt doesn't call the dropMimeData on a move, just a copy.
	private void copyTreeItem(QTreeWidgetItem source, QTreeWidgetItem target) {
		target.setText(0, source.text(0));
		target.setIcon(0, source.icon(0));
		target.setText(1, source.text(1));
		target.setText(2, source.text(2));
		Qt.Alignment ra = new Qt.Alignment(Qt.AlignmentFlag.AlignRight);
		target.setTextAlignment(1, ra.value());
		
		for (int i=0; i<source.childCount(); i++) {
			QTreeWidgetItem newChild = new QTreeWidgetItem(target);
			copyTreeItem(source.child(i), newChild);
			source.child(i).setHidden(true);
		}
		return;
	}
	
	// Check that we don't copy a parent as a child of a current child.
	private boolean checkParent(QTreeWidgetItem parent, String child) {
		if (parent != null)
			if (parent.text(2).equals(child))
				return false;
		if (parent == null)
			return true;
		return checkParent(parent.parent(), child);
	}


	public void selectTag(QTreeWidgetItem item) {
		MatchFlags flags = new MatchFlags();
		flags.set(MatchFlag.MatchWildcard);
		flags.set(MatchFlag.MatchRecursive);
		List <QTreeWidgetItem>	children = findItems("*", flags);
		
		for (int j=0; j<children.size(); j++) {
			String guid = children.get(j).text(2);
			if (item.text(2).equals(guid)) {
				children.get(j).setSelected(true);
			}
		}
	}

	@SuppressWarnings("unused")
	private void itemClicked() {
		
		List<QTreeWidgetItem> selectedItem = selectedItems();
		if (selectedItem.size() == 1) {
			if (selectedItem.get(0).text(0).equalsIgnoreCase(selectedTag) && !rightButtonClicked) {
				selectedTag = "";
				clearSelection();
			} else {
				selectedTag = selectedItem.get(0).text(0);
			}
			
		}
		selectionSignal.emit();
	}

	
	@Override
	public void mousePressEvent(QMouseEvent e) {
		if (e.button() == Qt.MouseButton.RightButton)
			rightButtonClicked = true;
		else
			rightButtonClicked = false;
		super.mousePressEvent(e);
	}

	
}
