"use strict";
this.name = "PlanetFall2_Turbulence";
this.author = "phkb";
this.copyright = "CC-BY-SA-NC 4.0";
this.description = "Controls atmospheric turbulence";

/*
    progressively applies force to player's ship, impacting on pitch/roll/yaw.
    impact increases as altitude decreases and/or speed increases
*/

this._swap = 0;
this._maxImpact = 0.0025;
this._scaleImpact = 0.5;

//-------------------------------------------------------------------------------------------------------------
this.systemWillPopulate = function() {
    // this will add turbulence settings to all planets that have an atmosphere
	system.setPopulator("pf_turbulence", {
		callback: function (pos) {
            var pl = system.planets;
            var i = pl.length;
            while (i--) {
                if (pl[i].isSun) continue;
                if (pl[i].solarGasGiant || pl[i].isGasGiant || !pl[i].hasAtmosphere) {
                    pl[i]._turbulence = [0, 0, 0];
                    continue;
                }
                var c = Math.random();
                var max = 0.02;
                var half = max / 2;
                if (c < 45) { // 2 types
                    // always pitch and roll
                    pl[i]._turbulence = [Math.random() * max - half, Math.random() * max - half, 0]; 
                } else { // only 1 type
                    var s = Math.random()
                    if (s < 50) pl[i]._turbulence = [Math.random() * max - half, 0, 0]; 
                    else pl[i]._turbulence = [0, Math.random() * max - half, 0];
                }
            }
		}.bind(this),
		location: "COORDINATES",
		coordinates: Vector3D(0, 0, 0),
		priority: 500
	});    
}

//-------------------------------------------------------------------------------------------------------------
this.$nearestPlanet = function() {
    var p = player.ship;
    var pp = p.position;
    var c = null;
    var d = 100000000000000000000.0; //10^20m for sure
    var pl = system.planets;
    var i = pl.length;
    while (i--) { //find the smallest distance
        var s = pp.distanceTo(pl[i].position) - pl[i].radius;
        if (d > s) {
            d = s;
            c = pl[i];
        }
    }
    if (c) {
        return c;
    } else {
        return null;
    }
}

//-------------------------------------------------------------------------------------------------------------
this.shipLaunchedFromStation = function() {
    var pl = this.$nearestPlanet();
    if (pl && this.$altitude(player.ship, pl) < (pl.radius * 3)) this.$startFCB(pl);
}

//-------------------------------------------------------------------------------------------------------------
this.shipWillEnterWitchspace = function() {
    this.$stopFCB();
    // check to see if we're jumping inside the atmosphere
    // if so, there's a 50% chance of a misjump
    var pl = this.$nearestPlanet();
    if (pl && this.$altitude(player.ship, pl) < (worldScripts.PlanetFall2._config.atmosphereMax * 1000) & isValidFrameCallback(this._fcb)) {
        worldScripts.PlanetFall2.$serviceLevelDecrease();
        if (Math.random() > 0.5) player.ship.scriptedMisjump = true;
    }
}

//-------------------------------------------------------------------------------------------------------------
this.shipEnteredPlanetaryVicinity = function(entity) {
    this.$startFCB(entity);
}

//-------------------------------------------------------------------------------------------------------------
this.shipExitedPlanetaryVicinity = function() {
    this.$stopFCB();
}

//-------------------------------------------------------------------------------------------------------------
this.shipDied = function() {
    this.$stopFCB();
}

//-------------------------------------------------------------------------------------------------------------
this.shipWillDockWithStation = function() {
    this.$stopFCB();
}

//-------------------------------------------------------------------------------------------------------------
this.$startFCB = function $startFCB(entity) {
    if (worldScripts.PlanetFall2._config.useTurbulence == false) return;
    if (entity.isSun || !entity.hasAtmosphere || entity.isGasGiant || entity.solarGasGiant) return;
    this._swap = 0;
    this._scaleImpact = worldScripts.PlanetFall2._config.turbulenceImpactScale;
    if (player.ship.hasEquipmentProviding("EQ_PLANETFALL")) {
        this._setMaxImpact = 0.00125;
    } else {
        this._setMaxImpact = this._maxImpact;
    }
    if (entity.hasOwnProperty("_pf2_turbulence")) {
        if (entity._pf2_turbulence.hasOwnProperty("scaleMax") && !isNaN(entity._pf2_turbulence.scaleMax)) this._setMaxImpact *= entity._pf2_turbulence.scaleMax;
    }
    if (isValidFrameCallback(this._fcb)) removeFrameCallback(this._fcb);
    this._fcb = addFrameCallback(this.$turbulence_FCB.bind(this, entity));
}

//-------------------------------------------------------------------------------------------------------------
this.$stopFCB = function $stopFCB() {
    if (isValidFrameCallback(this._fcb)) removeFrameCallback(this._fcb);
}

//-------------------------------------------------------------------------------------------------------------
this.$turbulence_FCB = function _turbulence_FCB(entity, delta) {
    function sign(val) {
        if (val > 0) return 1
        else if (val < 0) return -1;
        else return 0;
    }
    function min(x, y) {
        return (x < y ? x : y);
    }

    var that = _turbulence_FCB;
    var _rnd = (that._rnd = that._rnd || Math.random);
    var _min = (that._min = that._min || min);
    var _sign = (that._sign = that._sign || sign);
    var _alt = (that._alt = that._alt || this.$altitude);
    var _maxalt = (that._maxalt = that._maxalt || worldScripts.PlanetFall2._config.atmosphereMax * 1000);
    var _maximp = (that._maximp = that._maximp || this._setMaxImpact);
    var _scaleimp = (that._scaleimp = that._scaleimp || this._scaleImpact);
    var _p = (that._p = that._p || player.ship);

    if (!_p || !_p.isValid) return;

    // every 8 secs or so, possibly swap directions on one of the turbulence properties
    this._swap += delta;
    if (this._swap > 8) {
        this._swap = 0;
        if (_rnd() < 0.2) {
            // swap directions on one of the turbulence properties
            if (_rnd() > 0.5 && entity._turbulence[0] != 0) entity._turbulence[0] *= -1;
            else if (_rnd() > 0.5 && entity._turbulence[1] != 0) entity._turbulence[1] *= -1;
            //else if (_rnd() > 0.5 && entity._turbulence[2] != 0) entity._turbulence[2] *= -1;
        }
    }

    var alt = _alt(_p, entity);
    if (alt < _maxalt && _rnd() < ((_maxalt - alt) / _maxalt)) {
        // the impact of the turbulence is increased with lower altitude and higher speeds
        var impact = ((_maxalt - alt) + (_p.maxSpeed - _p.speed)) / (_maxalt + _p.maxSpeed);
        // pitch
        if (entity._turbulence[0] != 0) _p.orientation = _p.orientation.rotate(_p.orientation.vectorRight(), _min(_maximp * _sign(entity._turbulence[0]), entity._turbulence[0] * impact) * _scaleimp);
        // roll
        if (entity._turbulence[1] != 0) _p.orientation = _p.orientation.rotate(_p.orientation.vectorForward(), _min(_maximp * _sign(entity._turbulence[1]), entity._turbulence[1] * impact) * _scaleimp);
        // yaw
        //if (entity._turbulence[2] != 0) _p.orientation = _p.orientation.rotate(_p.orientation.vectorUp(), _min(_maximp * _sign(entity._turbulence[2]), entity._turbulence[2] * impact));
    }
}

//-------------------------------------------------------------------------------------------------------------
this.$altitude = function(p, planet) {
    var alt = 999999
    if (planet) {
        alt = p.position.distanceTo(planet) - planet.radius - p.collisionRadius;
    }
    return alt;
}
