/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * 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 "meshdata.h"
#include "gctl/io.h"
#include "gctl/algorithms.h"

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

	enum mesh_dim_e
	{
		MESH_0D,
		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 os 输出流
		 */
		void show_info(std::ostream &os = std::clog) const;

		/**
		 * @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_loctype 网格数据类型（块体或顶点数据）
		 * @param in_valtype 网格数据值类型
		 * @param name 数据名称
		 * @param init_val 数据初始值
		 * @param if_output 数据是否可用于输出
		 * @param nan_val 数据无效值标记
		 * 
		 * @return 数据对象的引用（非常量的）
		 */
		meshdata &add_data(mesh_data_type_e in_loctype, mesh_data_value_e in_valtype, std::string name, 
			double init_val, bool if_output = true, double nan_val = GCTL_BDL_MAX);

		/**
		 * @brief 添加网格数据，并返回数据对象的引用。
		 *
		 * @param in_loctype 网格数据类型（块体或顶点数据）
		 * @param name 数据名称
		 * @param init_arr 数据初始值数组
		 * @param if_output 数据是否可用于输出
		 * @param nan_val 数据无效值标记
		 * 
		 * @return 数据对象的引用（非常量的）
		 */
		meshdata &add_data(mesh_data_type_e in_loctype, std::string name, const array<double> &init_arr, 
			bool if_output = true, double nan_val = GCTL_BDL_MAX);

		/**
		 * @brief 添加网格数据，并返回数据对象的引用。
		 *
		 * @param in_loctype 网格数据类型（块体或顶点数据）
		 * @param name 数据名称
		 * @param init_arr 数据初始值数组
		 * @param if_output 数据是否可用于输出
		 * @param nan_val 数据无效值标记
		 * 
		 * @return 数据对象的引用（非常量的）
		 */
		meshdata &add_data(mesh_data_type_e in_loctype, std::string name, const array<point3dc> &init_arr, 
			bool if_output = true, double nan_val = GCTL_BDL_MAX);

		/**
		 * @brief 添加网格数据，并返回数据对象的引用。
		 *
		 * @param in_loctype 网格数据类型（块体或顶点数据）
		 * @param name 数据名称
		 * @param init_arr 数据初始值数组
		 * @param if_output 数据是否可用于输出
		 * @param nan_val 数据无效值标记
		 * 
		 * @return 数据对象的引用（非常量的）
		 */
		meshdata &add_data(mesh_data_type_e in_loctype, std::string name, const array<tensor> &init_arr, 
			bool if_output = true, double nan_val = GCTL_BDL_MAX);

		/**
		 * @brief 返回对应名称的数据对象引用。
		 * 
		 * @param datname 数据名
		 * @return 数据对象的引用（非常量的）
		 */
		meshdata &get_data(std::string datname);

		/**
		 * @brief 返回对应名称的数据对象常量引用。
		 * 
		 * @param datname 数据名
		 * @return 数据对象的引用（常量的）
		 */
		const meshdata &get_data(std::string datname) const;

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

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

		/**
		 * @brief 保存网格到Gmsh文件，并保存所有数据。
		 *
		 * @param filename 文件名
		 * @param out_mode 输出模式
		 * @param packed 索引是否为紧凑（从0开始）
		 */
		void save_gmsh_withdata(std::string filename, output_type_e out_mode, index_packed_e packed = Packed);
		
		/**
		 * @brief 保存网格到Gmsh文件，并保存指定名称的数据。
		 *
		 * @param filename 文件名
		 * @param datname 数据名
		 * @param out_mode 输出模式
		 * @param packed 索引是否为紧凑（从0开始）
		 */
		void save_gmsh_withdata(std::string filename, std::string datname, output_type_e out_mode, index_packed_e packed = Packed);

		/**
		 * @brief 保存网格到Gmsh文件，并读入所有数据。
		 *
		 * @param filename 文件名
		 * @param packed 索引是否为紧凑（从0开始）
		 */
		void load_gmsh_withdata(std::string filename, index_packed_e packed = Packed);

		/**
		 * @brief 保存网格到Gmsh文件，并读入特定数据。
		 *
		 * @param filename 文件名
		 * @param packed 索引是否为紧凑（从0开始）
		 */
		void load_gmsh_withdata(std::string filename, std::string datname, mesh_data_type_e type, index_packed_e packed = Packed);

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

		/**
		 * @brief （基类纯虚函数）读入Gmsh文件。
		 *
		 * @param filename 文件名
		 * @param packed 索引是否为紧凑（从0开始）
		 */
		virtual void load_gmsh(std::string filename, index_packed_e packed = Packed) = 0;

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

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

		/**
		 * @brief （基类纯虚函数）显示网格的维度信息
		 */
		virtual void show_mesh_dimension(std::ostream &os) const = 0;

	protected:
		mesh_type_e meshtype_;
		mesh_dim_e  meshdim_;
		std::string meshname_;
		std::string meshinfo_;
		gmshio meshio_;

		int node_num_, ele_num_;
		bool initialized_;

		std::vector<meshdata> datalist_;

		/**
		 * 以下为类的私有函数 只能被类的公共函数调用
		 */

		/**
		 * @brief 检查网格是否已经初始化。
		 * 
		 */
		void check_initiated(bool inverse = false) const;
		
		/**
		 * @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 datname 数据名
		 */
		int data_index(std::string datname) const;
		
		/**
		 * @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