/*
 * (C) Copyright 1996-2016 ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

/*! \file Akima761Method.h
    \brief Definition of the Template class Akima761Method.

    Magics Team - ECMWF 2004

    Started: Thu 11-Mar-2004

    Changes:

*/

#ifndef Akima761Method_H
#define Akima761Method_H

#include "magics.h"

#include "Akima761MethodAttributes.h"
#include "ContourMethod.h"
#include "PointsHandler.h"

// N1, N2, and N3 are the numbers of the data points used
// to determine the first-, second-, and third-degree
// polynomials
#define N1_ 3
#define N2_ 6
#define N3_ 10

namespace magics {

template <class P>
class Akima761 : public MatrixHandler<P> {
public:
    Akima761(const AbstractPoints<P>&, const Akima761Attributes&);
    ~Akima761() override;

    // Matrix operators
    double operator()(int i, int j) const;
    int rows() const  // output number of rows
    {
        return nrows_;
    }
    int columns() const  // output number of columns
    {
        return ncols_;
    }

    double row(int i) const;     // latitude value
    double column(int j) const;  // longitude value

    double missing() const  // missing value
    {
        return this->matrix_.missing();
    }

    // Initialization
    int Init(int, double*, double*, double*, int NROW = 6);

    // Scaterred-data bivariate interpolation
    int SDBI3P(double, double, double&) const;


private:
    Akima761Attributes attr_;
    Matrix matrix_;       // output matrix
    int nrows_;           // number of rows
    int ncols_;           // number of columns
    double minX_, maxX_,  // bounding box
        minY_, maxY_;

    double SWTOL_;  // tolerance

    int MD_;      // mode of computation, 1 for new XD-YD (default)
    int NDP_;     // number of data points
    int NROW_;    // number of entries per triangle (6 or 9)
    int NT_;      // number of triangles (maximum is 2*NDP-5)
    int NL_;      // number of border line segments (maximum is NDP)
    double* XD_;  // X coordinates of the data points
    double* YD_;  // Y coordinates of the data points
    double* ZD_;  // Z coordinates of the data points

    // Auxiliary variables
    int* LIST_;   // set of nodal indexes used in the triangulation
    int* LPTR_;   // set of pointers (LIST indexes)
    int* LEND_;   // set of pointers to adjacency lists
    int* LIST1_;  // copy of LIST_
    int* LPTR1_;  // copy of LPTR_
    int* LEND1_;  // copy of LEND_

    int* IPT_[3];  // point numbers of the vertexes
    int* IPL_[2];  // point numbers of the end points of the
                   // (IL)th border line segment

    int** LTRI_;  // internal work area
    int* ITL_;    // internal work area

    int* IORD_;    // degree of the polynomial used to compute PDD (NDP)
    int* IDSQ_;    // internal work area (NDP)
    int* IPC_[9];  // internal work area (9*NDP)
    int* NCP_;     // internal work area (NDP)

    double* PDD_[5];   // store estimated zx,zy,zxx,zxy,zyy values (5*NDP)
    double* CF3_[9];   // internal work area (9*NDP)
    double* CFL1_[2];  // internal work area (2*NDP)
    double* DSQ_;      // internal work area (NDP)

    // Member functions

    // Initialize working arrays
    int InitWorkingArrays();

    //---------------------------------
    // Terralib functions
    //---------------------------------

    // Triangulates the data area in the x-y plane with
    // a scattered data point set
    int SDTRAN(int&, int&);

    // Basic triangulation in the convex hull of a scattered
    // data point set in a plane
    int SDTRCH(int&, int&);

    // Removal of thin triangles along the border line of triangulation
    void SDTRTT(int&, int&);

    // Partial derivatives for bivariate interpolation and surface
    // fitting for scattered data
    void SDPD3P();

    // Selects, at each of the data points, nine data points closest to it
    void SDCLDP();

    // Computes coefficients of the third-degree polynomial for z(x,y)
    void SDCF3P();

    // Solution of a set of linear equations
    void SDLEQN(int, double[N3_][N3_], double*, double*, double&, double&);

    // Least squares fit of a linear surface (plane) to z(x,y) values
    void SDLS1P();

    // Estimates first and second partial derivatives at node K
    int GRADC(int, int, int*, int, double&, double&, double&, double&, double&);

    // Sets up the I-th row of an augmented regression matrix for a
    // weighted least squares fit of a cubic function f(x,y) to a set
    // of data values z, where f(XK,YK) = ZK
    void SETRO3(double, double, double, double, double, double, double, double, double, double, double*);

    // Constructs the Givens plane rotation
    void GIVENS(double&, double&, double&, double&);

    // Applies the Givens rotation to the 2 by N matrix
    void ROTATE(int, double, double, double*, double*);

    // Locating points in a scattered data point set
    void SDLCTN(int, double*, double*, int*, int*) const;

    // Compute the z value in a scattered data point set
    void SDPLNL(int, double*, double*, int*, int*, double*) const;

    //------------------------------------
    // Tripack functions
    //------------------------------------

    // Creates a Delaunay triangulation
    int TRMESH(int&);

    // Determines whether node N0 is to the left or to the right
    // of the line through N1-N2 as viewed by an observer at N1
    // facing N2
    bool LEFT(double, double, double, double, double, double);

    // Updates the data structure with the addition of a new node
    // in position K
    int ADDNOD(int, double, double, int, int, int*, int&, double*, double*, int*, int*, int*, int&);

    // Locates a point P relative to a triangulation created by
    // subroutine TRMESH or TRMSHR
    void TRFIND(int, double, double, double*, double*, int*, int*, int*, int&, int&, int&);

    // Returns the index (LIST pointer) of NB in C the adjacency
    // list for N0, where LPL = LEND(N0)
    int LSTPTR(int, int, int*, int*);

    // Check if a triangle lies in a constraint region
    bool CRTRI(int, int*, int, int, int);

    // Returns the index, if any, of an exterior constraint curve
    int INDXCC(int, int*, int, int*, int*);

    // Adds a boundary node to a triangulation of a set of points
    // in the plane
    void BDYADD(int, int, int, int*, int*, int*, int&);

    // Inserts a node as a neighbor of N1 following N2
    void INSERT(int, int, int*, int*, int&);

    // Adds an interior node to a triangulation of a set of points
    // in the plane
    void INTADD(int, int, int, int, int*, int*, int*, int&);

    // Replaces a diagonal arc in a strictly convex quadrilateral
    // with the other diagonal
    void SWAP(int, int, int, int, int*, int*, int*, int&);

    // Applies the circumcircle test to a quadrilateral defined
    // by a pair of adjacent triangles
    bool SWPTST(int, int, int, int, double*, double*);

    // Converts a triangulation data structure from the linked list
    // created by subroutine TRMESH or TRMSHR to a triangle list
    int TRLIST(int, int*, int, int*, int*, int*, int, int&, int*);

    // Sets NPTS(L) to the index of the next node in the sequence --
    // the node, other than NPTS(1),...,NPTS(L-1), which is closest
    // to NPTS(1)
    int GETNP(int, int*, int, double*, double*, int*, int*, int*, int, int*, double*);

    // Given a pair of line segments P1-P2 and P3-P4, returns 'true'
    // if and only if P1-P2 shares one or more points with P3-P4
    bool INTSEC(double, double, double, double, double, double, double, double);
};


template <class P>
class Akima761Method : public ContourMethod<P>, public Akima761MethodAttributes {
public:
    Akima761Method() { MagLog::dev() << "Akima761Method::Akima761Method-->" << *this << "\n"; }
    virtual ~Akima761Method() override {}

    ContourMethod<P>* clone() const override {
        Akima761Method<P>* method = new Akima761Method<P>();
        method->copy(*this);
        return method;
    }

    virtual void set(const map<string, string>& map) override { Akima761MethodAttributes::set(map); }

    virtual void set(const XmlNode& node) override { Akima761MethodAttributes::set(node); }
    virtual bool accept(const string& node) override {
        return Akima761MethodAttributes::accept(node) override;
        ;
    }
    virtual bool needPoints() override { return true; }
    virtual MatrixHandler<P>* handlePoints(const AbstractPoints<P>& points, const Layout&) override {
        return new Akima761<P>(points, *this) override;
    }
    virtual MatrixHandler<P>* handler(const AbstractMatrix&, const, const BasicGraphicsObjectContainer&) override {
        return 0 override;
    }

protected:
    //! Method to print string about this class on to a stream of type ostream (virtual).
    virtual void print(ostream& out) const override {
        out << "Akima761Method[";
        Akima761Attributes::print(out);
        out << "]";
    }

private:
    //! Copy constructor - No copy allowed
    Akima761Method(const Akima761Method&);
    //! Overloaded << operator to copy - No copy allowed
    Akima761Method& operator=(const Akima761Method&);

    // -- Friends
    //! Overloaded << operator to call print().
    friend ostream& operator<<(ostream& s, const Akima761Method<P>& p) {
        p.print(s);
        return s;
    }
};

}  // namespace magics

#include "Akima761.cc"
#include "Tripack.cc"

#endif
