// TODO
fn main() {}

// examples/04-rotating_the_earth

// See also 03-user_defined_operators.rs
// Run with:
// cargo run --example 04-rotating_the_earth

// In this example we implement a user defined operator. In contrast
// to the previous example in 03-user_defined_operators.rs, this one
// actually carries out something marginally geodetically useful: It
// formally shifts the geodetic reference frame, such that a certain
// surface point `A` ends where another point `B` used to be. And it
// does so using the **distance and bearing** between A and B, so
// while it may sound somewhat like a Helmert transformation, it really
// isnt: This operator moves all points the same amount, and in the same
// direction, **as measured on the *surface* of the earth**, i.e. taking
// care of variations in local ellipsoidal curvature. The Helmert
// transformation on the other hand, works in cartesian coordinates, and
// generally moves points on the earth's surface away from the surface
// when the system rotates.
//
// The technique implemented here is not in general geodetic use,
// so consider it more of a geodetic pun in the pursuit of a reasonable
// answer to the oft-occurring question "If we shift Copenhagen to Vienna,
// where would Helsinki end up?".
//
// Also note that since the return-bearing depends on the destination,
// this operator is **not** directly invertible (although an iterative
// solution is feasible)

/*
use geodesy::Ellipsoid;
use geodesy::Error;
use geodesy::{CoordinateTuple, Context};

pub struct GeodesicShift {
    args: Vec<(String, String)>,
    inverted: bool,

    ellps: Ellipsoid,

    bearing: f64,
    distance: f64,
}

impl GeodesicShift {
    fn new(res: &GysResource) -> Result<GeodesicShift, GeodesyError> {
        let mut args = res.to_args(0)?;
        let ellps = Ellipsoid::named(&args.string("ellps", "GRS80"))?;
        let inverted = args.flag("inv");

        // Coordinate of the origin
        let lat_0 = args.numeric("lat_0", std::f64::NAN)?;
        let lon_0 = args.numeric("lon_0", std::f64::NAN)?;

        // Coordinate of the target
        let lat_1 = args.numeric("lat_1", std::f64::NAN)?;
        let lon_1 = args.numeric("lon_1", std::f64::NAN)?;

        if [lat_0, lon_0, lat_1, lon_1].iter().any(|&f| f.is_nan()) {
            return Err(GeodesyError::General(
                "Missing lat_0, lon_0, lat_1 or lon_1",
            ));
        }

        // Now find the distance and bearing between the origin and the target
        let origin = CoordinateTuple::geo(lat_0, lon_0, 0., 0.);
        let target = CoordinateTuple::geo(lat_1, lon_1, 0., 0.);

        let d = ellps.geodesic_inv(&origin, &target);
        let bearing = d[0];
        let distance = d[2];

        Ok(GeodesicShift {
            args: args.used,
            ellps,
            inverted,
            bearing,
            distance,
        })
    }

    // This is the interface to the Rust Geodesy library: Construct a
    // GeodesicShift element, and wrap it properly for consumption.
    pub fn operator(args: &GysResource, _rp: &dyn Context) -> Result<Operator, GeodesyError> {
        let op = GeodesicShift::new(args)?;
        Ok(Operator(Box::new(op)))
    }
}

impl OperatorCore for GeodesicShift {
    fn fwd(&self, _ctx: &dyn Context, operands: &mut [CoordinateTuple]) -> bool {
        for coord in operands {
            let res = self.ellps.geodesic_fwd(&coord, self.bearing, self.distance);
            coord[0] = res[0];
            coord[1] = res[1];
        }
        true
    }

    // This operator is not invertible (because the return azimuth depends
    // on the destination), so we implement `invertible()` as false, let the
    // empty default implementation from the trait take care of `inv()`, and
    // leave it for a rainy day to implement an iterative inverse solution.
    fn invertible(&self) -> bool {
        false
    }

    fn name(&self) -> &'static str {
        "geodesic_shift"
    }

    fn is_inverted(&self) -> bool {
        self.inverted
    }

    fn args(&self, _step: usize) -> &[(String, String)] {
        &self.args
    }
}

fn main() -> anyhow::Result<()> {
    let mut ctx = geodesy::Plain::new(geodesy::SearchLevel::LocalPatches, false);
    ctx.register_operator("geodesic_shift", GeodesicShift::operator)?;
    let op = "geodesic_shift lat_0: 55 lon_0: 12 lat_1: 48 lon_1: 16";

    let cph_to_vie = ctx.define_operation(op)?;

    // Same test coordinates as in example 00, but no conversion to radians.
    let cph = CoordinateTuple::geo(55., 12., 0., 0.); // Copenhagen
    let osl = CoordinateTuple::geo(60., 10., 0., 0.); // Oslo
    let sth = CoordinateTuple::geo(59., 18., 0., 0.); // Stockholm
    let hel = CoordinateTuple::geo(60., 25., 0., 0.); // Helsinki

    let mut data = [osl, cph, sth, hel];

    // Now do the transformation
    ctx.fwd(cph_to_vie, &mut data);
    println!("cph_to_vie (fwd):");
    let mut result = data.clone();
    CoordinateTuple::geo_all(&mut result);
    for coord in result {
        println!("    {:?}", coord);
    }

    // And assert there is no way back...
    assert_eq!(false, ctx.inv(cph_to_vie, &mut data));
    Ok(())
}
*/
