//"Google Earth tour recorder"
//written by: Paul van Dinther
//            Dinther Product Design
//            Software development and specialists in simulation
//            email: vandinther@gmail.com
//class for tour recorder
//
//to use:
//
//Create instance: myrecorder = new tourRecorder(60);
//This allows up to 60 seconds worth of recording time
//
//Start recording by calling myrecorder.startRecording();
//attach a callback to the frameend even from Google Earth and track delta time between frames like:
//    var deltaTime = (now - lastMill) / 1000.0;
//    lastMill = now;
//in the same FrameEnd callback call: myrecorder.update(deltaTime);
//Call myRecorder.stoprecording(); to stop the recording
//Call myRecorder.asKml(); to obtain the recorded KML tour data.
//Altitude mode is assumed to be 'absolute'
//
//The recorder only returns the tour data. A standard KML header and footer like the example below needs to be added:
//header: <?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
//footer: </kml>
//note: the tour data must be included outside the KML <document> tag but inside the <kml> tag

    
function tourRecorder(fmaxIndex){
    this.name = 'planetinaction.com';
    this.description = 'Javascript based Google Earth tour recorder';
    this.address = 'Made in New Zealand';
    this.recording = false; //Flag showing if the recorder is recording
    this.playing = false; //Flag showing if recorder is playing back
    this.maxIndex = fmaxIndex; //Defines maximum recording length
    this.tourPoints = new Array(); //Quick internal storage array
    this.recordingIndex = 0; //Counter tracks number of recorded points (Faster than calling tourPoints.length every time)
    this.playbackIndex = 0; //Track which point to playback next.
    this.smooth = true; //Smooths camera motions between points.
    this.interval = 0.25; //Records a camera position every 'interval' seconds
    this.timeSpend = 0; //Tracks time since last point
    this.duration = 0; //tracks recording duration in seconds
    this.progress = 0; //tracks progress during playback in seconds
    this.onStopRecording = null; //event fired when recorder stops recording
    this.onStopPlayback = null; //event fired when recorder stops playback
    this.onStartRecording = null; //event fired when recorder start recording
    this.onStartPlayback = null; //event fired when recorder start playback
    this.onPausePlayback = null; //event fired when recorder pauses playback
    this.onInitFrame = null; //event fired when recorder finishes a frame init
    this.onPlayback = null; //event fired every time a frame update has been completed during playback
    this.onGetCamera = null; //event fired every time a frame is about to be recorded
    this.latitude = new easeTo(this.interval);
    this.longitude = new easeTo(this.interval);
    this.altitude = new easeTo(this.interval);
    this.heading = new circleEaseTo(this.interval);
    this.pitch = new easeTo(this.interval);
    this.roll = new easeTo(this.interval);
    this.clear = function(){
        this.stopRecording();
        this.stopPlayback();
        this.tourPoints = [];
        this.recordingIndex = 0;
        this.playbackIndex = 0;
        this.timeSpend = 0;
        this.duration = 0;
        this.progress = 0;
    }
    this.startPlayback = function(){
        if (!this.playing && !this.recording){
            this.playing = true;
            this.timeSpend = 0;
            //this.initFrame(0);
            if (this.onStartPlayback != null){ this.onStartPlayback(); }
        }
    }
    this.stopPlayback = function(){
        if (this.playing){
            this.playing = false;
            if (this.onStopPlayback != null){ this.onStopPlayback(); }
        }
    }    
    this.pausePlayblack = function(){
        if (this.playing){
            this.playing = false;
            if (this.onPausePlayback != null){ this.onPausePlayback(); }
        }
    }       
    this.startRecording = function(){
        if(!this.recording && !this.playing){
            this.clear();
            this.recording = true;
            this.duration = 0;
            if (this.onStartRecording != null){ this.onStartRecording(); }
        }
    }
    this.stopRecording = function(){
        if (this.recording){
            this.recording = false;
            this.playbackIndex = 0;
            this.progress = 0;
            if (this.onStopRecording != null){ this.onStopRecording(); }
        }
    }
    this.asKml = function(){
        var flyToMode = '';
        if (this.smooth){flyToMode = 'smooth';}
        else {flyToMode = 'bounce';}
        var kmldata = '<gx:Tour><name>'+this.name+'</name><address>'+this.address+'</address><description>'+this.description+'</description>';
        kmldata += this.asPlayList();
        kmldata += '</gx:Tour>';
        return kmldata;      
    }

    this.asPlayList = function(){
        var flyToMode = '';
        if (this.smooth){flyToMode = 'smooth';}
        else {flyToMode = 'bounce';}
        var kmldata = '<gx:Playlist>';
        //record at least one frame
        if (this.tourPoints.length < 6){
            this.storeFrame(0);
        }
        var i = 0;
        while (i < this.tourPoints.length){
            kmldata += '<gx:FlyTo><gx:flyToMode>'+flyToMode+'</gx:flyToMode><gx:duration>'+this.tourPoints[i++]+'</gx:duration><Camera><latitude>'+this.tourPoints[i++]+'</latitude><longitude>'+this.tourPoints[i++]+'</longitude><altitude>'+this.tourPoints[i++]+'</altitude><heading>'+this.tourPoints[i++]+'</heading><tilt>'+this.tourPoints[i++]+'</tilt><roll>'+this.tourPoints[i++]+'</roll><altitudeMode>absolute</altitudeMode></Camera></gx:FlyTo>';
        }  
        kmldata += '</gx:Playlist>';
        return kmldata;      
    }
      
    this.asCsv = function(){
        var kmldata = this.name+'\r\n'+this.address+'\r\n'+this.description+'\r\n';
        var i = 0;
        var fm = '0';
        if (this.smooth) {fm = '1';}
        else {fm = '0';}        
        //record at least one frame
        if (this.tourPoints.length < 6){
            this.storeFrame(0);
        }
        while (i < this.tourPoints.length){        
           kmldata += fm + ',' + this.tourPoints[i++]+','+this.tourPoints[i++]+','+this.tourPoints[i++]+','+this.tourPoints[i++]+','+this.tourPoints[i++]+','+this.tourPoints[i++]+','+this.tourPoints[i++] + '\r\n';
        }
        return kmldata;
    }    
    
    this.updateRecording = function(deltaTime){  
        if (!this.playing && this.recording){         
            this.timeSpend += deltaTime;
            this.duration += deltaTime;               
            if (this.timeSpend > this.interval){                
                this.storeFrame(this.timeSpend);
                this.timeSpend -= this.interval;
                if (this.recordingIndex > this.maxIndex){
                    this.stopRecording();
                }
            }
        }
    }
    this.storeFrame = function(deltaTime){
        if (this.onGetCamera != null){
            var myCam = this.onGetCamera();
            this.tourPoints[this.recordingIndex++] = deltaTime;
            this.tourPoints[this.recordingIndex++] = myCam.getLatitude();
            this.tourPoints[this.recordingIndex++] = myCam.getLongitude();
            this.tourPoints[this.recordingIndex++] = myCam.getAltitude();
            this.tourPoints[this.recordingIndex++] = myCam.getHeading();
            this.tourPoints[this.recordingIndex++] = myCam.getTilt();
            this.tourPoints[this.recordingIndex++] = myCam.getRoll();
        }
    }
    this.updatePlayback = function(deltaTime){
        if (!this.recording && this.playing){
            this.timeSpend += deltaTime;
            if (this.timeSpend > this.tourPoints[this.playbackIndex]){ //>this.interval
                this.timeSpend -= this.tourPoints[this.playbackIndex]; //this.interval;
                this.playFrame();
                if (this.playbackIndex > this.recordingIndex-1){
                    this.playbackIndex = 0;
                    this.stopPlayback();
                }
            }
            this.latitude.update(deltaTime);
            this.longitude.update(deltaTime);
            this.altitude.update(deltaTime);
            this.heading.update(deltaTime);
            this.pitch.update(deltaTime);
            this.roll.update(deltaTime);          
            if (this.onPlayback != null){ this.onPlayback(); };
            //var myCam = ge.getView().copyAsCamera(ge.ALTITUDE_ABSOLUTE);
            //myCam.set(this.latitude.current,this.longitude.current, this.altitude.current, ge.ALTITUDE_ABSOLUTE, this.heading.current,this.pitch.current,this.roll.current);
            //ge.getView().setAbstractView(myCam);                 
        }             
    }

    this.playFrame = function(){
        this.playbackIndex++;
        this.latitude.setTarget(this.tourPoints[this.playbackIndex++]);
        this.longitude.setTarget(this.tourPoints[this.playbackIndex++]);       
        this.altitude.setTarget(this.tourPoints[this.playbackIndex++]);     
        this.heading.setTarget(this.tourPoints[this.playbackIndex++]);      
        this.pitch.setTarget(this.tourPoints[this.playbackIndex++]);         
        this.roll.setTarget(this.tourPoints[this.playbackIndex++]);        
    }
    this.initFrame = function(frameIndex){
        this.playbackIndex = frameIndex;
        if (this.playbackIndex < this.recordingIndex -6){
            this.latitude.set(this.tourPoints[this.playbackIndex+1]);
            this.longitude.set(this.tourPoints[this.playbackIndex+2]);
            this.altitude.set(this.tourPoints[this.playbackIndex+3]);
            this.heading.set(this.tourPoints[this.playbackIndex+4]);
            this.pitch.set(this.tourPoints[this.playbackIndex+5]);
            this.roll.set(this.tourPoints[this.playbackIndex+6]);
            if (this.onInitFrame != null){ this.onInitFrame(); }            
        }
    }
    this.update = function(deltaTime){
        this.updateRecording(deltaTime);
        this.updatePlayback(deltaTime); 
    }
}    

function wrapKml(kmlData){
    return '<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">' + kmlData + '</kml>';
}

function easeTo(xinterval){
    this.target = 0;
    this.current = 0;
    this.interval = xinterval;
    this.delta = 0;
    this.update = function(deltaTime){
        this.current += this.delta * deltaTime;
    }
    this.setTarget = function(myvalue){
        this.target = myvalue;         
        this.delta = this.target - this.current;     
        this.delta = this.delta/this.interval;           
    }
    this.set = function(value){
        this.target = value;
        this.current = value;
        this.delta = 0;        
    }    
}

function circleEaseTo(xinterval){
    this.target = 0;
    this.current = 0;
    this.interval = xinterval;
    this.delta = 0;
    this.update = function(deltaTime){
        this.current += this.delta * deltaTime;
        if (this.current > 360){this.current -= 360;}
        else if (this.current < 0){this.current += 360;}
    }
    this.setTarget = function(myvalue){
        this.target = myvalue;         
        this.delta = this.target - this.current;
        if (this.delta > 180){this.delta -= 360; }   
        if (this.delta < -180){this.delta += 360;}         
        this.delta = this.delta/this.interval;           
    }
    this.set = function(value){
        this.target = value;
        this.current = value;
        this.delta = 0;
    }    
}

