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

#include <sys/ioctl.h>
#include <unistd.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <random>
#include <algorithm>

#include "../io/term_io.h"

namespace gctl
{
    class cliplot
    {
    public:
        /**
         * @brief 构建一个新的画布对象
         * 
         * @param width 画布宽度（大于终端宽度则取终端宽度）
         * @param height 画布高度（大于终端高度则取终端高度）
         * @param xmin x轴最小值
         * @param xmax x轴最大值
         * @param ymin y轴最小值
         * @param ymax y轴最大值
         */
        cliplot(size_t width, size_t height, double xmin, double xmax, double ymin, double ymax);

        virtual ~cliplot(); ///< 析构函数

        /**
         * @brief 清除画布数据
         */
        void clear();

        /**
         * @brief 设置画布字符属性
         * 
         * @param w 字符列位置
         * @param h 字符行位置
         * @param sym 字符
         * @param att 字符属性
         */
        void set(size_t w, size_t h, char sym, const std::string &att);

        /**
         * @brief 设置坐标轴刻度数量
         * 
         * @param xt_num x轴刻度数量
         * @param yt_num y轴刻度数量
         */
        void set_axis(int xt_num, int yt_num);

        /**
         * @brief 设置坐标轴刻度数字位数
         *
         * @param digs 数字位数
         */
        void set_digs(int digs);

        /**
         * @brief 设置是否使用新的屏幕显示画布
         *
         * @param new_screen 是否使用新的屏幕显示画布
         */
        void set_new_screen(bool new_screen);

        /**
         * @brief 设置坐标轴名称
         *
         * @param wname x轴名称
         */
        void set_wname(const std::string &wname);

        /**
         * @brief 设置坐标轴名称
         *
         * @param hname y轴名称
         */
        void set_hname(const std::string &hname);

        /**
         * @brief 绘制直线
         *
         * @param x1 直线起点x坐标
         * @param x2 直线终点x坐标
         * @param y1 直线起点y坐标
         * @param y2 直线终点y坐标
         * @param s 直线字符
         * @param t 直线字符属性
         */
        void plot_line(double x1, double x2, double y1, double y2, char s = '.', const std::string &t = GCTL_BOLDGREEN);

        /**
         * @brief 绘制函数
         *
         * @param func 函数
         * @param s 函数字符
         * @param t 函数字符属性
         */
        template <typename FuncOp>
        void plot_func(FuncOp func, char s = '.', const std::string &t = GCTL_BOLDGREEN);

        /**
         * @brief 绘制数据
         *
         * @param x x坐标
         * @param y y坐标
         * @param s 数据点字符
         * @param t 数据点字符属性
         */
        void plot_data(const std::vector<double> &x, const std::vector<double> &y, char s = '.', const std::string &t = GCTL_BOLDGREEN);

        /**
         * @brief 显示画布
         *
         * @param os 输出流
         */
        void display(std::ostream &os = std::cout);

    private:
        /**
         * @brief 计算坐标轴刻度标签
         *
         * @param num 数值
         * @param digs 数字位数
         * @param odr 数值的10的幂次
         * @return std::string 坐标轴刻度标签
         */
        std::string axis_label(double num, int digs, int &odr);

        /**
         * @brief 绘制坐标轴
         */
        void plot_axis();

    private:
        bool new_screen_;
        int digs_; // 数字位数（包括小数点）
        int xl_num_; // x轴刻度数量
        int yl_num_; // y轴刻度数量
        double xmin_; // x轴最小值
        double xmax_; // x轴最大值
        double ymin_; // y轴最小值
        double ymax_; // y轴最大值
        double dx_, dy_; // x轴与y轴的刻度间隔
        size_t w0_, h0_; // 原点的行与列位置
        size_t width_; // 画布宽度
        size_t height_; // 画布高度
        std::vector<char> sym_; // 画布字符
        std::vector<std::string> att_; // 画布字符属性
        std::string wname_;
        std::string hname_;
    };

    template <typename FuncOp>
    void gctl::cliplot::plot_func(FuncOp func, char s, const std::string &t)
    {
        for (size_t w = w0_; w < width_; w++)
        {
            double y, x = xmin_ + (w - w0_)*(xmax_ - xmin_)/(width_ - 1 - w0_);

            y = func(x - 0.25*dx_);
            int h1 = round(h0_ - 1 - (y - ymin_)*(h0_ - 1)/(ymax_ - ymin_));

            y = func(x + 0.25*dx_);
            int h2 = round(h0_ - 1 - (y - ymin_)*(h0_ - 1)/(ymax_ - ymin_));

            if (h1 > h2) std::swap(h1, h2);
            for (size_t h = h1; h <= h2; h++)
                set(w, h, s, t);
        }
        return;
    }
};

#endif // _GCTL_CLIPLOT_H