/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

//
// .NAME
// Satellite
//
// .AUTHOR:
// Ubirajara Freitas, Julio d'Alge
//
// .SUMMARY:
//  Provides methods that are required to handle geostationary satellites
//  image georeferencing.
//
// .DESCRIPTION:
//  Specifies methods that are necessary to establish the relation between
//  geodetic and image coordinates in the case of geostationary satellites,
//  typically used for low-resolution meteorological images. The "satellite"
//  projection uses the Hayford ellipsoid. Class Satellite also
//  encapsulates the following important information:
//		Sensor angular resolution or IFOV;
//		Sub-satellite point coordinates;
//		Radius of satellite orbit;
//		Scanning mode;
//		Orientation or yaw angle.
//
// . DESCENDENT:
//  None
//
// .RELATED:
//  Point
//
// .ASCENDENT:
//  PmProjection.
//
// .COMMENTS:
//  None
//

#pragma once

#include "PmProjection.h"

class Satellite : public PmProjection
{
    double SPri{0.};   // IFOV along y axis (rad);
    double SPrj{0.};   // IFOV along x axis (rad);
    double SPis{0.};   // Sub-satellite Y coordinate (m);
    double SPjs{0.};   // Sub-satellite X coordinate (m);
    double SPla0{0.};  // Sub-satellite latitude (rad);
    double SPlo0{0.};  // Sub-satellite longitude (rad);
    double SPrs{0.};   // Radius of satellite orbit (m);
    double SPscn{0.};  // Scanning mode: 0 (WE/NS), 1 (SN/EW)
    double SPyaw{0.};  // Orientation or yaw angle (rad).

    int SProw0{0};   // Image initial row
    int SPcol0{0};   // Map initial column
    int SPnrows{0};  // Input image rows
    int SPncols{0};  // Input image columns
    int SPgrid{0};   // Map Grid orientation

    Cached SPglobal;  // IMAGE_SUBAREA_SELECTION (ON/OFF)


public:
    Satellite(const MvRequest& reqst);
    //		Empty constructor.

    ~Satellite() override = default;
    //		Empty destructor.

    friend bool operator==(Satellite& a, Satellite& b);
    //		Equality operator. Objects a and b are considered equal if
    //		their Satellite projection parameters are the same.
    //		Input:
    //			a:	Object of type Satellite;
    //			b:	Object of type Satellite.
    //		Return:
    //			TRUE:	Objects are equal;
    //			FALSE:	Objects are different.

    virtual double SensorResolutionX() { return SPrj; }
    //		This implementation of a pure virtual method defined in
    //		Projection returns the sensor angular resolution (IFOV)
    //		along X direction.
    //		Return:
    //			SPrj:	Sensor IFOV along X direction (rad).

    virtual void SensorResolutionX(double s) { SPrj = s; }
    //		This implementation of a pure virtual method defined in
    //		Projection updates the sensor angular resolution (IFOV)
    //		along X direction.
    //		Input:
    //			s: 	Sensor IFOV along X direction (rad).
    //		Precondition:
    //			s must be a valid angular resolution.

    virtual double SensorResolutionY() { return SPri; }
    //		This implementation of a pure virtual method defined in
    //		Projection returns the sensor angular resolution (IFOV)
    //		along Y direction.
    //		Return:
    //			SPri:	Sensor IFOV along Y direction (rad)

    virtual void SensorResolutionY(double s) { SPri = s; }
    //		This implementation of a pure virtual method defined in
    //		Projection updates the sensor angular resolution (IFOV)
    //		along Y direction.
    //		Input:
    //			s: 	Sensor IFOV along Y direction (rad).
    //		Precondition:
    //			s must be a valid angular resolution.

    virtual double SubSatelliteX() { return SPjs; }
    //		This implementation of a pure virtual method defined in
    //		Projection returns the sub-satellite X coordinate.
    //		Return:
    //			SPjs:	Sub-satellite X coordinate (m)

    virtual void SubSatelliteX(double s) { SPjs = s; }
    //		This implementation of a pure virtual method defined in
    //		Projection updates the sub-satellite X coordinate.
    //		Input:
    //			s: 	Sub-satellite X coordinate (m)
    //		Precondition:
    //			s must be a valid X coordinate

    virtual double SubSatelliteY() { return SPis; }
    //		This implementation of a pure virtual method defined in
    //		Projection returns the sub-satellite Y coordinate.
    //		Return:
    //			SPis:	Sub-satellite Y coordinate (m)

    virtual void SubSatelliteY(double s) { SPis = s; }
    //		This implementation of a pure virtual method defined in
    //		Projection updates the sub-satellite Y coordinate.
    //		Input:
    //			s: 	Sub-satellite Y coordinate (m)
    //		Precondition:
    //			s must be a valid Y coordinate

    virtual double SubSatelliteLng() { return SPlo0; }
    //		This implementation of a pure virtual method defined in
    //		Projection returns the sub-satellite longitude.
    //		Return:
    //			SPlo0:	Sub-satellite longitude (rad)

    virtual void SubSatelliteLng(double s) { SPlo0 = s; }
    //		This implementation of a pure virtual method defined in
    //		Projection updates the sub-satellite longitude.
    //		Input:
    //			s: 	Sub-satellite longitude (rad)
    //		Precondition:
    //			s must be a valid longitude ([0,pi] or [0,-pi])

    virtual double SubSatelliteLat() { return SPla0; }
    //		This implementation of a pure virtual method defined in
    //		Projection returns the sub-satellite latitude.
    //		Return:
    //			SPla0:	Sub-satellite latitude (rad)

    virtual void SubSatelliteLat(double s) { SPla0 = s; }
    //		This implementation of a pure virtual method defined in
    //		Projection updates the sub-satellite latitude.
    //		Input:
    //			s: 	Sub-satellite latitude (rad)
    //		Precondition:
    //			s must be a valid latitude ([0,pi/2] or [0,-pi/2])

    virtual double Radius() { return SPrs; }
    //		This implementation of a pure virtual method defined in
    //		Projection returns the satellite orbit radius.
    //		Return:
    //			SPrs:	Satellite orbit radius (m)

    virtual void Radius(double r) { SPrs = r; }
    //		This implementation of a pure virtual method defined in
    //		Projection updates the satellite orbit radius.
    //		Input:
    //			r: 	Satellite orbit radius (m)
    //		Precondition:
    //			r must be a valid orbit radius

    virtual double ScanMode() { return SPscn; }
    //		This implementation of a pure virtual method defined in
    //		Projection returns the sensor scanning mode.
    //		Return:
    //			SPscn:	Scanning mode: 0 (WE/NS), 1 (SN/EW)

    virtual double Yaw() { return SPyaw; }
    //		This implementation of a pure virtual method defined in
    //		Projection returns the orientation or yaw angle.
    //		Return:
    //			SPyaw:	Orientation or yaw angle (rad)

    virtual void Yaw(double y) { SPyaw = y; }
    //		This implementation of a pure virtual method defined in
    //		Projection updates the orientation or yaw angle.
    //		Input:
    //			y: 	Orientation or yaw angle (rad)
    //		Precondition:
    //			y must be a valid orientation angle

    virtual double OriginX() { return 0.; }
    //		No code.
    //		Description:
    //			Returns the image initial x coordinate. Not
    //			implemented yet.

    virtual double OriginY() { return 0.; }
    //		No code.
    //		Description:
    //			Returns the image initial y coordinate. Not
    //			implemented yet.

    virtual int Hemisphere() { return 0; }
    //		No code
    //		Description:
    //			Returns the hemisphere. Not implemented yet.

    virtual void Hemisphere(int) {}
    //		No code
    //		Description:
    //			Updates the hemisphere. Not implemented yet.

    virtual double OriginLatitude() { return 0.; }
    //		No code
    //		Description:
    //			Returns the latitude of origin. Not implemented yet.

    virtual void OriginLatitude(double) {}
    //		No code
    //		Description:
    //			Updates the latitude of origin. Not implemented yet.

    virtual double OriginLongitude() { return 0.; }
    //		No code
    //		Description:
    //			Returns the longitude of origin. Not implemented yet.

    virtual void OriginLongitude(double) {}
    //		No code
    //		Description:
    //			Updates the longitude of origin. Not implemented yet.

    virtual double StandardLatitudeOne() { return 0.; }
    //		No code
    //		Description:
    //			Returns the latitude of the first standard parallel.
    //			Not implemented yet.

    virtual void StandardLatitudeOne(double) {}
    //		No code
    //		Description:
    //			Updates the latitude of the first standard parallel.
    //			Not implemented yet

    Point LL2PC(Point& p) override;
    //		This implementation of a pure virtual method defined in
    //		Projection transforms geodetic into Satellite projection
    //		coordinates.
    //		Input:
    //			p:	Geodetic coordinates (rad).
    //		Return:
    //			p:	Satellite projection coordinates (m).
    //		Preconditions:
    //			Geodetic coordinates must be a valid latitude
    //			([0,pi/2] or [0,-pi/2]) and a valid longitude
    //			([0,pi] or [0,-pi]).

    Point PC2LL(Point& p) override;
    //		This implementation of a pure virtual method defined in
    //		Projection transforms Satellite projection into geodetic
    //		coordinates.
    //		Input:
    //			p:	Satellite projection coordinates (m).
    //		Return:
    //			p:	Geodetic coordinates (rad).
    //		Preconditions:
    //			X and Y Satellite projection coordinates must be
    //			both valid, within their typical range.

    void CalculateProjectionCoord() override;
    //              Calculate the bounding box in projection coordinates
    //              (internal function )
    //              Input:
    //                    geodCoord_ : bounding box in lat/long
    //              Output:
    //                    projCoord_ : bounding box in projection coordinates

    bool CheckGeodeticCoordinates(Location&) override;

    double CheckOriginLongitude() override
    {
        return OriginLongitude();
    }
};
