#pragma once
#include <chrono>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <stdlib.h>
#include <vector>
#include <algorithm>
#include <cstring>
#include <fstream>
#include <numeric>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "ANSLicense.h"


namespace ANSCENTER
{
    /*#define CODE_INIT 0
    #define CODE_OK 100
    #define CODE_OK_NONE 101
    #define MSG_OK_NONE(p) "No text found in image. Path: \"" + p + "\""
    #define CODE_ERR_PATH_EXIST 200
    #define MSG_ERR_PATH_EXIST(p) "Image path dose not exist. Path: \"" + p + "\""
    #define CODE_ERR_PATH_CONV 201
    #define MSG_ERR_PATH_CONV(p) "Image path failed to convert to utf-16 wstring. Path: \"" + p + "\""
    #define CODE_ERR_PATH_READ 202
    #define MSG_ERR_PATH_READ(p) "Image open failed. Path: \"" + p + "\""
    #define CODE_ERR_PATH_DECODE 203
    #define MSG_ERR_PATH_DECODE(p) "Image decode failed. Path: \"" + p + "\""
    #define CODE_ERR_CLIP_OPEN 210
    #define MSG_ERR_CLIP_OPEN "Clipboard open failed."
    #define CODE_ERR_CLIP_EMPTY 211
    #define MSG_ERR_CLIP_EMPTY "Clipboard is empty."
    #define CODE_ERR_CLIP_FORMAT 212
    #define MSG_ERR_CLIP_FORMAT "Clipboard format is not valid."
    #define CODE_ERR_CLIP_DATA 213
    #define MSG_ERR_CLIP_DATA "Getting clipboard data handle failed."
    #define CODE_ERR_CLIP_FILES 214
    #define MSG_ERR_CLIP_FILES(n) "Clipboard number of query files is not valid. Number: " + std::to_string(n)
    #define CODE_ERR_CLIP_GETOBJ 215
    #define MSG_ERR_CLIP_GETOBJ "Clipboard get bitmap object failed."
    #define CODE_ERR_CLIP_BITMAP 216
    #define MSG_ERR_CLIP_BITMAP "Getting clipboard bitmap bits failed."
    #define CODE_ERR_CLIP_CHANNEL 217
    #define MSG_ERR_CLIP_CHANNEL(n) "Clipboard number of image channels is not valid. Number: " + std::to_string(n)
    #define CODE_ERR_BASE64_DECODE 300
    #define MSG_ERR_BASE64_DECODE "Base64 decode failed."
    #define CODE_ERR_BASE64_IM_DECODE 301
    #define MSG_ERR_BASE64_IM_DECODE "Base64 data imdecode failed."
    #define CODE_ERR_JSON_DUMP 400
    #define MSG_ERR_JSON_DUMP "Json dump failed."
    #define CODE_ERR_JSON_PARSE 401
    #define MSG_ERR_JSON_PARSE "Json parse failed."
    #define CODE_ERR_JSON_PARSE_KEY 402
    #define MSG_ERR_JSON_PARSE_KEY(k) "Json parse key [" + k + "] failed."
    #define CODE_ERR_NO_TASK 403
    #define MSG_ERR_NO_TASK "No valid tasks."*/


    struct OCRModelConfig {
        bool			userGPU;
        bool			useTensorRT;
        int				gpuId;
        int				gpuMemory;
        int             cpuThreads;
        bool			enableMKLDNN;
        bool			ensureASCII;
        OCRLanguage     ocrLanguage;
        std::string	    precisionType;
        std::string	    ocrType;
        std::string		limitType;

        bool			useDetector;
        bool			useRecognizer;
        bool			useCLS;
        bool			useTable;
        bool			useLayout;

        std::string		detectionModelDir;
        std::string		recognizerModelDir;
        std::string		detectionScoreMode;
        std::string		recogizerCharDictionaryPath;
        std::string		clsModelDir;
        std::string		layoutModelDir;
        std::string		layourDictionaryPath;
        std::string		tableModelDir;
        std::string		tableCharDictionaryPath;

        int				limitSideLen;
        double			detectionDBThreshold;
        double			detectionBoxThreshold;
        double			detectionDBUnclipRatio;
        bool            useDilation;
        bool            useAngleCLS;
        double			clsThreshold;
        int				clsBatchNumber;
        int				recognizerBatchNum;
        int				recoginzerImageHeight;
        int				recoginzerImageWidth;
        double			layoutScoreThreshold;
        double			layoutNMSThreshold;
        int				tableModelMaxLengh;
        int				tableBatchNum;
        bool			mergeNoSpanStructure;
    };
    struct OCRObject
    {
        int classId{ 0 };
        std::string className{};
        float confidence{ 0.0 };
        cv::Rect box{};
        cv::Mat mask{}; //Json string mask ="point1.x,point1.y,...."
        std::string extraInfo;// More information such as facial recognition
    };

    struct OCRPredictResult
    {
        std::vector<std::vector<int>> box;
        std::string text;
        float score = -1.0;
        float cls_score;
        int cls_label = -1;
    };
    struct StructurePredictResult
    {
        std::vector<float> box;
        std::vector<std::vector<int>> cell_box;
        std::string type;
        std::vector<OCRPredictResult> text_res;
        std::string html;
        float html_score = -1;
        float confidence;
    };
    class ANSOCRUtility
    {
    public:
        static std::vector<std::string> ReadDict(const std::string &path);
        static void VisualizeBboxes(const cv::Mat &srcimg,
                                    const std::vector<OCRPredictResult> &ocr_result,
                                    const std::string &save_path);
        static void VisualizeBboxes(const cv::Mat &srcimg,
                                    const StructurePredictResult &structure_result,
                                    const std::string &save_path);
        template <class ForwardIterator>
        inline static size_t argmax(ForwardIterator first, ForwardIterator last)
        {
            return std::distance(first, std::max_element(first, last));
        }
        static void GetAllFiles(const char *dir_name,
                                std::vector<std::string> &all_inputs);

        static cv::Mat GetRotateCropImage(const cv::Mat &srcimage,
                                          std::vector<std::vector<int>> box);

        static std::vector<int> argsort(const std::vector<float> &array);

        static std::string basename(const std::string &filename);

        static bool PathExists(const std::string &path);

        static void CreateDir(const std::string &path);

        static void print_result(const std::vector<OCRPredictResult> &ocr_result);

        static cv::Mat crop_image(cv::Mat &img, const std::vector<int> &area);
        static cv::Mat crop_image(cv::Mat &img, const std::vector<float> &area);

        static void sorted_boxes(std::vector<OCRPredictResult> &ocr_result);

        static std::vector<int> xyxyxyxy2xyxy(std::vector<std::vector<int>> &box);
        static std::vector<int> xyxyxyxy2xyxy(std::vector<int> &box);

        static float fast_exp(float x);
        static std::vector<float>
        activation_function_softmax(std::vector<float> &src);
        static float iou(std::vector<int> &box1, std::vector<int> &box2);
        static float iou(std::vector<float> &box1, std::vector<float> &box2);
        static std::string OCRDetectionToJsonString(const std::vector<OCRObject>& dets);

    private:
        static bool comparison_box(const OCRPredictResult &result1,
                                   const OCRPredictResult &result2)
        {
            if (result1.box[0][1] < result2.box[0][1])
            {
                return true;
            }
            else if (result1.box[0][1] == result2.box[0][1])
            {
                return result1.box[0][0] < result2.box[0][0];
            }
            else
            {
                return false;
            }
        }
    };

    class AutoLogger {
    private:
        SPDLogger& _logger = SPDLogger::instance("AOCREngine", false);
    public:
        AutoLogger() {};
        void Initalise(std::string model_name,
            bool use_gpu,
            bool enable_tensorrt,
            bool enable_mkldnn,
            int cpu_threads,
            int batch_size,
            std::string input_shape,
            std::string model_precision,
            std::vector<double> time_info,
            int img_num) {
            this->model_name_ = model_name;
            this->use_gpu_ = use_gpu;
            this->enable_tensorrt_ = enable_tensorrt;
            this->enable_mkldnn_ = enable_mkldnn;
            this->cpu_threads_ = cpu_threads;
            this->batch_size_ = batch_size;
            this->input_shape_ = input_shape;
            this->model_precision_ = model_precision;
            this->time_info_ = time_info;
            this->img_num_ = img_num;
        }
        void LogDebug(std::string source, std::string message) {
            this->_logger.LogDebug(source, message);
        }
        void LogFatal(std::string source, std::string message) {
            this->_logger.LogFatal(source, message);
        }
        void LogError(std::string source, std::string message) {
            this->_logger.LogError(source, message);
        }

        void LogInfo(std::string source, std::string message) {
            this->_logger.LogInfo(source, message);
        }
        void Report() 
        {
            if (this->use_gpu_)
            {
                this->_logger.LogInfo(this->model_name_, "Runtime device: GPU");
            }
            else {
                this->_logger.LogInfo(this->model_name_, "Runtime device: CPU");
            }
            if (this->enable_tensorrt_) {
                this->_logger.LogInfo(this->model_name_,"Enable TensorRT: True");
            }
            else {
                this->_logger.LogInfo(this->model_name_,"Enable TensorRT:False");
            }
            if (this->enable_mkldnn_) {
                this->_logger.LogInfo(this->model_name_,"Enable MKLDNN:True");
            }
            else {
                this->_logger.LogInfo(this->model_name_,"Enable MKLDNN:False");
            }
            this->_logger.LogInfo(this->model_name_,"CPU Math Number Threads"+ std::to_string(this->cpu_threads_));
        }

    private:
        std::string model_name_;
        bool use_gpu_ = false;
        bool enable_tensorrt_ = false;
        bool enable_mkldnn_ = true;
        int cpu_threads_ = 10;
        int batch_size_ = 1;
        std::string input_shape_ = "dynamic";
        std::string model_precision_ = "fp32";
        std::vector<double> time_info_;
        int img_num_;
    };

} // namespace ANSCENTER