/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * Geophysical Computational Tools & Library (GCTL)
 *
 * Copyright (c) 2023  Yi Zhang (yizhang-geo@zju.edu.cn)
 *
 * GCTL is distributed under a dual licensing scheme. You can redistribute 
 * it and/or modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation, either version 2 
 * of the License, or (at your option) any later version. You should have 
 * received a copy of the GNU Lesser General Public License along with this 
 * program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * If the terms and conditions of the LGPL v.2. would prevent you from using 
 * the GCTL, please consider the option to obtain a commercial license for a 
 * fee. These licenses are offered by the GCTL's original author. As a rule, 
 * licenses are provided "as-is", unlimited in time for a one time fee. Please 
 * send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget 
 * to include some description of your company and the realm of its activities. 
 * Also add information on how to contact you by electronic and paper mail.
 ******************************************************/

#ifndef _GCTL_RECTANGLE2D_H
#define _GCTL_RECTANGLE2D_H

#include "vertex.h"
#include "entity.h"

namespace gctl
{
    // Declaration of the basic rectangle2d type
    template <typename A> struct type_rectangle2d;

    typedef type_rectangle2d<void> rectangle2d; // rectangle2d type of attribute type of void

    /**
	 * @brief      structure of a rectangle under the Cartesian coordinates (2D)
	 */
    template <typename A>
	struct type_rectangle2d : public entity<vertex2dc, 4, A>
	{
		/**
		 * the down left and up right corners of the block. These two points efficiently define 
		 * dimensions of the block.
		 */
		vertex2dc *dl, *ur;
        /**
         * neighbors of the block stored in the order x neighbor on left, x neighbor on right, 
         * y neighbor in front, y neighbor in back
         */
        type_rectangle2d<A> *neigh[4];
        /**
         * constructor
         */
		type_rectangle2d();
        /**
         * @brief      Constructs an object from a vertex array
         *
         * @param[in]  vert_arr  The vertex array
         * @param[in]  n0_id     The vertex 0 index
         * @param[in]  n1_id     The vertex 1 index
         * @param[in]  n2_id     The vertex 2 index
         * @param[in]  n3_id     The vertex 3 index
         * @param[in]  index     The element index
         */
        type_rectangle2d(const array<vertex2dc> &vert_arr, int n0_id, int n1_id, int n2_id, int n3_id, int index = 0);
        /**
         * @brief      Construct a new block object from parameters
         * 
         * @warning    This function will locate memories to store vertice
         *
         * @param[in]  xmin   Minimal coordinates of x
         * @param[in]  xmax   Maximal coordinates of x
         * @param[in]  ymin   Minimal coordinates of y
         * @param[in]  ymax   Maximal coordinates of y
         * @param[in]  index  The element index
         */
        type_rectangle2d(double xmin, double xmax, double ymin, double ymax, int index = 0);
        /**
         * @brief      de-constructor
         */
        virtual ~type_rectangle2d(){}
        /**
         * @brief      Constructs an object from a vertex array
         *
         * @param[in]  vert_arr  The vertex array
         * @param[in]  n0_id     The vertex 0 index
         * @param[in]  n1_id     The vertex 1 index
         * @param[in]  n2_id     The vertex 2 index
         * @param[in]  n3_id     The vertex 3 index
         * @param[in]  index     The element index
         */
        void set(const array<vertex2dc> &vert_arr, int n0_id, int n1_id, int n2_id, int n3_id, int index = 0);
        /**
         * @brief      Construct a new block object from parameters
         * 
         * @warning    This function will locate memories to store vertice
         *
         * @param[in]  xmin   Minimal coordinates of x
         * @param[in]  xmax   Maximal coordinates of x
         * @param[in]  ymin   Minimal coordinates of y
         * @param[in]  ymax   Maximal coordinates of y
         * @param[in]  index  The element index
         */
        void set(double xmin, double xmax, double ymin, double ymax, int index = 0);
        /**
         * @brief      Reset the structure to initial state
         */
        void reset();
	};

    template <typename A>
    type_rectangle2d<A>::type_rectangle2d() : entity<vertex2dc, 4, A>::entity()
    {
        dl = ur = nullptr;
        for (int i = 0; i < 4; ++i)
        {
            neigh[i] = nullptr;
        }
    }

    template <typename A>
    type_rectangle2d<A>::type_rectangle2d(const array<vertex2dc> &vert_arr, int n0_id, int n1_id, int n2_id, int n3_id, int index) : type_rectangle2d()
    {
        set(vert_arr, n0_id, n1_id, n2_id, n3_id, index);
    }

    template <typename A>
    type_rectangle2d<A>::type_rectangle2d(double xmin, double xmax, double ymin, double ymax, int index) : type_rectangle2d()
    {
        set(xmin, xmax, ymin, ymax, index);
    }

    template <typename A>
    void type_rectangle2d<A>::set(const array<vertex2dc> &vert_arr, int n0_id, int n1_id, int n2_id, int n3_id, int index)
    {
        if (index < 0)
        {
            throw out_of_range("Invalid index, From type_rectangle2d::set(...)");
        }

        if (vert_arr.size() < 4)
        {
            throw domain_error("Invalid array size. From type_rectangle2d::set(...)");
        }

        if (vert_arr[n0_id].x >= vert_arr[n2_id].x || vert_arr[n0_id].y >= vert_arr[n2_id].y)
        {
            throw invalid_argument("Invalid vertex's coordinates. From type_rectangle2d::set(...)");
        }

        this->id = index;
        this->vert[0] = vert_arr.get(n0_id);
        this->vert[1] = vert_arr.get(n1_id);
        this->vert[2] = vert_arr.get(n2_id);
        this->vert[3] = vert_arr.get(n3_id);
        dl = this->vert[0];
        ur = this->vert[2];
        return;
    }

    template <typename A>
    void type_rectangle2d<A>::set(double xmin, double xmax, double ymin, double ymax, int index)
    {
        if (index < 0)
        {
            throw out_of_range("Invalid index, From type_rectangle2d::set(...)");
        }

        if (xmin >= xmax || ymin >= ymax)
        {
            throw invalid_argument("Invalid vertex's coordinates. From type_rectangle2d::set(...)");
        }

        for (int i = 0; i < 4; ++i)
        {
            this->vert[i] = new vertex2dc;
        }
        this->self_host = true;

        this->id = index;
        this->vert[0]->set(point2dc(xmin, ymin), 0);
        this->vert[1]->set(point2dc(xmax, ymin), 1);
        this->vert[2]->set(point2dc(xmax, ymax), 2);
        this->vert[3]->set(point2dc(xmin, ymax), 3);
        dl = this->vert[0];
        ur = this->vert[2];
        return;
    }

    template <typename A>
    void type_rectangle2d<A>::reset()
    {
        entity<vertex2dc, 4, A>::reset();
        dl = ur = nullptr;
        for (int i = 0; i < 4; ++i)
        {
            neigh[i] = nullptr;
        }
        return;
    }
};

#endif // _GCTL_RECTANGLE2D_H