#ifndef ANSCUSTOMCODE_H
#define ANSCUSTOMCODE_H
#pragma once
#include <string>
#include <vector>
#include <opencv2/opencv.hpp>
#define CUSTOM_API __declspec(dllexport)
struct CustomObject
{
    int classId{ 0 };
    int trackId{ 0 };
    std::string className{};
    float confidence{ 0.0 };
    cv::Rect box{};
    std::vector<cv::Point2f> polygon;    // Polygon that contain x1,y1,x2,y2,x3,y3,x4,y4
    cv::Mat mask{};                      // Face image in box (cropped) or mask image for segmentation
    cv::cuda::GpuMat gpuMask{};          // GPU-resident face crop (set by NV12 affine warp, avoids re-upload)
    std::vector<float> kps{};            // Pose exsimate 17 keypoints or oriented bouding box (xcenter, ycenter, width, height, angle)
    std::string extraInfo;               // More information such as facial recognition
    std::string cameraId;                // Use to check if this object belongs to any camera
    //std::string attributes;            // Attributes such as keypoint string
};
struct CustomPoint {
    int x, y;
};
struct CustomROIConfig {
    bool Rectangle;
    bool Polygon;
    bool Line;
    int MinItems;
    int MaxItems;
    std::string Name;
    std::string ROIMatch;
};


// Data type for custom parameters
// DataType can be Boolean, Integer,Float,List-Single, List-Multiple, Range, String, DaySelections
struct CustomParameter {
    std::string Name;
    std::string DataType;
    int NoOfDecimals;
    int MaxValue;
    int MinValue;
    std::string StartValue;
    std::vector<std::string> ListItems;
    std::string DefaultValue;
    std::string Value;
};

struct CustomROIValue {
    std::string ROIMatch;
    std::vector<CustomPoint> ROIPoints;
    std::string Option;
    std::string Name;
    int OriginalImageSize;
};
struct CustomParams {
	std::vector<CustomROIConfig> ROI_Config;
	std::vector<std::string> ROI_Options;
	std::vector<CustomParameter> Parameters;
	std::vector<CustomROIValue> ROI_Values;
};
/* Example
{
  "ROI_Config":[
    {
      "Rectangle":true,
      "Polygon":true,
      "Line":false,
      "MinItems":0,
      "MaxItems":3,
      "Name":"Traffic Light",
      "ROI-Match":"All Corners"
    },
    {
      "Rectangle":true,
      "Polygon":false,
      "Line":false,
      "MinItems":1,
      "MaxItems":1,
      "Name":"Car Zone",
      "ROI-Match":"All Corners"
    },
    {
      "Rectangle":false,
      "Polygon":false,
      "Line":true,
      "MinItems":1,
      "MaxItems":2,
      "Name":"Cross Line",
      "ROI-Match":"All Corners"
    }
  ],
  "ROI_Options":[
    "Inside ROI",
    "Inside ROI",
    "Both Directions"
  ],
  "Parameters":[
    {
      "Name":"Para1",
      "DataType":"Boolean",
      "NoOfdecimals":0,
      "MaxValue":0,
      "MinValue":0,
      "StartValue":"",
      "ListItems":[],
      "DefaultValue":"",
      "Value":"true"
    },
    {
      "Name":"Para2",
      "DataType":"Integer",
      "NoOfdecimals":0,
      "MaxValue":5,
      "MinValue":1,
      "StartValue":"2",
      "ListItems":[],
      "DefaultValue":"",
      "Value":"3"
    },
    {
      "Name":"Para3",
      "DataType":"List-Single",
      "NoOfdecimals":0,
      "MaxValue":0,
      "MinValue":0,
      "StartValue":"",
      "ListItems":["A","B","C"],
      "DefaultValue":"",
      "Value":"A"
    },
    {
      "Name":"Para4",
      "DataType":"Range",
      "NoOfdecimals":0,
      "MaxValue":100,
      "MinValue":50,
      "StartValue":">,60",
      "ListItems":[">","<"],
      "DefaultValue":"",
      "Value":">,52.000000"
    }
  ],
  "ROI_Values":[
    {
      "ROI-Match":"Centre Point",
      "ROIPoints":[
        {"x":269,"y":134},
        {"x":777,"y":134},
        {"x":777,"y":457},
        {"x":269,"y":457}
      ],
      "Option":"Inside ROI",
      "Name":"Car Zone 1",
      "OriginalImageSize":1920
    },
    {
      "ROI-Match":"Centre Point",
      "ROIPoints":[{"x":280,"y":613},{"x":1108,"y":280}],
      "Option":"Above",
      "Name":"Cross Line 1",
      "OriginalImageSize":1920
    },
    {
      "ROI-Match":"Centre Point",
      "ROIPoints":[{"x":1511,"y":383},{"x":1283,"y":754}],
      "Option":"Left side",
      "Name":"Cross Line 2",
      "OriginalImageSize":1920
    },
    {
      "ROI-Match":"Centre Point",
      "ROIPoints":[
        {"x":229,"y":161},
        {"x":964,"y":161},
        {"x":964,"y":628},
        {"x":229,"y":628}
      ],
      "Option":"Left side",
      "Name":"Traffic Light 1",
      "OriginalImageSize":1920
    },
    {
      "ROI-Match":"Centre Point",
      "ROIPoints":[
        {"x":1115,"y":304},
        {"x":1730,"y":304},
        {"x":1730,"y":695},
        {"x":1115,"y":695}
      ],
      "Option":"Left side",
      "Name":"Traffic Light 2",
      "OriginalImageSize":1920
    },
    {
      "ROI-Match":"Centre Point",
      "ROIPoints":[
        {"x":678,"y":683},
        {"x":1217,"y":683},
        {"x":1217,"y":1026},
        {"x":678,"y":1026}
      ],
      "Option":"Left side",
      "Name":"Traffic Light 3",
      "OriginalImageSize":1920
    }
  ]
}
*/
class CUSTOM_API IANSCustomClass
{
protected:
    std::string _modelDirectory; // The directory where the model is located
	float _detectionScoreThreshold{ 0.5 };
    CustomParams _params; // Parameters for the model
	bool _loadEngineOnCreate{ false }; // Load engine on create
public:
    virtual bool Initialize(const std::string& modelDirectory, float detectionScoreThreshold, std::string& labelMap) = 0;
    virtual bool OptimizeModel(bool fp16) = 0;
    virtual std::vector<CustomObject> RunInference(const cv::Mat& input) = 0;
    virtual std::vector<CustomObject> RunInference(const cv::Mat& input, const std::string& camera_id) = 0;

    virtual bool ConfigureParameters(CustomParams& param) = 0;
    bool SetParameters(const CustomParams& param) {
        try {
            this->_params.ROI_Config.clear();
            for (auto& cf : param.ROI_Config) {
                this->_params.ROI_Config.push_back(cf);
            }
            this->_params.ROI_Options.clear();
            for (auto& op : param.ROI_Options) {
                this->_params.ROI_Options.push_back(op);
            }
            this->_params.Parameters.clear();
            for (auto& par : param.Parameters) {
                this->_params.Parameters.push_back(par);
            }
            this->_params.ROI_Values.clear();
            for (auto& roi : param.ROI_Values) {
                this->_params.ROI_Values.push_back(roi);
            }
            return true;
        }
        catch (...) {
            return false;
        }
    };
	void SetLoadEngineOnCreate(bool loadEngineOnCreate) {
		this->_loadEngineOnCreate = loadEngineOnCreate;
	}
    virtual bool Destroy() = 0;
};
#endif