#include "ANSOVFBFaceDetector.h"
#include <models/detection_model_faceboxes.h>
#include <pipelines/metadata.h>
#include <models/input_data.h>
#include <models/results.h>
#include "Utility.h"
namespace ANSCENTER {
    bool ANSOVFBFD::Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) {
        bool result = ANSFDBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap);
        labelMap = "Face";
        // We do not need to check for the license 
        _licenseValid = true;
        if (!_licenseValid) return false;
        try {
            _modelConfig = modelConfig;
            _modelConfig.modelType = ModelType::FACEDETECT;
            _modelConfig.detectionType = DetectionType::FACEDETECTOR;

            // We need to get the modelfolder from here
            std::string xmlfile = CreateFilePath(_modelFolder, "facebox.xml");
            if (std::filesystem::exists(xmlfile)) {
                _modelFilePath = xmlfile;
                this->_logger.LogDebug("ANSOVFBFD::Initialize.  Loading ansovfb weight", _modelFilePath, __FILE__, __LINE__);
            }
            else {
                this->_logger.LogError("ANSOVFBFD::Initialize.  Model ansovfb.xml file is not exist", _modelFilePath, __FILE__, __LINE__);
                return false;
            }
            std::string landmarkModel = CreateFilePath(_modelFolder, "landmarks.xml");
            if (std::filesystem::exists(landmarkModel)) {
                _landmarkModelFilePath = landmarkModel;
                this->_logger.LogDebug("ANSOVFBFD::Initialize.  Loading landmarks weight", _landmarkModelFilePath, __FILE__, __LINE__);
                ov::Core lmcore;
                CnnConfig landmarks_config(_landmarkModelFilePath, "Facial Landmarks Regression");
                landmarks_config.m_deviceName = "GPU";
                landmarks_config.m_max_batch_size = 1;
                landmarks_config.m_core = lmcore;
                this->_faceLandmark = std::make_unique<VectorCNN>(landmarks_config);
                if (_faceLandmark == nullptr) {
                    this->_logger.LogFatal("ANSOVFBFD::Initialize", "Failed to initialize face recognizer model", __FILE__, __LINE__);
                }
            }
            else {
                this->_logger.LogError("ANSOVFBFD::Initialize.  Model landmarks.xml file is not exist", _landmarkModelFilePath, __FILE__, __LINE__);
            }

            std::vector<std::string> labels;
            labels.push_back(labelMap);
            std::unique_ptr<ModelBase> _faceDetector;
            _faceDetector.reset(new ModelFaceBoxes(_modelFilePath,
                static_cast<float>(0.5),
                false,
                static_cast<float>(0.5),
                ""));
            _faceDetector->setInputsPreprocessing(false, "", "");
            ov::Core core;
            this->_faceDectectorPipeline = std::make_unique<AsyncPipeline>(std::move(_faceDetector), ConfigFactory::getUserConfig("GPU", 0, "", 0), core);

            _isInitialized = true;
            return true;
        }
        catch (std::exception& e) {
            this->_logger.LogFatal("ANSOVFBFD::Initialize", e.what(), __FILE__, __LINE__);
            return false;
        }
    }

    bool ANSOVFBFD::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) {
        try {
            // We need to get the _modelFolder
            bool result = ANSFDBase::LoadModel(modelZipFilePath, modelZipPassword);
            if (!result) return false;
            // We need to get the modelfolder from here
            std::string xmlfile = CreateFilePath(_modelFolder, "ansovfb.xml");
            if (std::filesystem::exists(xmlfile)) {
                _modelFilePath = xmlfile;
                this->_logger.LogDebug("ANSOVFBFD::LoadModel.  Loading ansovfb weight", _modelFilePath, __FILE__, __LINE__);
            }
            else {
                this->_logger.LogError("ANSOVFBFD::LoadModel.  Model ansovfb.xml file is not exist", _modelFilePath, __FILE__, __LINE__);
                return false;
            }
            return true;
        }
        catch (std::exception& e) {
            this->_logger.LogFatal("ANSOVFBFD::LoadModel", e.what(), __FILE__, __LINE__);
            return false;
        }

    }
    bool ANSOVFBFD::OptimizeModel(bool fp16, std::string& optimizedModelFolder) {
        if (!FileExist(_modelFilePath)) {
            optimizedModelFolder = "";
            return false;
        }
        return true;
    }
    std::vector<Object> ANSOVFBFD::RunInference(const cv::Mat& input) {
        std::vector<Object> output;
        output.clear();
        if (!_licenseValid) {
            this->_logger.LogError("ANSOVFBFD::RunInference", "Invalid license", __FILE__, __LINE__);
            return output;
        }
        if (!_isInitialized) {
			this->_logger.LogError("ANSOVFBFD::RunInference", "Model is not initialized", __FILE__, __LINE__);
			return output;
		}
        try {
            bool croppedFace = false; // Check if the image is cropped face image
            cv::Mat im = input.clone();
            //std::cout << "Width x Height=" << input.size[0] << "x" << input.size[1] << std::endl;
            // We know that the image sizes <=300 px, it is likely that image is cropped for face only
            if ((input.size[0] <= 300) || (input.size[1] <= 300)) croppedFace = true;
            if (croppedFace) cv::copyMakeBorder(input, im, 200, 200, 200, 200, cv::BORDER_REPLICATE);

            ;
            auto startTime = std::chrono::steady_clock::now();
            int64_t frameNum = _faceDectectorPipeline->submitData(ImageInputData(im), std::make_shared<ImageMetaData>(im, startTime));
            _faceDectectorPipeline->waitForTotalCompletion();
            std::unique_ptr<ResultBase> result = _faceDectectorPipeline->getResult();

            auto outputImg = result->metaData->asRef<ImageMetaData>().img;
            if (outputImg.empty()) {
                throw std::invalid_argument("Renderer: image provided in metadata is empty");
            }
            for (auto& obj : result->asRef<DetectionResult>().objects) {
                Object result;
                std::vector<cv::Mat> face_rois;
                std::vector<cv::Mat> landmarks;

                float confidence = obj.confidence;
                if (confidence >= _modelConfig.detectionScoreThreshold)
                {
                    int x_min = obj.x;
                    int y_min = obj.y;
                    int x_max = obj.x + obj.width;
                    int y_max = obj.y + obj.height;
                    result.classId = obj.labelID;
                    result.className = obj.label;
                    result.confidence = confidence;
                    result.box.x = x_min;
                    result.box.y = y_min;
                    if (croppedFace) {
                        if (x_min <= 200) x_min = 200;
                        if (y_min <= 200) y_min = 200;
                        result.box.x = x_min - 200;
                        result.box.y = y_min - 200;
                    }
                    result.box.width = x_max - x_min;
                    result.box.height = y_max - y_min;

                    if (_faceLandmark != nullptr) {
                        landmarks.clear();
                        face_rois.clear();
                        cv::Mat face_roi = ANSUtilityHelper::GetCroppedFace(im, x_min, y_min, x_max, y_max);
                        face_rois.push_back(face_roi);
                        _faceLandmark->Compute(face_rois, &landmarks, cv::Size(2, 5));
                        ANSUtilityHelper::AlignFaces(&face_rois, &landmarks);
                        result.mask = face_rois[0];
                        face_roi.release();
                    }
                    else {
                        result.mask = ANSUtilityHelper::GetCroppedFace(im, x_min, y_min, x_max, y_max);
                    }
					result.cameraId = "FBFaceCAM";
                    output.push_back(result);
                }
            }
            im.release();
            EnqueueDetection(output, "FBFaceCAM");
            return output;
        }
        catch (std::exception& e) {
            this->_logger.LogFatal("ANSOVFBFD::RunInference", e.what(), __FILE__, __LINE__);
            return output;
        }
    }
    std::vector<Object> ANSOVFBFD::RunInference(const cv::Mat& input, const std::string& camera_id) {
        std::vector<Object> output;
        output.clear();
        if (!_licenseValid) {
            this->_logger.LogError("ANSOVFBFD::RunInference", "Invalid license", __FILE__, __LINE__);
            return output;
        }
        if (!_isInitialized) {
            this->_logger.LogError("ANSOVFBFD::RunInference", "Model is not initialized", __FILE__, __LINE__);
            return output;
        }
        try {
            bool croppedFace = false; // Check if the image is cropped face image
            cv::Mat im = input.clone();
            //std::cout << "Width x Height=" << input.size[0] << "x" << input.size[1] << std::endl;
            // We know that the image sizes <=300 px, it is likely that image is cropped for face only
            if ((input.size[0] <= 300) || (input.size[1] <= 300)) croppedFace = true;
            if (croppedFace) cv::copyMakeBorder(input, im, 200, 200, 200, 200, cv::BORDER_REPLICATE);

            ;
            auto startTime = std::chrono::steady_clock::now();
            int64_t frameNum = _faceDectectorPipeline->submitData(ImageInputData(im), std::make_shared<ImageMetaData>(im, startTime));
            _faceDectectorPipeline->waitForTotalCompletion();
            std::unique_ptr<ResultBase> result = _faceDectectorPipeline->getResult();

            auto outputImg = result->metaData->asRef<ImageMetaData>().img;
            if (outputImg.empty()) {
                throw std::invalid_argument("Renderer: image provided in metadata is empty");
            }
            for (auto& obj : result->asRef<DetectionResult>().objects) {
                Object result;
                std::vector<cv::Mat> face_rois;
                std::vector<cv::Mat> landmarks;

                float confidence = obj.confidence;
                if (confidence >= _modelConfig.detectionScoreThreshold)
                {
                    int x_min = obj.x;
                    int y_min = obj.y;
                    int x_max = obj.x + obj.width;
                    int y_max = obj.y + obj.height;
                    result.classId = obj.labelID;
                    result.className = obj.label;
                    result.confidence = confidence;
                    result.box.x = x_min;
                    result.box.y = y_min;
                    if (croppedFace) {
                        if (x_min <= 200) x_min = 200;
                        if (y_min <= 200) y_min = 200;
                        result.box.x = x_min - 200;
                        result.box.y = y_min - 200;
                    }
                    result.box.width = x_max - x_min;
                    result.box.height = y_max - y_min;

                    if (_faceLandmark != nullptr) {
                        landmarks.clear();
                        face_rois.clear();
                        cv::Mat face_roi = ANSUtilityHelper::GetCroppedFace(im, x_min, y_min, x_max, y_max);
                        face_rois.push_back(face_roi);
                        _faceLandmark->Compute(face_rois, &landmarks, cv::Size(2, 5));
                        ANSUtilityHelper::AlignFaces(&face_rois, &landmarks);
                        result.mask = face_rois[0];
                        face_roi.release();
                    }
                    else {
                        result.mask = ANSUtilityHelper::GetCroppedFace(im, x_min, y_min, x_max, y_max);
                    }
					result.cameraId = camera_id;    
                    output.push_back(result);
                }
            }
            im.release();
			EnqueueDetection(output, camera_id);
            return output;
        }
        catch (std::exception& e) {
            this->_logger.LogFatal("ANSOVFBFD::RunInference", e.what(), __FILE__, __LINE__);
            return output;
        }
    }

    ANSOVFBFD::~ANSOVFBFD() {
        try {
        }
        catch (std::exception& e) {
            this->_logger.LogFatal("ANSOVFBFD::Destroy", e.what(), __FILE__, __LINE__);
        }
    }
    bool ANSOVFBFD::Destroy() {
        try {
			return true;
        }
        catch (std::exception& e) {
            this->_logger.LogFatal("ANSOVFBFD::Destroy", e.what(), __FILE__, __LINE__);
			return false;
        }
    }
}