package org.richfaces.realworld.tree;

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;
import javax.faces.model.SelectItemGroup;
import javax.imageio.ImageIO;

import org.ajax4jsf.context.AjaxContext;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Synchronized;
import org.richfaces.component.UIDataTable;
import org.richfaces.component.UIDatascroller;
import org.richfaces.component.UIDragSupport;
import org.richfaces.component.UITree;
import org.richfaces.component.UITreeNode;
import org.richfaces.component.html.HtmlTree;
import org.richfaces.event.DataScrollerEvent;
import org.richfaces.event.DropEvent;
import org.richfaces.event.NodeSelectedEvent;
import org.richfaces.model.TreeNode;
import org.richfaces.model.TreeRowKey;
import org.richfaces.realworld.domain.Album;
import org.richfaces.realworld.domain.Image;
import org.richfaces.realworld.domain.User;
import org.richfaces.realworld.fileupload.FileManager;
import org.richfaces.realworld.util.NavigationEnum;

@Name("treeSelectionManager")
@Synchronized(timeout=2000)
@Scope(ScopeType.SESSION)
public class TreeSelectionManager {

	private static final FacesContext FacesContext = null;

	@In
	private User user;
	
	private NavigationEnum mainArea = NavigationEnum.IMAGE_PREVIEW;
	
	private Image selectedImage;

	private Album selectedAlbum;
	
	private int selectedImageIndex = 1;
	
	@In(create=true)
	FileManager fileManager;
	
	@In(create=true)
	TreeActionManager treeActionManager;

	private String nodeTitle;

	public Album getSelectedAlbum() {
		return selectedAlbum;
	}

	public void showFileUpload(TreeAlbumItem item){
		this.setSelectedAlbum(item.getAlbum());
		if(item.isLeaf()){
			this.setSelectedImage(null);
		}else{
			this.setSelectedImage(((TreeImageItem)(item.getImages().get(0))).getImage());
		}
		this.setMainArea(NavigationEnum.FILE_UPLOAD);
	}
	
	public void scrollerListener(DataScrollerEvent event) {
		List<Image> images = this.getSelectedAlbum().getImages();
		if (event.getNewScrolVal().equals("previous")) {
			for(int index = 0 ; index < images.size(); index++){
				if(images.get(index) == this.getSelectedImage()){
					this.setSelectedImage(images.get(index - 1));
					this.setSelectedImageIndex(index);
				}
			}
		} else if (event.getNewScrolVal().equals("last")) {
			this.setSelectedImage(images.get(images.size() - 1));
			this.setSelectedImageIndex(images.size());
		} else if (event.getNewScrolVal().equals("next")) {
			for(int index = 0 ; index < images.size(); index++){
				if(images.get(index) == this.getSelectedImage()){
						this.setSelectedImage(images.get(index + 1));
						this.setSelectedImageIndex(index + 2);
						return;
				}
			}
		} else if (event.getNewScrolVal().equals("first")) {
			this.setSelectedImage(images.get(0));
			this.setSelectedImageIndex(1);
		}

	}
	
	public synchronized void paint(OutputStream out, Object data) throws IOException{
		if (null == data) {
			return;
		}
		File imageResource = fileManager.getImage(user.getLogin() + fileManager.getFileSeparator() + this.getSelectedAlbum().getName() + fileManager.getFileSeparator() + data.toString());
		if (imageResource != null) {
			FileInputStream fileInputStream = new FileInputStream(imageResource);
			BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
			InputStream paintData = bufferedInputStream;
			if (null == paintData) {
				// throw new Exception("");
				// TODO
			}
			try{
			BufferedImage image = ImageIO.read(paintData);
			ImageIO.write(image, "jpeg", out);
			}catch(Exception e){
				
			}finally{
				fileInputStream.close();
				bufferedInputStream.close();
				paintData.close();
			}
		}
	}
	
	public synchronized void paintAvatar(OutputStream out, Object data) throws IOException{
		if (null == data) {
			return;
		}
		File imageResource = fileManager.getImage(user.getLogin() + fileManager.getFileSeparator() + "avatars" + fileManager.getFileSeparator() + data.toString());
		if (imageResource != null) {
			FileInputStream fileInputStream = new FileInputStream(imageResource);
			BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
			InputStream paintData = bufferedInputStream;
			if (null == paintData) {
				// throw new Exception("");
				// TODO
			}
			try{
			BufferedImage image = ImageIO.read(paintData);
			ImageIO.write(image, "jpeg", out);
			}catch(Exception e){
				
			}finally{
				fileInputStream.close();
				bufferedInputStream.close();
				paintData.close();
			}
		}
	}
	
	public synchronized void paintFriendAvatar(OutputStream out, Object data) throws IOException{
		if (null == data) {
			return;
		}
		String temp = data.toString();
		String[] array = temp.split(":");
		File imageResource = fileManager.getImage(array[0] + fileManager.getFileSeparator() + "avatars" + fileManager.getFileSeparator() + array[1]);
		if (imageResource != null) {
			FileInputStream fileInputStream = new FileInputStream(imageResource);
			BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
			InputStream paintData = bufferedInputStream;
			if (null == paintData) {
				// throw new Exception("");
				// TODO
			}
			try{
			BufferedImage image = ImageIO.read(paintData);
			ImageIO.write(image, "jpeg", out);
			}catch(Exception e){
				
			}finally{
				fileInputStream.close();
				bufferedInputStream.close();
				paintData.close();
			}
		}
	}
	
	public void dropListener(DropEvent dropEvent) {
		
		// resolve drag destination attributes
		UITreeNode destNode = (dropEvent.getSource() instanceof UITreeNode) ? (UITreeNode) dropEvent.getSource() : null;
		UITree destTree = destNode != null ? destNode.getUITree() : null;
		TreeRowKey dropNodeKey = (dropEvent.getDropValue() instanceof TreeRowKey) ? (TreeRowKey) dropEvent.getDropValue() : null;
		TreeNode droppedInNode = dropNodeKey != null ? destTree.getTreeNode(dropNodeKey) : null;
		
		// resolve drag source attributes
		UITreeNode srcNode = (dropEvent.getDraggableSource() instanceof UITreeNode) ? (UITreeNode) dropEvent.getDraggableSource() : null;
		UITree srcTree = srcNode != null ? srcNode.getUITree() : null;
		TreeRowKey dragNodeKey = (dropEvent.getDragValue() instanceof TreeRowKey) ? (TreeRowKey) dropEvent.getDragValue() : null;
		TreeNode draggedNode = dragNodeKey != null ? srcTree.getTreeNode(dragNodeKey) : null;
		if (dropEvent.getDraggableSource() instanceof UIDragSupport && srcTree == null && draggedNode == null && dropEvent.getDragValue() instanceof TreeNode) {	    
		    srcTree = destTree;
		    draggedNode = (TreeNode) dropEvent.getDragValue();
		    dragNodeKey = srcTree.getTreeNodeRowKey(draggedNode) instanceof TreeRowKey ? (TreeRowKey) srcTree.getTreeNodeRowKey(draggedNode) : null;
		}

		// Note: check if we dropped node on to itself or to item instead of
		// folder here
		if (droppedInNode != null && (droppedInNode.equals(draggedNode) || droppedInNode.getParent().getParent() != null || draggedNode.getParent().getParent() == null)) {
		    System.out.println("Warning: Can't drop on itself or to pic itself! Also can't move folders");
		    return;
		}

		if (dropNodeKey != null) {
		    // add destination node for rerender
		    destTree.addRequestKey(dropNodeKey);

		    Object state = null;
		    if (dragNodeKey != null) { // Drag from this or other tree
			TreeNode parentNode = draggedNode.getParent();
			// 1. remove node from tree
			state = srcTree.removeNode(dragNodeKey);
			// 2. add parent for rerender
			Object rowKey = srcTree.getTreeNodeRowKey(parentNode);
			srcTree.addRequestKey(rowKey);
			//TODO refactor this code
			if (dropEvent.getDraggableSource() instanceof UIDragSupport) {
			    // if node was gragged in it's parent place dragged node to
			    // the end of selected nodes in grid
			    if (droppedInNode.equals(parentNode)) {
			    }
			}
		    } else if (dropEvent.getDragValue() != null) { // Drag from some
								    // drag source
			draggedNode.setData(dropEvent.getDragValue().toString());
		    }

		    // generate new node id
		    Object id = getNewId(destTree.getTreeNode(dropNodeKey));
		    destTree.addNode(dropNodeKey, draggedNode, id, state);
		}

		AjaxContext ac = AjaxContext.getCurrentInstance();
		ac.addRenderedArea("mainform:imageList");
		Image draggedImage = ((TreeImageItem)draggedNode).getImage();
		draggedImage.setAlbumName(((TreeAlbumItem)droppedInNode).getAlbum().getName());
		treeActionManager.editImage(draggedImage);
		// Add destination tree to reRender
		try {
		    ac.addComponentToAjaxRender(destTree);
		} catch (Exception e) {
		    System.err.print(e.getMessage());
		}
		
	    }
	
	private Object getNewId(TreeNode parentNode) {
		Map<Object, TreeNode> childs = new HashMap<Object, TreeNode>();
		Iterator<Map.Entry<Object, TreeNode>> iter = parentNode.getChildren();
		while (iter != null && iter.hasNext()) {
		    Map.Entry<Object, TreeNode> entry = iter.next();
		    childs.put(entry.getKey(), entry.getValue());
		}

		Integer index = 1;
		while (childs.containsKey(index)) {
		    index++;
		}
		return index;
	    }

	public Image getSelectedImage() {
		return selectedImage;
	}

	public void setSelectedImage(Image selectedImage) {
		this.selectedImage = selectedImage;
	}

	public void setSelectedAlbum(Album selectedAlbum) {
		this.selectedAlbum = selectedAlbum;
	}

	public Boolean adviseNodeSelected(UITree tree) {
		TreeRowKey treeRowKey = (TreeRowKey) tree.getRowKey();
		TreeNode treeNode = (TreeNode) tree.getRowData(treeRowKey);
		
		if(treeNode instanceof TreeAlbumItem){
			TreeAlbumItem currentNode = (TreeAlbumItem)treeNode;
			if(this.getSelectedAlbum() != null){
				return currentNode.getAlbum() == this.getSelectedAlbum() && null == this.getSelectedImage() ? Boolean.TRUE: Boolean.FALSE;
			}else{
				this.setSelectedAlbum(currentNode.getAlbum());
				if(currentNode.isLeaf()){
					return Boolean.TRUE ;
				}else{
					this.setSelectedImage(((TreeImageItem)(currentNode.getImages().get(0))).getImage());
					return Boolean.FALSE ;
				}
			}
		}else if(treeNode instanceof TreeImageItem){
			return ((TreeImageItem)treeNode).getImage() == this.getSelectedImage() ? Boolean.TRUE : Boolean.FALSE;
		}
		return null;
	}

	public Boolean adviseNodeOpened(UITree tree) {
		TreeRowKey treeRowKey = (TreeRowKey) tree.getRowKey();
		TreeNode treeNode = (TreeNode) tree.getRowData(treeRowKey);
		if(treeNode instanceof TreeAlbumItem){
			TreeAlbumItem currentNode = (TreeAlbumItem)treeNode;
			if(this.getSelectedAlbum() != null){
				return currentNode.getAlbum() == this.getSelectedAlbum() ? Boolean.TRUE: Boolean.FALSE;
			}else{
				this.setSelectedAlbum(currentNode.getAlbum());
				if(!currentNode.isLeaf()){
					this.setSelectedImage(((TreeImageItem)currentNode.getImages().get(0)).getImage());
				}
				return Boolean.TRUE ;
			}
		}
		return null;
	}

	public void processSelection(NodeSelectedEvent event) {
		HtmlTree tree = (HtmlTree) event.getComponent();
		if (tree.getRowData() instanceof TreeImageItem) {
			nodeTitle = ((TreeImageItem) (tree.getRowData())).getImage()
					.getName();
		} else if (tree.getRowData() instanceof TreeAlbumItem) {
			nodeTitle = ((TreeAlbumItem) (tree.getRowData())).getAlbum()
					.getName();
		}
		TreeNode<String> currentNode = tree.getModelTreeNode(tree.getRowKey());
		if(currentNode instanceof TreeImageItem){
				Image image = ((TreeImageItem)currentNode).getImage();
				this.setSelectedImage(image);
				Album album = ((TreeAlbumItem)currentNode.getParent()).getAlbum();
				if(album != this.getSelectedAlbum()){
					this.setSelectedAlbum(album);
				}
				int index = album.getIndex(image);
				setDataScrollerIndex(index);
				return;
		}else if(currentNode instanceof TreeAlbumItem){
			TreeAlbumItem node = (TreeAlbumItem)currentNode;
			setDataScrollerIndex(0);
			if (node.isLeaf()) {
				this.setSelectedImage(null);
				Album album = node.getAlbum();
				this.setSelectedAlbum(album);
			}else{
				if(node.getAlbum() == this.getSelectedAlbum()){
					return;
				}else{
					setSelectedAlbum(node.getAlbum());
						this.setSelectedImage(node.getAlbum().getImages().get(0));
				}		
			}
		}
	}

	public void updateScroller(ActionEvent event){
		this.setDataScrollerIndex(this.selectedImageIndex - 1);
	}
	
	private void setDataScrollerIndex(int index) {
		UIComponent component = FacesContext.getCurrentInstance().getViewRoot();
		UIDatascroller scroller = (UIDatascroller)component.findComponent("mainform:sc1");
		scroller.setPage(index+1);
		this.setSelectedImageIndex(index+1);
	}

	public String getNodeTitle() {
		return nodeTitle;
	}

	public void setNodeTitle(String nodeTitle) {
		this.nodeTitle = nodeTitle;
	}

	public NavigationEnum getMainArea() {
		return mainArea;
	}

	public void setMainArea(NavigationEnum mainArea) {
		this.mainArea = mainArea;
	}

	public int getSelectedImageIndex() {
		return selectedImageIndex;
	}

	public void setSelectedImageIndex(int selectedImageIndex) {
		this.selectedImageIndex = selectedImageIndex;
	}
	
	public SelectItem[] getAvailableIndexOfImages(){
		SelectItem[] group = new SelectItem[this.getSelectedAlbum().getImages().size()];
		for(int i = 0; i < this.getSelectedAlbum().getImages().size(); i++){
			group[i] = new SelectItem(i+1);
		}
		return group;
	}
}
