import World from 'https://code.agentscript.org/src/World.js'
import Model from 'https://code.agentscript.org/src/Model.js'
import { mapzen as provider } from 'https://code.agentscript.org/src/TileData.js'
import { xyz2bbox } from 'https://code.agentscript.org/src/gis.js'

// Cody's Avalanche model
const pi = Math.PI
export default class AvalancheModel extends Model {
    // ======================
    constructor(worldOptions = World.defaultOptions(50)) {
        super(worldOptions)
    }

    // data can be gis zxy or a DataSet
    async startup() {
        const z = 13,
            x = 1555,
            y = 3084
        const bounds = xyz2bbox(x, y, z) // alta utah. USA
        const elev = await provider.zxyToDataSet(z, x, y)
        this.installDataSets(elev)
    }

    installDataSets(elevation) {
        const { slope, aspect, dzdx, dzdy } = elevation.slopeAndAspect()
        this.patches.importDataSet(aspect, 'aspect')
        this.patches.importDataSet(slope, 'slope')
        this.patches.importDataSet(dzdx, 'dzdx')
        this.patches.importDataSet(dzdy, 'dzdy')
        this.patches.importDataSet(elevation, 'elevation')
        // for 3d
        const elevationScaled = elevation.clone().scale(-10, 10)
        this.patches.importDataSet(elevationScaled, 'z', true) // for drawing
    }
    setup() {
        this.patches.ask(p => {
            p.snowDepth = 1
        })
    }
    step() {
        // start new buffer
        this.patches.ask(p => (p.nextSnow = p.snowDepth))
        // drop snow
        this.patches.ask(p => (p.nextSnow += +0.01))
        // make avalanche
        this.patches.ask(p => {
            const maxSnowDepth = pi / p.slope
            if (p.snowDepth > maxSnowDepth) {
                //choose the 2 downhill neighbors to give snow to
                const n = Math.min(2, p.neighbors.length)
                p.neighbors
                    .minNOf(n, 'elevation')
                    .ask(p2 => (p2.nextSnow = p2.nextSnow + p.snowDepth / n))
                p.nextSnow = 0
            }
        })
        // swap buffer
        this.patches.ask(p => (p.snowDepth = p.nextSnow))
    }
}
