/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * 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_BASE_MESH_H
#define _GCTL_BASE_MESH_H

#include "list"

#include "new_meshdata.h"
//#include "meshdata.h"
//#include "meshdata_scalar.h"
//#include "meshdata_vector.h"
//#include "meshdata_tensor.h"

#include "gctl/io.h"
#include "gctl/algorithm.h"

namespace gctl
{
	enum mesh_type_e
	{
		REGULAR_MESH,
		LINEAR_MESH,
		TRI_TET_MESH,
		REGULAR_MESH_SPH,
		LINEAR_MESH_SPH,
		TRI_TET_MESH_SPH,
		REGULAR_GRID,
	};

	enum mesh_dim_e
	{
		MESH_2D,
		MESH_3D,
	};

	/**
	 * @brief      网格对象（纯虚类）。此类为所有具体网格类型的基类
	 */
	class base_mesh
	{
	public:
		base_mesh();
		virtual ~base_mesh();
		
		/**
		 * @brief 清除所有网格数据。
		 * 
		 */
		void clear();

		/**
		 * @brief 检查网格是否已经初始化。
		 * 
		 */
		bool initiated() const;

		/**
		 * @brief 检查是否存在对应名称的数据。
		 * 
		 * @param datname 数据名
		 */
		bool saved(std::string datname) const;

		/**
		 * @brief 返回对应名称的数据对象指针。
		 * 
		 * @param datname 数据名
		 * @return 数据指针
		 */
		meshdata *get_data(std::string datname) const;

		/**
		 * @brief 返回所有数据指针。
		 * 
		 * @param out_list 数据指针列表
		 */
		void get_all_data(array<meshdata*> &out_list) const;

		/**
		 * @brief 返回对应名称的数据对象的数组指针。
		 * 
		 * @param datname 数据名
		 * @return 数组指针（注意需要转换为对应的数组指针类型）
		 */
		void *get_datval(std::string datname) const;

		/**
		 * @brief 删除对应名称的数据对象。
		 * 
		 * @param datname 数据名
		 */
		void remove_data(std::string datname);

		/**
		 * @brief 显示网格与数据信息。
		 * 
		 * @param os 输出流
		 */
		void show_info(std::ostream &os = std::clog) const;

		/**
		 * @brief 重命名数据对象。
		 * 
		 * @param oldname 旧名称
		 * @param newname 新名称
		 */
		void rename_data(std::string oldname, std::string newname);

		/**
		 * @brief 返回网格类型
		 * 
		 * @return 网格类型 
		 */
		mesh_type_e get_meshtype() const;

		/**
		 * @brief 返回网格维度类型
		 * 
		 * @return 网格维度
		 */
		mesh_dim_e get_meshdim() const;

		/**
		 * @brief 返回顶点数量
		 * 
		 * @return 顶点数量
		 */
		int get_nodenum() const;

		/**
		 * @brief 返回网格单元数量
		 * 
		 * @return 单元数量
		 */
		int get_elenum() const;

		/**
		 * @brief 返回数据对象数量
		 * 
		 * @return 数据对象数量
		 */
		int get_datanum() const;

		/**
		 * @brief 返回网格名称
		 * 
		 * @return 网格名称
		 */
		std::string get_meshname() const;

		/**
		 * @brief 设置网格名称
		 * 
		 * @param in_name 网格名称
		 */
		void set_meshname(std::string in_name);

		/**
		 * @brief 返回网格说明信息
		 * 
		 * @return 说明信息
		 */
		std::string get_meshinfo() const;

		/**
		 * @brief 设置网格说明信息
		 * 
		 * @param in_info 说明信息
		 */
		void set_meshinfo(std::string in_info);

		/**
		 * @brief 添加标量网格数据对象并赋初始值，如果对象已经存在则赋初始值
		 * 
		 * @param in_name 数据对象名称
		 * @param in_type 数据对象类型
		 * @param if_output 数据对象是否可输出
		 * @param init_val 初始值
		 * @return 新建的数据对象指针
		 */
		meshdata *add_data(std::string in_name, mesh_data_type_e in_type, bool if_output, double init_val);
		
		/**
		 * @brief 添加适量网格数据对象并赋初始值，如果对象已经存在则赋初始值
		 * 
		 * @param in_name 数据对象名称
		 * @param in_type 数据对象类型
		 * @param if_output 数据对象是否可输出
		 * @param init_val 初始值
		 * @return 新建的数据对象指针
		 */
		meshdata *add_data(std::string in_name, mesh_data_type_e in_type, bool if_output, gctl::point3dc init_val);
		
		/**
		 * @brief 添加张量网格数据对象并赋初始值，如果对象已经存在则赋初始值
		 * 
		 * @param in_name 数据对象名称
		 * @param in_type 数据对象类型
		 * @param if_output 数据对象是否可输出
		 * @param init_val 初始值
		 * @return 新建的数据对象指针
		 */
		meshdata *add_data(std::string in_name, mesh_data_type_e in_type, bool if_output, gctl::tensor init_val);
		
		/**
		 * @brief 添加张量网格数据对象并初始为0值
		 * 
		 * @param in_name 数据对象名称
		 * @param in_type 数据对象类型
		 * @param if_output 数据对象是否可输出
		 * @param init_val 初始值
		 * @return 新建的数据对象指针
		 */
		meshdata *add_data(std::string in_name, mesh_data_type_e in_type, bool if_output, mesh_data_value_e val_type);

		/**
		 * @brief （基类虚函数）初始化规则网格
		 * 
		 * @param in_name 网格名称
		 * @param in_info 网格说明信息
		 * @param xnum 网格x轴数量
		 * @param ynum 网格y轴数量
		 * @param xmin 网格x轴最小值
		 * @param ymin 网格y轴最小值
		 * @param dx 网格x轴间隔
		 * @param dy 网格y轴间隔
		 */
		virtual void init(std::string in_name, std::string in_info, int xnum, int ynum, 
			double xmin, double ymin, double dx, double dy);

		/**
		 * @brief 
		 * 
		 * @param in_name 
		 * @param in_info 
		 * @param xbnum 
		 * @param ybnum 
		 * @param zbnum 
		 * @param xmin 
		 * @param ymin 
		 * @param zmin 
		 * @param xsize 
		 * @param ysize 
		 * @param zsize 
		 */
		virtual void init(std::string in_name, std::string in_info, int xbnum, int ybnum, int zbnum, 
			double xmin, double ymin, double zmin, double xsize, double ysize, double zsize);
		
		/**
		 * @brief 
		 * 
		 * @param in_name 
		 * @param in_info 
		 * @param lon_min 
		 * @param lat_min 
		 * @param rad_min 
		 * @param lon_size 
		 * @param lat_size 
		 * @param rad_size 
		 * @param lon_bnum 
		 * @param lat_bnum 
		 * @param rad_bnum 
		 */
		virtual void init(std::string in_name, std::string in_info, double lon_min, double lat_min, 
			double rad_min, double lon_size, double lat_size, double rad_size, int lon_bnum, int lat_bnum, int rad_bnum);
		
		/**
		 * @brief 
		 * 
		 * @param in_name 
		 * @param in_info 
		 * @param xmin 
		 * @param ymin 
		 * @param xsizes 
		 * @param ysizes 
		 */
		virtual void init(std::string in_name, std::string in_info, double xmin, double ymin, 
			const gctl::array<double> &xsizes, const gctl::array<double> &ysizes);
		
		/**
		 * @brief 
		 * 
		 * @param in_name 
		 * @param in_info 
		 * @param xmin 
		 * @param ymin 
		 * @param zmin 
		 * @param xsizes 
		 * @param ysizes 
		 * @param zsizes 
		 */
		virtual void init(std::string in_name, std::string in_info, double xmin, double ymin, 
			double zmin, const gctl::array<double> &xsizes, const gctl::array<double> &ysizes, 
			const gctl::array<double> &zsizes);
		
		/**
		 * @brief 
		 * 
		 * @param in_name 
		 * @param in_info 
		 * @param in_nodes 
		 * @param in_triangles 
		 */
		virtual void init(std::string in_name, std::string in_info, const gctl::array<gctl::vertex2dc> &in_nodes, 
			const gctl::array<gctl::triangle2d> &in_triangles);
		
		/**
		 * @brief 
		 * 
		 * @param in_name 
		 * @param in_info 
		 * @param in_nodes 
		 * @param in_tets 
		 */
		virtual void init(std::string in_name, std::string in_info, const gctl::array<gctl::vertex3dc> &in_nodes, 
			const gctl::array<gctl::tetrahedron> &in_tets);

		/**
		 * @brief 显示网格的维度信息
		 * 
		 */
		virtual void show_mesh_dimension(std::ostream &os) const;

		/**
		 * @brief （基类纯虚函数）读入二进制网格文件
		 * 
		 * @param filename 文件名
		 */
		virtual void load_binary(std::string filename) = 0;

		/**
		 * @brief （基类纯虚函数）写入二进制网格文件
		 * 
		 * @param filename 文件名
		 */
		virtual void save_binary(std::string filename) = 0;

		/**
		 * @brief （基类虚函数）保存网格到Gmsh文件。
		 * 
		 * @param filename 文件名
		 * @param packed 索引是否为紧凑（从0开始）
		 */
		virtual void save_gmsh(std::string filename, index_packed_e packed = Packed);
		
		/**
		 * @brief 
		 * 
		 * @param filename 
		 * @param d_type 
		 * @param out_mode 
		 * @param packed 
		 */
		void save_gmsh(std::string filename, mesh_data_type_e d_type, output_type_e out_mode, index_packed_e packed = Packed);
		
		/**
		 * @brief 
		 * 
		 * @param filename 
		 * @param datname 
		 * @param out_mode 
		 * @param packed 
		 */
		void save_gmsh(std::string filename, std::string datname, output_type_e out_mode, index_packed_e packed = Packed);

		/**
		 * @brief      将输入的自由点位上的浮点数据插值为规则网络的顶点或者块体数据
		 *
		 * @param      in_posi      输入的自由点位的坐标
		 * @param      in_val       输入的自由点位的数值
		 * @param[in]  search_xlen  插值搜索区域的x半径
		 * @param[in]  search_ylen  插值搜索区域的y半径
		 * @param[in]  search_deg   插值搜索区域的x半径绕x轴正方向逆时针旋转的角度（弧度）
		 * @param[in]  datname      网格后的数据名称
		 * @param[in]  d_type       网格后的数据类型
		 */
		virtual void load_data_cloud(const array<point2dc> &in_posi, const array<double> &in_val, double search_xlen, 
			double search_ylen, double search_deg, std::string datname, mesh_data_type_e d_type = NodeData);
		
		/**
		 * @brief 
		 * 
		 * @param in_posi 
		 * @param in_val 
		 * @param search_xlen 
		 * @param search_ylen 
		 * @param search_deg 
		 * @param datname 
		 * @param d_type 
		 */
		virtual void load_data_cloud(const array<point3dc> &in_posi, const array<double> &in_val, double search_xlen, 
			double search_ylen, double search_deg, std::string datname, mesh_data_type_e d_type = NodeData);

		/**
		 * @brief 
		 * 
		 * @param datname 
		 * @param in_posi 
		 * @param out_val 
		 */
		virtual void extract_points(std::string datname, const array<point2dc> &in_posi, array<double> &out_val);
		
		/**
		 * @brief 
		 * 
		 * @param datname 
		 * @param in_posi 
		 * @param out_val 
		 */
		virtual void extract_points(std::string datname, const array<point3dc> &in_posi, array<double> &out_val);
		
		/**
		 * @brief 
		 * 
		 * @param datname 
		 * @param start_p 
		 * @param end_p 
		 * @param size_p 
		 * @param out_posi 
		 * @param out_val 
		 */
		virtual void extract_profile(std::string datname, const point2dc &start_p, const point2dc &end_p, int size_p, 
			array<point2dc> &out_posi, array<double> &out_val);
		
		/**
		 * @brief 
		 * 
		 * @param datname 
		 * @param start_p 
		 * @param end_p 
		 * @param size_p 
		 * @param dh 
		 * @param out_posi 
		 * @param out_val 
		 */
		virtual void extract_profile(std::string datname, const point3dc &start_p, const point3dc &end_p, int size_p, 
			double dh, array<point3dc> &out_posi, array<double> &out_val);

		/**
		 * @brief 
		 * 
		 * @param datname 
		 * @param p_type 
		 * @param v_type 
		 * @param para_str 
		 * @param in_val 
		 */
		virtual void edit_data(std::string datname, physical_type_e p_type, value_operator_e v_type, std::string para_str, double in_val);
		
		/**
		 * @brief 
		 * 
		 * @param datname 
		 * @param p_type 
		 * @param v_type 
		 * @param para_str 
		 * @param in_val 
		 */
		virtual void edit_data(std::string datname, physical_type_e p_type, value_operator_e v_type, std::string para_str, point3dc in_val);
		
		/**
		 * @brief 
		 * 
		 * @param datname 
		 * @param p_type 
		 * @param v_type 
		 * @param para_str 
		 * @param in_val 
		 */
		virtual void edit_data(std::string datname, physical_type_e p_type, value_operator_e v_type, std::string para_str, tensor in_val);

		/**
		 * @brief 
		 * 
		 * @param datname 
		 * @param in_val 
		 */
		virtual void purge_data(std::string datname, double in_val);
		
		/**
		 * @brief 
		 * 
		 * @param datname 
		 * @param in_val 
		 */
		virtual void purge_data(std::string datname, point3dc in_val);
		
		/**
		 * @brief 
		 * 
		 * @param datname 
		 * @param in_val 
		 */
		virtual void purge_data(std::string datname, tensor in_val);

	protected:
		mesh_type_e meshtype;
		mesh_dim_e  meshdim;
		std::string meshname;
		std::string meshinfo;

		int node_num, ele_num;
		bool initialized;

		std::list<meshdata*> saved_data;
		std::list<meshdata*>::iterator iter;

		/**
		 * 以下为类的私有函数 只能被类的公共函数调用
		 */
		
		/**
		 * @brief 初始化网格
		 * 
		 * @param in_type 
		 * @param in_dim 
		 * @param in_name 
		 * @param in_info 
		 */
		void init(mesh_type_e in_type, mesh_dim_e in_dim, std::string in_name, std::string in_info);

		/**
		 * @brief 读入二进制网格头信息
		 * 
		 * @param infile 输入流
		 * @param expected_type 预期获取的网格类型 
		 * @param expected_dim 预期获得的网格维度类型
		 */
		void load_headinfo(std::ifstream &infile, mesh_type_e expected_type, mesh_dim_e expected_dim);
		
		/**
		 * @brief 读入二进制数据块
		 * 
		 * @param infile 输入流
		 */
		void load_datablock(std::ifstream &infile);
		
		/**
		 * @brief 写入二进制网格头信息
		 * 
		 * @param outfile 输出流
		 */
		void save_headinfo(std::ofstream &outfile);
		
		/**
		 * @brief 写入二进制数据块
		 * 
		 * @param outfile 输出流
		 */
		void save_datablock(std::ofstream &outfile);
	};
}

#endif //_GCTL_BASE_MESH_H