package electrosphere.renderer.anim;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
import org.joml.Matrix4f;
import org.joml.Quaternionf;
import org.joml.Vector3f;

/**
 *
 * @author amaterasu
 */
public class AnimChannel {
    double timeCurrent;
    double timeTotal;
    public String nodeID;
    
    
    public Vector3f startingPosition;
    public ArrayList<Keyframe> positionFrame;
    public ListIterator<Keyframe> positionFrameIterator;
    Keyframe positionFrameCurrent;
    Keyframe positionFrameNext;
    double nextPositionTime;
    
    
    public Quaternionf startingRotation;
    public ArrayList<Keyframe> rotationFrame;
    public ListIterator<Keyframe> rotationFrameIterator;
    Keyframe rotationFrameCurrent;
    Keyframe rotationFrameNext;
    double nextRotationTime;
    
    
    public ArrayList<Keyframe> scaleFrame;
    public ListIterator<Keyframe> scaleFrameIterator;
    Keyframe scaleFrameCurrent;
    Keyframe scaleFrameNext;
    double nextScaleTime;
    
    
    public AnimChannel(double maxTime){
        timeTotal = maxTime;
    }
    
    public Vector3f getCurrentPosition(){
        Vector3f rVal = new Vector3f();
        if(positionFrameCurrent != null){
            if(positionFrameNext != null){
                double percent_Next = ((timeCurrent - positionFrameCurrent.time) / (positionFrameNext.time - positionFrameCurrent.time));
                rVal = new Vector3f().add(new Vector3f().add(positionFrameCurrent.position).mul((float)(1.0 - percent_Next))).add(new Vector3f().add(positionFrameNext.position).mul((float)(percent_Next)));
            } else {
                rVal = new Vector3f().add(positionFrameCurrent.position);
            }
//            rVal.add(new Vector3f().add(positionFrameCurrent.position).mul(()/(positionFrameNext.time - positionFrameCurrent.time)));
        }
//        rVal = rVal.sub(startingPosition);
        return rVal;
    }
    
    public Quaternionf getCurrentRotation(){
        Quaternionf rVal = new Quaternionf();
        if(rotationFrameCurrent != null){
            if(rotationFrameNext != null){
                double percent_Next = ((timeCurrent - rotationFrameCurrent.time) / (rotationFrameNext.time - rotationFrameCurrent.time));
//                rVal = new Quaternionf(rotationFrameCurrent.rotation).normalize();//.add(rotationFrameCurrent.rotation);
                rVal = new Quaternionf(rotationFrameCurrent.rotation).slerp(rotationFrameNext.rotation, (float)percent_Next);
//                rVal = new Quaternionf(rotationFrameCurrent.rotation).normalize().slerp(new Quaternionf(rotationFrameNext.rotation).normalize(), (float)(percent_Next)).rotateAxis((float)(-Math.PI/2), new Vector3f(1,0,0));
            } else {
                rVal = new Quaternionf(rotationFrameCurrent.rotation);//.add(rotationFrameCurrent.rotation);
            }
        }
//        rVal = rVal.mul(new Quaternionf(startingRotation).invert());
        return rVal;
    }
    
    public Vector3f getCurrentScale(){
        Vector3f rVal = new Vector3f();
        if(scaleFrameCurrent != null){
            if(scaleFrameNext != null){
                double percent_Next = ((timeCurrent - scaleFrameCurrent.time) / (scaleFrameNext.time - scaleFrameCurrent.time));
                rVal = new Vector3f().add(new Vector3f().add(scaleFrameCurrent.scale).mul((float)(1.0 - percent_Next))).add(new Vector3f().add(scaleFrameNext.scale).mul((float)(percent_Next)));
            } else {
                rVal = new Vector3f().add(scaleFrameCurrent.scale);
            }
//            rVal.add(new Vector3f().add(positionFrameCurrent.position).mul(()/(positionFrameNext.time - positionFrameCurrent.time)));
        }
        return rVal;
    }
    
    public void incrementTime(double incrementValue){
        timeCurrent = timeCurrent + incrementValue;
        while(positionFrameNext != null && timeCurrent > positionFrameNext.time){
            positionFrameCurrent = positionFrameNext;
            if(positionFrameIterator.hasNext()){
                positionFrameNext = positionFrameIterator.next();
            } else {
                positionFrameNext = null;
            }
        }
        while(rotationFrameNext != null && timeCurrent > rotationFrameNext.time){
            rotationFrameCurrent = rotationFrameNext;
            if(rotationFrameIterator.hasNext()){
                rotationFrameNext = rotationFrameIterator.next();
            } else {
                rotationFrameNext = null;
            }
        }
        while(scaleFrameNext != null && timeCurrent > scaleFrameNext.time){
            scaleFrameCurrent = scaleFrameNext;
            if(scaleFrameIterator.hasNext()){
                scaleFrameNext = scaleFrameIterator.next();
            } else {
                scaleFrameNext = null;
            }
        }
    }
    
    public void rewind(){
        timeCurrent = 0;
        
        //TODO: Make sure has at least two frames
        positionFrameIterator = positionFrame.listIterator();
        if(positionFrameIterator.hasNext()){
            positionFrameCurrent =  positionFrameIterator.next();
            if(positionFrameIterator.hasNext()){
                positionFrameNext =     positionFrameIterator.next();
                nextPositionTime = positionFrameNext.time;
            } else {
                positionFrameNext = null;
            }
        }
        
        rotationFrameIterator = rotationFrame.listIterator();
        if(rotationFrameIterator.hasNext()){
            rotationFrameCurrent =  rotationFrameIterator.next();
            if(rotationFrameIterator.hasNext()){
                rotationFrameNext =     rotationFrameIterator.next();
            } else {
                rotationFrameNext = null;
            }
        }
        
        scaleFrameIterator = scaleFrame.listIterator();
        if(scaleFrameIterator.hasNext()){
            scaleFrameCurrent =  scaleFrameIterator.next();
            if(scaleFrameIterator.hasNext()){
                scaleFrameNext =     scaleFrameIterator.next();
            } else {
                scaleFrameNext = null;
            }
        }
    }
    
    public void describeChannel(){
        System.out.println("Target object: " + nodeID);
        System.out.println("Time: " + timeCurrent + "/" + timeTotal);
        System.out.println(positionFrame.size() + " position Frames");
        System.out.println(rotationFrame.size() + " rotation Frames");
        System.out.println(scaleFrame.size() + " scale Frames");
    }
    
    public void fullDescribeChannel(){
        System.out.println("Target object: " + nodeID);
        System.out.println("Time: " + timeCurrent + "/" + timeTotal);
        System.out.println(positionFrame.size() + " position Frames");
        Iterator<Keyframe> frameIterator = positionFrame.iterator();
        while(frameIterator.hasNext()){
            System.out.println(frameIterator.next());
        }
        System.out.println(rotationFrame.size() + " rotation Frames");
        frameIterator = rotationFrame.iterator();
        while(frameIterator.hasNext()){
            System.out.println(frameIterator.next());
        }
        System.out.println(scaleFrame.size() + " scale Frames");
        frameIterator = scaleFrame.iterator();
        while(frameIterator.hasNext()){
            System.out.println(frameIterator.next());
        }
    }
    
}
