package scene3d;



import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g3d.Environment;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.SnapshotArray;


public class Group3d extends Actor3d{
	private final SnapshotArray<Actor3d> children = new SnapshotArray<Actor3d>(true, 4, Actor3d.class);
	public int visibleCount;
	
	public Group3d(){
		super();
	}
	
	public Group3d(Model model){
		super(model);
	}
	
	public void act (float delta) {
        super.act(delta);
        Actor3d[] actors = children.begin();
        for(int i = 0, n = children.size; i < n; i++){
        	actors[i].act(delta);
        }
        children.end();
	}
	
	/** Draws the group and its children. The default implementation calls {@link #applyTransform(Batch, Matrix4)} if needed, then
	 * {@link #drawChildren(Batch, float)}, then {@link #resetTransform(Batch)} if needed. */
	@Override
	public void draw(ModelBatch modelBatch, Environment environment) {
		super.draw(modelBatch, environment);
		drawChildren(modelBatch, environment);
	}

	
	public void drawChildren(ModelBatch modelBatch, Environment environment){
	     //modelBatch.render(children, environment); maybe faster 
	     SnapshotArray<Actor3d> children = this.children;
		 Actor3d[] actors = children.begin();
		 visibleCount = 0;
		 for (int i = 0, n = children.size; i < n; i++){
			 if(actors[i] instanceof Group3d){
	    		 ((Group3d) actors[i]).drawChildren(modelBatch, environment);
	    	 }
			 else{
					float offsetX = x, offsetY = y, offsetZ = z;
					float offsetScaleX = scaleX, offsetScaleY = scaleY, offsetScaleZ = scaleZ;
					float offsetYaw = yaw, offsetPitch = pitch, offsetRoll = roll;
					x = 0;
					y = 0;
					z = 0;
					scaleX = 0;
					scaleY = 0;
					scaleZ = 0;
					yaw = 0;
					pitch = 0;
					roll = 0;
					Actor3d child = actors[i];
					if (!child.isVisible()) continue;
					/*Matrix4 diff = sub(child.getTransform(), getTransform());
					Matrix4 childMatrix = child.getTransform().cpy();
					child.getTransform().set(add(diff, childMatrix));
					child.draw(modelBatch, environment);*/
					float cx = child.x, cy = child.y, cz = child.z;
					float sx = child.scaleX, sy = child.scaleY, sz = child.scaleZ;
					float ry = child.yaw, rp = child.pitch, rr = child.roll;
					//child.x = cx + offsetX;
					//child.y = cy + offsetY;
					//child.z = cz + offsetZ;
					child.setPosition(cx + offsetX, cy + offsetY, cz + offsetZ);
					child.setScale(sx + offsetScaleX, sy + offsetScaleY, sz + offsetScaleZ);
					child.setRotation(ry + offsetYaw, rp + offsetPitch, rr +offsetRoll);
			        if (child.isCullable(getStage3d().getCamera())) {
			        	child.draw(modelBatch, environment);
			            visibleCount++;
			        }
					child.x = cx;
					child.y = cy;
					child.z = cz;
					x = offsetX;
					y = offsetY;
					z = offsetZ;
					child.scaleX = sx;
					child.scaleY = sy;
					child.scaleZ = sz;
					scaleX = offsetScaleX;
					scaleY = offsetScaleY;
					scaleZ = offsetScaleZ;
					child.yaw = ry;
					child.pitch = rp;
					child.roll = rr;
					yaw = offsetYaw;
					pitch = offsetPitch;
					roll = offsetRoll;
			 }
		 }
		 children.end();
	}

    /** Adds an actor as a child of this group. The actor is first removed from its parent group, if any.
     * @see #remove() */
    public void addActor3d(Actor3d actor3d) {
         actor3d.remove();
         children.add(actor3d);
         actor3d.setParent(this);
         actor3d.setStage3d(getStage3d());
         childrenChanged();
    }
    
    /** Removes an actor from this group. If the actor will not be used again and has actions, they should be
     * {@link Actor#clearActions3d() cleared} so the actions will be returned to their
     * {@link Action#setPool(com.badlogic.gdx.utils.Pool) pool}, if any. This is not done automatically. */
    public boolean removeActor3d(Actor3d actor3d) {
            if (!children.removeValue(actor3d, true)) return false;
            Stage3d stage = getStage3d();
            if (stage != null) stage.unfocus(actor3d);
            actor3d.setParent(null);
            actor3d.setStage3d(null);
            childrenChanged();
            return true;
    }
    
    /** Called when actors are added to or removed from the group. */
    protected void childrenChanged () {
    }
    
    /** Removes all actors from this group. */
    public void clearChildren () {
            Actor3d[] actors = children.begin();
            for (int i = 0, n = children.size; i < n; i++) {
                    Actor3d child = actors[i];
                    child.setStage3d(null);
                    child.setParent(null);
            }
            children.end();
            children.clear();
            childrenChanged();
    }

    /** Removes all children, actions, and listeners from this group. */
    public void clear () {
            super.clear();
            clearChildren();
    }

    /** Returns the first actor found with the specified name. Note this recursively compares the name of every actor in the group. */
    public Actor3d findActor (String name) {
            Array<Actor3d> children = this.children;
            for (int i = 0, n = children.size; i < n; i++)
                    if (name.equals(children.get(i).getName())) return children.get(i);
            for (int i = 0, n = children.size; i < n; i++) {
                    Actor3d child = children.get(i);
                    if (child instanceof Group3d) {
                            Actor3d actor = ((Group3d)child).findActor(name);
                            if (actor != null) return actor;
                    }
            }
            return null;
    }
    
    @Override
    protected void setStage3d (Stage3d stage3d) {
        super.setStage3d(stage3d);
        Array<Actor3d> children = this.children;
        for (int i = 0, n = children.size; i < n; i++)
            children.get(i).setStage3d(stage3d);
    }
    
    /** Returns an ordered list of child actors in this group. */
    public SnapshotArray<Actor3d> getChildren () {
    	return children;
    }
    
    public boolean hasChildren () {
    	return children.size > 0;
    }
    
    /** Prints the actor hierarchy recursively for debugging purposes. */
    public void print () {
            print("");
    }

    private void print (String indent) {
            Actor3d[] actors = children.begin();
            for (int i = 0, n = children.size; i < n; i++) {
                    System.out.println(indent + actors[i]);
                    if (actors[i] instanceof Group3d) ((Group3d)actors[i]).print(indent + "|  ");
            }
            children.end();
    }

	@Override
	public void dispose() {
		super.dispose();
		for(Actor3d actor3d: children)
			actor3d.dispose();
	}
}