/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.sis.referencing.operation.provider;

import jakarta.xml.bind.annotation.XmlTransient;
import org.opengis.util.FactoryException;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterDescriptorGroup;
import org.opengis.referencing.cs.EllipsoidalCS;
import org.opengis.referencing.operation.Conversion;
import org.opengis.referencing.operation.MathTransform;
import org.apache.sis.referencing.operation.transform.PoleRotation;
import org.apache.sis.metadata.iso.citation.Citations;
import org.apache.sis.parameter.ParameterBuilder;
import org.apache.sis.parameter.Parameters;
import org.apache.sis.measure.Longitude;
import org.apache.sis.measure.Latitude;
import org.apache.sis.measure.Units;
import org.apache.sis.util.internal.shared.Constants;


/**
 * The provider for the WMO <cite>Rotated Latitude/Longitude</cite> coordinate operation.
 * This is defined by the World Meteorological Organization (WMO) in GRIB2 template 3.1.
 * The 180° rotated meridian runs through both the geographical and the rotated South pole.
 *
 * <h2>Comparison with UCAR library</h2>
 * This is consistent with {@link ucar.unidata.geoloc.projection.RotatedLatLon}
 * in UCAR netCDF library version 5.5.2.
 *
 * @author  Martin Desruisseaux (Geomatys)
 *
 * @see NorthPoleRotation
 */
@XmlTransient
public final class SouthPoleRotation extends AbstractProvider {
    /**
     * For cross-version compatibility.
     */
    private static final long serialVersionUID = -5970630604222205521L;

    /**
     * The operation parameter descriptor for the <cite>grid south pole latitude</cite> parameter value.
     * This is the geographic latitude (usually in degrees) of the southern pole of the coordinate system.
     * The symbol used in GRIB2 template 3.1 is θ<sub>p</sub>.
     *
     * <!-- Generated by ParameterNameTableGenerator -->
     * <table class="sis">
     *   <caption>Parameter names</caption>
     *   <tr><td> SIS:     </td><td> Latitude of rotated pole </td></tr>
     *   <tr><td> NetCDF:  </td><td> grid_south_pole_latitude </td></tr>
     * </table>
     * <b>Notes:</b>
     * <ul>
     *   <li>No default value</li>
     * </ul>
     */
    static final ParameterDescriptor<Double> POLE_LATITUDE;

    /**
     * The operation parameter descriptor for the <cite>grid south pole longitude</cite> parameter value.
     * This is the geographic longitude (usually in degrees) of the southern pole of the coordinate system.
     * The symbol used in GRIB2 template 3.1 is λ<sub>p</sub>.
     *
     * <!-- Generated by ParameterNameTableGenerator -->
     * <table class="sis">
     *   <caption>Parameter names</caption>
     *   <tr><td> SIS:     </td><td> Longitude of rotated pole </td></tr>
     *   <tr><td> NetCDF:  </td><td> grid_south_pole_longitude </td></tr>
     * </table>
     * <b>Notes:</b>
     * <ul>
     *   <li>No default value</li>
     * </ul>
     */
    static final ParameterDescriptor<Double> POLE_LONGITUDE;

    /**
     * The operation parameter descriptor for the <cite>grid_south_pole_angle</cite> parameter value (optional).
     * This is the angle of rotation about the new polar axis (measured clockwise when looking from the southern
     * to the northern pole) of the coordinate system, assuming the new axis to have been obtained by first
     * rotating the sphere through λ<sub>p</sub> about the geographic polar axis, and then rotating through
     * (90° + θ<sub>p</sub>) degrees so that the southern pole moved along the (previously rotated) Greenwich meridian.
     *
     * <!-- Generated by ParameterNameTableGenerator -->
     * <table class="sis">
     *   <caption>Parameter names</caption>
     *   <tr><td> SIS:     </td><td> Axis rotation </td></tr>
     *   <tr><td> NetCDF:  </td><td> grid_south_pole_angle </td></tr>
     * </table>
     * <b>Notes:</b>
     * <ul>
     *   <li>Value domain: [-180.0 … 180.0]°</li>
     *   <li>Optional</li>
     * </ul>
     */
    static final ParameterDescriptor<Double> AXIS_ANGLE;

    /**
     * The group of all parameters expected by this coordinate operation.
     */
    public static final ParameterDescriptorGroup PARAMETERS;
    static {
        final ParameterBuilder builder = new ParameterBuilder().setCodeSpace(Citations.NETCDF, Constants.NETCDF).setRequired(true);

        POLE_LATITUDE = builder
                .addName(Citations.SIS, "Latitude of rotated pole")
                .addName("grid_south_pole_latitude")
                .createBounded(Latitude.MIN_VALUE, Latitude.MAX_VALUE, Double.NaN, Units.DEGREE);

        POLE_LONGITUDE = builder
                .addName(Citations.SIS, "Longitude of rotated pole")
                .addName("grid_south_pole_longitude")
                .createBounded(Longitude.MIN_VALUE, Longitude.MAX_VALUE, Double.NaN, Units.DEGREE);

        AXIS_ANGLE = builder.setRequired(false)
                .addName(Citations.SIS, "Axis rotation")
                .addName("grid_south_pole_angle")
                .createBounded(Longitude.MIN_VALUE, Longitude.MAX_VALUE, 0, Units.DEGREE);

        PARAMETERS = builder.setRequired(true)
                .addName(Citations.SIS, "South pole rotation")
                .addName(Citations.WMO, "Rotated Latitude/longitude")
                .addName("rotated_latlon_grib")
                .createGroup(POLE_LATITUDE,    // Note: `PoleRotation` implementation depends on this parameter order.
                             POLE_LONGITUDE,
                             AXIS_ANGLE);
    }

    /**
     * Constructs a new provider.
     */
    public SouthPoleRotation() {
        super(Conversion.class, PARAMETERS,
              EllipsoidalCS.class, false,
              EllipsoidalCS.class, false,
              (byte) 2);
    }

    /**
     * Creates a coordinate operation from the specified group of parameter values.
     *
     * @param  context  the parameter values together with its context.
     * @return the coordinate operation created from the given parameter values.
     * @throws FactoryException if the coordinate operation cannot be created.
     */
    @Override
    public MathTransform createMathTransform(final Context context) throws FactoryException {
        final Parameters p = Parameters.castOrWrap(context.getCompletedParameters());
        return PoleRotation.rotateSouthPole(context.getFactory(),
                p.getValue(POLE_LATITUDE),
                p.getValue(POLE_LONGITUDE),
                p.getValue(AXIS_ANGLE));
    }
}
