package thaumcraft.api.visnet;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;

import thaumcraft.api.TileThaumcraft;
import thaumcraft.api.WorldCoordinates;
import thaumcraft.api.aspects.Aspect;

/**
 * @author Azanor
 * 
 * The tile entity used by nodes in the vis energy network. A node is either a source (like an aura node),
 * a transport relay or vis receiver (like the infernal furnace).
 *
 */
public abstract class TileVisNode extends TileThaumcraft {
	
	WeakReference<TileVisNode> parent = null;
	ArrayList<WeakReference<TileVisNode>> children = new ArrayList<WeakReference<TileVisNode>>();
	
	/**
	 * @return the WorldCoordinates location of where this node is located
	 */
	public WorldCoordinates getLocation() {
		return new WorldCoordinates(this);
	}
	
	/**
	 * @return the number of blocks away this node will check for parent nodes to connect to. 
	 */
	public abstract int getRange();
	
	/**
	 * @return true if this is the source or root node of the vis network. 
	 */
	public abstract boolean isSource();
		
	/**
	 * This method should never be called directly. Use VisNetHandler.drainVis() instead
	 * @param aspect what aspect to drain
	 * @param vis how much to drain
	 * @return how much was actually drained
	 */
	public int consumeVis(Aspect aspect, int vis) {
		if (VisNetHandler.isNodeValid(getParent())) {
			int out = getParent().get().consumeVis(aspect, vis);
			if (out>0) {
				triggerConsumeEffect(aspect);
			}
			return out;
		}
		return 0;
	}
	
	public void removeThisNode() {
		for (WeakReference<TileVisNode> n:getChildren()) {
			if (n!=null && n.get()!=null) {
				n.get().removeThisNode();
			}
		}	
		
		children = new ArrayList<WeakReference<TileVisNode>>();
		if (VisNetHandler.isNodeValid(this.getParent())) {
			this.getParent().get().nodeRefresh=true;
		}
		this.setParent(null);
		this.parentChanged();
		
		if (this.isSource()) {
			HashMap<WorldCoordinates, WeakReference<TileVisNode>> sourcelist = VisNetHandler.sources.get(worldObj.provider.dimensionId);
			if (sourcelist==null) {
				sourcelist = new HashMap<WorldCoordinates, WeakReference<TileVisNode>>();
			}
			sourcelist.remove(getLocation());
			VisNetHandler.sources.put( worldObj.provider.dimensionId, sourcelist );
		}
		
		worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
	}
	
	

	@Override
	public void invalidate() {
		removeThisNode();
		super.invalidate();
	}

	public void triggerConsumeEffect(Aspect aspect) {	}

	/**
	 * @return
	 */
	public WeakReference<TileVisNode> getParent() {
		return parent;
	}
	
	/**
	 * @return
	 */
	public WeakReference<TileVisNode> getRootSource() {
		return VisNetHandler.isNodeValid(getParent()) ? 
				getParent().get().getRootSource() : this.isSource() ?
						new WeakReference(this) : null;
	}
	
	/**
	 * @param parent
	 */
	public void setParent(WeakReference<TileVisNode> parent) {
		this.parent = parent;
	}
	
	/**
	 * @return
	 */
	public ArrayList<WeakReference<TileVisNode>> getChildren() {
		return children;
	}
	
	@Override
	public boolean canUpdate() {
		return true;
	}
	
	protected int nodeCounter = 0;
	private boolean nodeRegged = false;
	public boolean nodeRefresh = false;

	@Override
	public void updateEntity() {
				
		if (!worldObj.isRemote && ((nodeCounter++) % 40==0 || nodeRefresh)) {
			//check for changes
			if (!nodeRefresh && children.size()>0) {
				for (WeakReference<TileVisNode> n:children) {
					if (n==null || n.get()==null) {
						nodeRefresh=true;
						break;
					}
				}			
			}
			
			//refresh linked nodes
			if (nodeRefresh) {
				for (WeakReference<TileVisNode> n:children) {
					if (n.get()!=null) {
						n.get().nodeRefresh=true;
					}
				}
				children.clear();
				parent=null;
			}
			
			//redo stuff
			if (isSource() && !nodeRegged) {
				VisNetHandler.addSource(getWorldObj(), this);
				nodeRegged = true;
			} else 
			if (!isSource() && !VisNetHandler.isNodeValid(getParent())) {
				setParent(VisNetHandler.addNode(getWorldObj(), this));				
				nodeRefresh=true;
			}
			
			if (nodeRefresh) {
				worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
				parentChanged();
			}
			nodeRefresh=false;
		}
		
	}
	
	public void parentChanged() { }
	
	/**
	 * @return the type of shard this is attuned to:
	 * none -1, air 0, fire 1, water 2, earth 3, order 4, entropy 5
	 * Should return -1 for most implementations
	 */
	public byte getAttunement() {
		return -1;
	}
		
	
}