// Version 1.0.0
//
// The contents of this file are subject to the Mozilla Public License
// Version 1.1 (the "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
//
// Alternatively, you may redistribute this library, use and/or modify it under the terms of the
// GNU Lesser General Public License as published by the Free Software Foundation;
// either version 2.1 of the License, or (at your option) any later version.
// You may obtain a copy of the LGPL at http://www.gnu.org/copyleft/.
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
// specific language governing rights and limitations under the License.
//
// The original code is geairlines.js, released October 1, 2006.
//
// The initial developer of the original code is Luca Rocchi (Rome, Italy, www.googleearthairlines.com,www.musicdyne.com),
// (luca.rocchi@tin.it, lr1313@gmail.com).

//var list=new Array();
var serverpath='http://www.googleearthairlines.com/';
var responder=serverpath+'js/kmlAirlines.php';
var imagepath=serverpath+'images/';
var xmlheader='<?xml version="1.0" encoding="UTF-8"?>'+'\n';
var kmlheader='<kml xmlns="http://earth.google.com/kml/2.1">'+'\n';
var geap=null;//new GEAPlugin();
//var plug=null;
var copyright=null;
//var objectContent='<object id="iePlug" classid="clsid:CA538B16-F9BB-44A6-96FB-5C487C12869E" ></object>';
//var embedContent ='<embed id="mozPlug" pluginspage="http://www.googleearthairlines.com/download" type="application/kml-plugin" oninit="javascript:initReady()" onclick=""></embed>';
//var setupContent ='<a href="http://www.googleearthairlines.com/gexsetup4.exe"><img src="http://www.googleearthairlines.com/images/install_geairlines_plugin.png"></a>';

//-----------------------------------------
function GEAPlugin() {
   this.initialized=false;
   this.id="GEAirlines Plugin";
   this.targetId="";
   this.log='';
   this.viewAngle=0.0;  //camera angle from direction
   this.stopped=true;
   this.streamingMode=false;
   this.frames=25;
   this.autopilotspeed=5.0;
   this.onClock=undefined;
   this.onClick=undefined;
   this.initCallback=undefined;
   this.loadQueue=new Array();
   this.snapshots=new Array();
   this.clock=0;
   this.elapsedTime=0;

   this.metersCounter=0.0;
   this.camera=null;
   this.direction=0.0;  //fly direction
   this.debugMode=false;
   this.changed=false;
}

GEAPlugin.prototype.init=function(){
   this.camera=new Camera();
   copyright=new ScreenOverlay('geacopyright.png');
   copyright.draworder=0;
   copyright.visibility=1;
   copyright.overlayY=0;
   copyright.overlayX=0;
   copyright.screenY=0;
   copyright.screenX=0;
   //this.openKml(copyright);//.load();
   this.initialized=true;
   //this.autoPilotSpeed(5.0);
   //if (geap.initCallback!=undefined){
   //   geap.initCallback();
   //}
   //setTimeout('clockTimer()',1000);
   //this.trace("initialization completed...\n");
   //this.trace(plug.getParamValue('version')+"\n");
   this.debugMode =false;
}

GEAPlugin.prototype.toString=function(){
   return this.id;
}

GEAPlugin.prototype.saveImage=function(fn){
   var src='file:///'+plug.saveImage(fn);
   this.snapshots.push(src);
   return src;
}

GEAPlugin.prototype.autoPilotSpeed=function(speed){
   this.autopilotspeed=speed;
   this.camera.LookAt.speed=speed;
   plug.setValue("speed",this.autopilotspeed);
   this.trace("speed "+this.autopilotspeed);
}

GEAPlugin.prototype.setLayer=function(name,visible){
   plug.setLayer(name,visible);
}

GEAPlugin.prototype.terrain=function(visible){
   plug.setLayer("terrain",visible);
   plug.setLayer("Terrain",visible);
   plug.setLayer("Gelände",visible);
   plug.setLayer("Terreno 3D",visible);
   plug.setLayer("Terreno",visible);
   plug.setLayer("Relief",visible);
}

GEAPlugin.prototype.borders=function(visible){
   plug.setLayer("borders",visible);
   plug.setLayer("Grenzen",visible);
   plug.setLayer("Fronteras",visible);
   plug.setLayer("Confini",visible);
   plug.setLayer("Frontières",visible);
}

GEAPlugin.prototype.roads=function(visible){
   plug.setLayer("roads",visible);
   plug.setLayer("Roads",visible);
   plug.setLayer("Straßen",visible);
   plug.setLayer("Carreteras",visible);
   plug.setLayer("Strade",visible);
   plug.setLayer("Routes",visible);
}

GEAPlugin.prototype.streaming=function(){
   var spp=plug.streamingProgressPercentage();
   return (spp<100);
}

GEAPlugin.prototype.trace=function(info){
   if (this.debugMode){
      var re = /</gi;
      info=info.replace(re,"&lt;");
      re = />/gi;
      info=info.replace(re,"&gt;");
      this.log+='info:\t'+info+'\n';
   }
}

GEAPlugin.prototype.traceWarning=function(info){
   if (this.debugMode){
      info=info.replace("<","&lt;");
      info=info.replace(">","&gt;");
      this.log+='warning:\t'+info+'\n';
   }
}

GEAPlugin.prototype.traceException=function(info){
   if (this.debugMode){
      info=info.replace("<","&lt;");
      info=info.replace(">","&gt;");
      this.log+='exception:\t'+info+'\n';
   }
}

GEAPlugin.prototype.gotoOrigin=function(){
   this.camera.set(10,10,0,100000,60,0);
   this.camera.setParams();
}

GEAPlugin.prototype.fly=function(){
   this.viewAngle=0;
   this.direction=this.camera.LookAt.azimuth;  //fly direction
   this.camera.LookAt.speed=this.autopilotspeed;
   this.stopped=false;
   //setTimeout('clockTimer()',1000/geap.frames);
   //this.trace("fly:\n"+this.camera.LookAt);
}

GEAPlugin.prototype.open=function(href){
   if (this.loadQueue.length==0){
       //setTimeout('openTimer()',1000);
   }
   this.loadQueue.push(href);
}

GEAPlugin.prototype.play=function(){
   this.stopped=false;
   //setTimeout('playTimer()',1000/frames);
}

GEAPlugin.prototype.pause=function(){
   this.camera.azimuthControl.increment=0;
   this.camera.tiltControl.increment=0;
   this.camera.rangeControl.increment=0;
   this.camera.altitudeControl.increment=0;
   this.camera.rollControl.increment=0;
   this.stopped=true;
}

GEAPlugin.prototype.stop=function(){
   this.stopped=true;
   this.clock=0;
}

GEAPlugin.prototype.loadKml=function(kmlObj){
   var kml=xmlheader+kmlheader+kmlObj.toString()+'</kml>';
   plug.loadKml(kmlObj);
}
GEAPlugin.prototype.openKml=function(kmlObj){
   var kml=xmlheader+kmlheader+kmlObj.toString()+'</kml>';
   var filename=plug.writeKml(kmlObj.name,kml);
   //geap.trace(filename);
   //plug.openFile(filename);
   this.open(filename);
}
GEAPlugin.prototype.openNl=function(kmlObj){
   var kml=xmlheader+kmlheader+kmlObj.toString()+'</kml>';
   //plug.loadKml(kmlObj);
   var filename=plug.writeKml(kmlObj.name,kml);
   //geap.trace(filename);
   plug.openFile(filename);
}

function openTimer(){
   return;

   if (geap.loadQueue.length>0){
      var elem=geap.loadQueue.shift();
      //plug.setLayer('Temporary Places',1);
      plug.openFile(elem);
      geap.trace("openFile:"+elem);
      //alert(elem);
      if (geap.loadQueue.length>0){
         //setTimeout('openTimer()',3000);
      }
   }
}

function clockTimer(){
   geap.changed=false;
   geap.clock++;
   if (!geap.stopped){
       if (geap.camera.speedControl.process()){
          geap.elapsedTime=parseFloat(geap.elapsedTime)+parseFloat(1/geap.frames);
          geap.changed=true;
       }
       if (geap.camera.rollControl.process()){
          geap.changed=true;
          //if (geap.camera.rollControl.increment!=0)
          //   return;
       }
   }
   if (geap.camera.azimuthControl.process()){
      geap.changed=true;
   }
   if (geap.camera.rangeControl.process()){
      geap.changed=true;
   }
   if (geap.camera.altitudeControl.process()){
      geap.changed=true;
      //geap.trace('clock='+geap.clock+' '+geap.camera.LookAt);
      //return;
   }
   if (geap.camera.tiltControl.process()){
      geap.changed=true;
   }
   var deltaTime=1000/geap.frames;
   if (!geap.changed && geap.stopped) {
      geap.camera.getView();
      geap.direction=geap.camera.LookAt.azimuth;
      deltaTime=1000;
      //geap.trace('stop ='+geap.clock+' '+geap.camera.LookAt);
   }
   if (!geap.stopped){
      if (geap.onClock!=undefined){
         geap.onClock(geap.clock);
      }
      setTimeout('clockTimer()',deltaTime);
   }
}
//-----------------------------------------
function CameraControl(camera) {
    this.target    =0;
    this.increment =0;
}
CameraControl.prototype.name;
CameraControl.prototype.camera;
//CameraControl.prototype.value;
CameraControl.prototype.target;
CameraControl.prototype.increment;
CameraControl.prototype.max;
CameraControl.prototype.min;
CameraControl.prototype.process=function(){
    return false;
}
//-----------------------------------------
TiltControl.prototype = new CameraControl();
function TiltControl(camera) {
    CameraControl.call(this);
    this.name      ='TiltControl';
    this.camera     =camera;
    this.max       =90;
    this.min       =0;
    this.targetValue=undefined;
}
TiltControl.prototype.process=function(){
   var changed=false;
   if (this.increment!=0){
      var tilt=parseFloat(this.camera.LookAt.tilt)+parseFloat(this.increment/geap.frames);
      if (tilt<this.min || tilt >this.max){
         this.camera.tiltControl.increment=-parseFloat(this.increment);
         tilt=parseFloat(this.camera.LookAt.tilt)+parseFloat(this.increment/geap.frames);
      }
      this.camera.LookAt.tilt=tilt;
      changed=true;
   }
   if (this.targetValue!=undefined){
      this.camera.LookAt.tilt=TILT(parseFloat(this.targetValue));
      this.targetValue=undefined;
      changed=true;
   }
   return changed;
}
//-----------------------------------------
RangeControl.prototype = new CameraControl();
function RangeControl(camera) {
    CameraControl.call(this);
    this.name      ='RangeControl';
    this.camera     =camera;
    this.max       =Math.pow(10,8);
    this.min       =30;
    this.targetValue=undefined;
    //alert(this.camera);
}
RangeControl.prototype.process=function(){
   var changed=false;
   if (this.increment!=0){
       //var inc =parseFloat(this.increment/geap.frames)
       var sign=Math.abs(this.increment)/this.increment;
       var ii=parseFloat((this.camera.LookAt.range/2)/geap.frames);
       if (ii<10) ii=10;
       var inc =sign*ii;
       var range=parseFloat(this.camera.LookAt.range)+inc;
       if (range<this.min || range >this.max){
          this.increment=-this.increment;
          inc=-inc;
       }
       this.camera.LookAt.range=parseFloat(this.camera.LookAt.range)+inc;
       changed=true;
   }
   if (this.targetValue!=undefined){
      this.camera.LookAt.range=this.targetValue;
      //alert(this.camera.LookAt.range);
      this.targetValue=undefined;
      changed=true;
   }
   return changed;
}
//-----------------------------------------
SpeedControl.prototype = new CameraControl();
function SpeedControl(camera) {
    CameraControl.call(this);
    this.name      ='SpeedControl';
    this.camera    =camera;
    this.max       =10000.0;
    this.min       =0.0;
    this.value     =0;
    this.increment =0.0;
}
SpeedControl.prototype.process=function(){
    if (this.increment!=0){
        this.value=parseFloat(this.value)+parseFloat(this.increment/geap.frames);
        if (this.value>this.max){
            this.value=this.max;
        }
        if (this.value<this.min){
            this.value=this.min;
        }
    }
    return this.value>=0;
}
//-----------------------------------------
AzimuthControl.prototype = new CameraControl();
function AzimuthControl(camera) {
    CameraControl.call(this);
    this.name      ='AzimuthControl';
    this.camera     =camera;
    this.max       =180;
    this.min       =-180;
    this.targetValue=undefined;
}
AzimuthControl.prototype.process=function(){
   var changed=false;
   if (this.increment!=0){
      this.camera.LookAt.azimuth=AZIMUTH(parseFloat(this.camera.LookAt.azimuth)+parseFloat(this.increment/geap.frames));
      changed=true;
   }
   if (this.targetValue!=undefined){
      this.camera.LookAt.azimuth=AZIMUTH(parseFloat(this.targetValue));
      this.targetValue=undefined;
      changed=true;
   }
   return changed;
}
//-----------------------------------------
AltitudeControl.prototype = new CameraControl();
function AltitudeControl(camera) {
    CameraControl.call(this);
    this.name      ='AltitudeControl';
    this.camera     =camera;
    this.max       =Math.pow(10,8);
    this.min       =0;
}
AltitudeControl.prototype.process=function(){
   var changed=false;
   if (this.increment!=0){
      var alt=parseFloat(this.camera.eye.altitude)+parseFloat(this.increment/geap.frames);
      if (alt>this.max){
          //alt=this.max;
      }
      if (alt<this.min){
          alt=this.min;
          this.increment=0;
      }
      changed=true;
      var ratio=alt/this.camera.eye.altitude;
      var newpoint=this.camera.eye.move(this.camera.eye.azimuth,this.camera.focusDistance*ratio);
      this.camera.LookAt.latitude=newpoint.latitude;
      this.camera.LookAt.longitude=newpoint.longitude;
      this.camera.LookAt.range=Math.sqrt(Math.pow(alt,2)+Math.pow(this.camera.focusDistance*ratio,2));
   }
   return changed;
}
//-----------------------------------------
RollControl.prototype = new CameraControl();
function RollControl(camera) {
    CameraControl.call(this);
    this.name      ='RollControl';
    this.camera     =camera;
    this.max       =5;
    this.min       =-5;
    this.value     =0;
    this.distance     =0;
    this.direction =0;
    this.LookAt=new LookAt();
    this.path=new Arc();
}
RollControl.prototype.process=function(){
    var changed=false;
    this.distance=this.camera.focusDistance;
    var vel=this.camera.speedControl.value;
    var km=vel/3600;
    var meters=km*1000;
    geap.metersCounter+=meters;
    this.direction =0;
    if (this.increment!=0){
        this.value+=this.increment;
        this.direction =this.increment<0?-1:1;
        if (this.value<this.min)
            this.value=this.min;
        if (this.value>this.max)
            this.value=this.max;
    }
    if (this.increment==0){
        if (this.value<0){
            this.value+=1;
            this.direction =1;
            if (this.value>-1)
                this.value=0;
        }
        if (this.value>0){
            this.value-=1;
            this.direction =-1;
            if (this.value<1)
                this.value=0;
        }
        //this.value=0;
    }
    if (this.value!=0){
        var radius=Math.pow(meters,2)/(9.81*Math.tan(toRad(Math.abs(this.value))));
        this.LookAt.azimuth=AZIMUTH(parseFloat(this.camera.LookAt.azimuth)+parseFloat(this.direction*90));
        var next=this.camera.eye.move(this.LookAt.azimuth,radius);
        this.LookAt.tilt =this.camera.LookAt.tilt;
        this.LookAt.range =this.camera.LookAt.range;
        this.LookAt.latitude =next.latitude;
        this.LookAt.longitude =next.longitude;
        this.LookAt.altitude =0;
        this.path.set(this.LookAt.asPoint(),radius,AZIMUTH(parseFloat(180)+parseFloat(this.LookAt.azimuth)),this.direction*meters,2);
        var p=this.path.points[1];
        geap.direction=AZIMUTH(parseFloat(p.azimuth)+parseFloat(this.direction*90));
        this.camera.LookAt.azimuth=geap.direction;
        var next=p.move(geap.direction,this.distance);
        this.camera.LookAt.latitude =next.latitude;
        this.camera.LookAt.longitude=next.longitude;
    }

    if (this.increment==0 && this.value==0 && meters>0){
        var next=this.camera.LookAt.asPoint().move(geap.direction,meters);
        this.camera.LookAt.latitude  =next.latitude;
        this.camera.LookAt.longitude =next.longitude;
    }
    changed=true;
    return changed;
}
//-----------------------------------------
function PointOnTerrain(x,y) {
    this.latitide=0;
    this.longitude=0;
    this.altitude=0;
    this.projectedOntoGlobe=false;
    this.zeroElevationExaggeration=false;

    try{
        var cv=plug.getPointOnTerrain(x,y);
        for(var i=0;i<5;i++)
           cv=cv.replace(",",".");
        var values=cv.split(" ");
        this.latitude    =values[0];
        this.longitude   =values[1];
        this.altitude    =values[2];
        this.projectedOntoGlobe=values[3];
        this.zeroElevationExaggeration=values[4];
    }catch(e){
        geap.traceException("PointOnTerrain");
    }
}
//--------------------------------------------------------------------
//CAMERA
//--------------------------------------------------------------------
function Camera() {
    this.LookAt=new LookAt();
    this.LookAt.altitudeMode='relativeToGround';
    this.id="Camera";
    this.eye=new Point();
    this.eye.extrude=1;
    this.eye.altitudeMode='relativeToGround';
    this.focusDistance=0;
    this.snapshot=false;
    this.path =new LineString();
    this.considerTerrain =0;

    this.camerapath =new LineString();
    this.camerapath.altitudeMode='absolute';
    this.camerapath.extrude=0;

    this.tiltControl    =new TiltControl(this);
    this.rangeControl   =new RangeControl(this);
    this.azimuthControl =new AzimuthControl(this);
    this.rollControl    =new RollControl(this);
    this.speedControl   =new SpeedControl(this);
    this.altitudeControl=new AltitudeControl(this);
    this.getView();
}
Camera.prototype.id;
Camera.prototype.name;
Camera.prototype.description;
Camera.prototype.LookAt;
//-----------------------------------------
Camera.prototype.setConsiderTerrain=function(value){
    this.considerTerrain =value;
    plug.setValue('considerterrain',value);
}
//-----------------------------------------
Camera.prototype.getView=function(){
    try{
        var cv=plug.getViewParams();
        if (cv=='') {
            geap.trace("cv=''");
            //return;
        }
        for(var i=0;i<8;i++)
           cv=cv.replace(",",".");

        var values=cv.split(" ");
        this.LookAt.latitude    =values[0];
        this.LookAt.longitude   =values[1];
        this.LookAt.altitude    =values[2];
        var altM =values[3];
        if (altM==0)
           this.LookAt.altitudeMode='clampToGround';
        if (altM==1)
           this.LookAt.altitudeMode='relativeToGround';
        if (altM==2)
           this.LookAt.altitudeMode='absolute';
        this.LookAt.range       =values[4];
        this.LookAt.tilt        =values[5];
        this.LookAt.azimuth     =values[6];
        //this.LookAt.speed       =values[7];
        this.update();
    }catch(e){
        geap.traceException("getView");
    }

}

Camera.prototype.set=function(lat,lon,alt,range,tilt,azimuth){
    this.LookAt.latitude    =lat;
    this.LookAt.longitude   =lon;
    this.LookAt.altitude    =alt;
    this.LookAt.range       =range;
    this.LookAt.tilt        =tilt;
    this.LookAt.azimuth     =azimuth;
}

Camera.prototype.setParams=function(){
    kml=this.LookAt.latitude+' '+this.LookAt.longitude+' '+this.LookAt.altitude+' '+this.LookAt.altitudeModeValue()+' ';
    kml+=this.LookAt.range+' '+this.LookAt.tilt+' '+this.LookAt.azimuth+' '+this.LookAt.speed;
    plug.setViewParams(kml);
    geap.trace("SetCameraParams:"+kml);
    //kml=this.LookAt.latitude+' '+this.LookAt.longitude+' '+'4000'+' '+'2'+' ';
    //kml+=500000+' '+'90'+' '+this.LookAt.azimuth+' '+this.LookAt.speed;
    this.update();
}

Camera.prototype.update=function(){
    var heading=AZIMUTH(this.LookAt.azimuth-180);
    var angle=90-this.LookAt.tilt;
    var rad  = toRad(angle);
    this.eye.altitude=Math.sin(rad)*this.LookAt.range;
    this.focusDistance=Math.cos(rad)*this.LookAt.range;
    var next=this.LookAt.asPoint().move(heading,this.focusDistance);
    this.eye.latitude =next.latitude;
    this.eye.longitude =next.longitude;
}


Camera.prototype.check=function(){
    var c1=!isNaN(this.LookAt.longitude);
    var c2=!isNaN(this.LookAt.latitude );
    var c3=!isNaN(this.LookAt.azimuth );
    var c4=!isNaN(this.LookAt.range );
    var c5=!isNaN(this.LookAt.tilt );
    var c6=this.LookAt.speed >0;
    return c1 && c2 && c3 && c4 && c5 && c6;
}


Camera.prototype.circle=function(){
   var eye=new Point();
   var heading=AZIMUTH(this.LookAt.azimuth-180);
   var angle=90-this.LookAt.tilt;
   var rad  = angle * Math.PI / 180;
   eye.altitude=20;//Math.sin(rad)*this.LookAt.range;
   var dist=Math.cos(rad)*this.LookAt.range;
   var next=this.LookAt.asPoint().move(heading,dist);
   eye.latitude =next.latitude;
   eye.longitude =next.longitude;

   for(var i=0;i<=60;i++){
      var angle=AZIMUTH(parseFloat(this.LookAt.azimuth)+parseFloat(6*i));
      var dest=eye.move(angle,dist);
      this.path.add(dest);
   }
   this.path.add(eye);
}


//--------------------------------------------------------------------
//--------------------------------------------------------------------
//utility-------------------------------------------------------------
//--------------------------------------------------------------------
//--------------------------------------------------------------------
function sleep(millisecondi) {
    var now = new Date();
    var exitTime = now.getTime() + millisecondi;
    while(true) {
        now = new Date();
        if(now.getTime() > exitTime)
           return;
    }
}

function extractFileName(fullPath) {
  var lastSlash = fullPath.lastIndexOf("/") ;
  if (lastSlash==-1){
     lastSlash = fullPath.lastIndexOf("\\") ;
     //alert(lastSlash);
  }
  return fullPath.substring(lastSlash+1,fullPath.length);
}

function extractName(fullPath) {
  var fn=extractFileName(fullPath);
  var lastDot = fn.lastIndexOf(".")
  fn=fn.substring(0,lastDot);
  //alert(fn);
  return fn;
}

function extractExt(fullPath) {
  var fn=extractFileName(fullPath);
  var lastDot = fn.lastIndexOf(".")
  var ext=fn.substring(lastDot+1,fn.length);
  if (ext!='kml'){
     alert(ext);
     alert(fullPath);
  }
  return fn;
}
function mod(x,y){
   var _mod=y - x * Math.round(y/x);
   if ( _mod < 0)
       _mod = _mod + x;
   return _mod;
}

//------------------------------------------------------------
function horizonDistance(){
   //b=0.8279 is a factor that accounts for atmospheric refraction and depends on the atmospheric temperature lapse rate, which is taken to be standard. R is the radius of the earth. Note that the earth is assumed smooth- likely only true over the oceans!
  b=0.8279 ;
  d = sqrt(2*R*h/b);
  return d;
}


