
var SkeletalPieceCollectionStatics = {};
SkeletalPieceCollectionStatics.UniquePieceID = 0;

SkeletalPieceCollectionStatics.SkeletalPieceCollectionCache = {};

Engine.SkeletalPieceCollection = class
{
	constructor()
	{
        this.piecesByName = {};
        this.replacements = {};
	}

	setReplacement(source, replacement)
	{
		this.replacements[source] = replacement;
	}

	clearReplacement(source, replacement)
	{
		if (this.replacements[source] !== undefined) delete this.replacements[source];
	}

	clearReplacements()
	{
		this.replacements = {};
	}

	/* Adds all the unqiue pieces from a graphic def (that is type skeletal) to the collection. */
	addPiecesFromGraphicDef(graphicDef)
	{
		for (var pieces of graphicDef.sequences)
		{
			for (var i = 0; i < pieces.length; i++)
			{
				var piece = this.addPiece(pieces[i], graphicDef);
			}
		}
	}

	/* Adds a new piece to the piece set by name, only one unqiue piece allowed per name */
	addPiece(pieceData, graphicDef)
	{
		
		if (pieceData.key === undefined) pieceData.key = pieceData.x+"_"+pieceData.y+"_"+pieceData.texture_index;
		if (pieceData.n === undefined) pieceData.n = pieceData.key;
		
		var name = pieceData.n;
		var key = pieceData.key;

		if (this.piecesByName[name] !== undefined) return;


		var piece = null;
		var textureIndex = pieceData.texture_index;

		let texture = new PIXI.Texture(graphicDef.baseTextures[pieceData.texture_index]);
		piece = new PIXI.Sprite(texture);
		piece.texture.frame = graphicDef.frames[key].frame;

		piece.anchor.x = graphicDef.frames[key].pivot.x;
		piece.anchor.y = graphicDef.frames[key].pivot.y;

		piece.visible = true;
		piece.piece_in_use = false;
		piece.piece_name = name;
		piece.piece_source_frame = graphicDef.frames[key];

		if (this.piecesByName[name] === undefined) this.piecesByName[name] = [];
		this.piecesByName[name].push(piece);
		//console.log(name);

		return piece;
	}


	/* Clones a piece for the cases where the same piece appears more than once on a given frame but with a different transform */
	clonePiece(sourcePiece)
	{
		var piece = new PIXI.Sprite(sourcePiece.texture);

		piece.texture.frame = sourcePiece.piece_source_frame.frame;
		piece.anchor.x = sourcePiece.piece_source_frame.pivot.x;
		piece.anchor.y = sourcePiece.piece_source_frame.pivot.y;
		piece.visible = true;
		piece.piece_in_use = false;
		piece.piece_name = sourcePiece.piece_name;

		if (this.piecesByName[sourcePiece.piece_name] === undefined) this.piecesByName[sourcePiece.piece_name] = [];
		this.piecesByName[sourcePiece.piece_name].push(piece);
		return piece;
	}

	getPieceByName(name)
	{
		if (this.replacements[name] !== undefined && this.replacements[name] != "") name = this.replacements[name];

		var pieceVisible = true;
		if (this.replacements[name] !== undefined && this.replacements[name] == "") pieceVisible = false;

		var availableCopiesOfPiece = this.piecesByName[name];
		if (availableCopiesOfPiece == null)
		{
			Debug.Log("Error no copies of piece, was the graphic loaded?!");
			return null;
		}


		var piece = null;
		for (var i = 0; i < availableCopiesOfPiece.length; i++)
		{
			if (!availableCopiesOfPiece[i].piece_in_use)
			{
				piece = availableCopiesOfPiece[i];
				break;
			}
		}

		if (piece == null)
		{
			piece = this.clonePiece(availableCopiesOfPiece[0]);
		}

		piece.visible = pieceVisible;
		piece.piece_in_use = true;
		return piece;
	}

	clearUsedPieces()
	{
		for (var pieces of Object.values(this.piecesByName))
		{
			for (var piece of pieces)
			{
				piece.visible = false;
				piece.piece_in_use = false;
			}
		}
	}

	clearAndRemove()
	{
		for (var pieces of Object.values(this.piecesByName))
		{
			for (var piece of pieces)
			{
				piece.visible = false;
				piece.piece_in_use = false;
				if (piece.parent != null) piece.parent.removeChild(piece);
			}
		}
	}

}

Engine.SkeletalAnimationControllerReplaceable = class
{

	static replaceControllerOnDrawable(drawable)
	{
		if (drawable.skeletalController == null)
		{
			Debug.ErrorLog("Drawable did not contain a skeletal controller to replace.");
			return;
		}

		drawable.skeletalController.destroy();
		drawable.skeletalController = new Engine.SkeletalAnimationControllerReplaceable(drawable.graphicData);
		drawable.addChild(drawable.skeletalController.group);
	}

	constructor(graphicDef)
	{
		this.graphicDef = graphicDef;

		if (SkeletalPieceCollectionStatics.SkeletalPieceCollectionCache[this.graphicDef.name] === undefined)
		{
			SkeletalPieceCollectionStatics.SkeletalPieceCollectionCache[this.graphicDef.name] = new Engine.ObjectPool(Engine.SkeletalPieceCollection);
		}
		var piecePool = SkeletalPieceCollectionStatics.SkeletalPieceCollectionCache[this.graphicDef.name];

		this.pieceCollection = piecePool.getNextObject();// new Engine.SkeletalPieceCollection();
		this.pieceCollection.addPiecesFromGraphicDef(graphicDef);

        this.group = new PIXI.Container();
	}

	setFrame(sequence, frame)
	{
		//this.clearDebug();
		this.clearUsedPieces();

		this.pieces = this.graphicDef.sequences[sequence];
		if (this.pieces === undefined){
			Debug.LogError("Attempt to set non existant sequence "+sequence+" on "+this.graphicDef.name);
			return;
		}

		for (var i = 0; i < this.pieces.length; i++)
		{
			var frameInfo = this.pieces[i].sequence[frame];
			if (frameInfo === undefined){
				Debug.LogError("Attempt to set non existant frame "+frame+" on "+this.graphicDef.name);
				return;
			}
			if (frameInfo.used)
			{
				var piece = piece = this.pieceCollection.getPieceByName(this.pieces[i].n);
				if (piece == null) continue;
				
				var rot = frameInfo.rot;
				var xScaleNeg = frameInfo.sx < 0;
				var yScaleNeg = frameInfo.sy < 0;
				if (xScaleNeg != yScaleNeg) rot *= -1;
				piece.rotation = rot;
				piece.x = frameInfo.tx;
				piece.y = frameInfo.ty;
				piece.z = frameInfo.z;
				piece.zOrder = piece.z;
				piece.scale.x = frameInfo.sx;
				piece.scale.y = frameInfo.sy;
				if (piece.parent != this.group) this.group.addChild(piece);

				//this.addDebug(piece, frameInfo, this.pieces[i]);
			}
		}

		this.group.children.sort((a, b) => {
		    if(a.zOrder === b.zOrder) return 0;
		    else return (a.zOrder < b.zOrder ? -1 : 1);
		});

	}

	clearDebug()
	{
		if (this.activeDebug !== undefined){
			for(var i = 0; i < this.activeDebug.length; i++){
				this.activeDebug[i].parent.removeChild(this.activeDebug[i]);
			}
			this.activeDebug.length = 0;
		}
	}

	addDebug(piece, frameInfo, pieceInfo)
	{
		if (pieceInfo.n.indexOf("Unknown") == -1) {
			//piece.visible = false;
			return;
		}

		if (this.debugClips === undefined) {
			this.debugClips = new Engine.ObjectPool(Engine.DrawableText);
			this.activeDebug = [];
		}
		var clip = this.debugClips.getNextObject();
		this.activeDebug.push(clip);
		//clip.setText(pieceInfo.n +" "+frameInfo.tx+" "+frameInfo.ty, "Verdana", 24);

		clip.setText(pieceInfo.n, "Verdana", 24);
		clip.scale.x = 1/5;
		clip.scale.y = 1/5;
		piece.addChild(clip);
	}

	clearUsedPieces()
	{
		this.pieceCollection.clearUsedPieces();
	}

	destroy()
	{
		this.pieceCollection.clearAndRemove();
		SkeletalPieceCollectionStatics.SkeletalPieceCollectionCache[this.graphicDef.name].returnObject(this.pieceCollection);

		this.graphicDef = null;

		if (this.group.parent != null) this.group.parent.removeChild(this.group);
        this.group = null;
	}

}
