#ifndef ANSMOTIONDETECTOR_H
#define ANSMOTIONDETECTOR_H
#pragma once
#include "ANSEngineCommon.h"
#include <string>
#include <opencv2/opencv.hpp>
#include <openvino/openvino.hpp>
#include <array>

namespace ANSCENTER
{
    class ANSENGINE_API ANSMOTIONDETECTOR : public ANSODBase
    {

        protected:
            struct CameraStats {
                size_t total_frames_processed = 0;
                size_t frames_with_movement = 0;
                size_t frames_rejected_by_temporal_check = 0;
                size_t control_frames_count = 0;
                double average_psnr = 0.0;
                double min_psnr = std::numeric_limits<double>::max();
                double max_psnr = 0.0;
                double average_temporal_consistency = 0.0;
                std::chrono::milliseconds total_processing_time{ 0 };
                std::chrono::high_resolution_clock::time_point last_movement_time;

                // Reset stats
                void reset()
                {
                    total_frames_processed = 0;
                    frames_with_movement = 0;
                    frames_rejected_by_temporal_check = 0;
                    control_frames_count = 0;
                    average_psnr = 0.0;
                    min_psnr = std::numeric_limits<double>::max();
                    max_psnr = 0.0;
                    average_temporal_consistency = 0.0;
                    total_processing_time = std::chrono::milliseconds{ 0 };
                }
            };
            struct CameraData
            {
                // Detection state
                bool movement_detected = false;
                bool transition_detected = false;
                size_t next_frame_index = 0;
                size_t next_key_frame = 0;
                double most_recent_psnr_score = 0.0;
                size_t frame_index_with_movement = 0;
                double max_change_percentage = 15.0;
                double min_change_percentage = 0.5;
                std::chrono::high_resolution_clock::time_point movement_last_detected;

                // Control frames storage
                std::map<size_t, cv::Mat> control_frames;

                // Output data
                cv::Mat output;
                cv::Mat mask;
                std::vector<std::vector<cv::Point>> contours;

                // Configuration parameters
                size_t key_frame_frequency = 20;
                size_t number_of_control_frames = 5;  // CHANGED: Reduce from 10 to 5
                double psnr_threshold = 35;
                double thumbnail_ratio = 0.1;
                cv::Size thumbnail_size = cv::Size(0, 0);

                // NEW: Maximum age of control frames to compare (in frames)
                size_t max_control_frame_age = 20;    // Only compare frames within last 100 frames (~5 seconds at 20fps)


                // NEW: Sensitivity multiplier (1.0 = normal, 1.2 = more sensitive, 0.8 = less sensitive)
                double sensitivity_multiplier = 1.1;

                // Mask computation parameters
                int mask_diff_threshold = 10;        // Fixed threshold for difference detection
                double mask_min_percentage = 0.01;    // Min % of frame to be valid movement
                double mask_max_percentage = 40.0;   // Max % of frame (reject as scene change)
                double min_mask_pixels_percentage = 0.05;  // 0.02% of working image

                // Filtering parameters
                double min_object_area = 1000;
                int min_object_dimension = 5;
                int min_object_total_size = 25;

                // Morphology parameters
                int morphology_iterations = 2;

                // Temporal consistency parameters
                bool temporal_consistency_enabled = true;
                double mask_overlap_threshold = 0.1;
                size_t temporal_history_size = 5;
                size_t min_consistent_frames = 3;
                bool location_stability_enabled = true;
                double max_location_jitter = 30.0;

                // Temporal consistency state
                std::deque<cv::Mat> mask_history;
                std::deque<cv::Point> centroid_history;
                size_t consistent_frame_count = 0;
                double last_temporal_consistency_score = 0.0;

                // Statistics
                CameraStats stats;

                // Clear function to release memory
                void clear()
                {
                    for (auto& [index, frame] : control_frames)
                    {
                        frame.release();
                    }
                    control_frames.clear();
                    output.release();
                    mask.release();
                    contours.clear();

                    // Clear temporal history
                    for (auto& m : mask_history)
                    {
                        m.release();
                    }
                    mask_history.clear();
                    centroid_history.clear();

                    // Reset state
                    movement_detected = false;
                    transition_detected = false;
                    most_recent_psnr_score = 0.0;
                    frame_index_with_movement = 0;
                    thumbnail_size = cv::Size(0, 0);
                    consistent_frame_count = 0;
                    last_temporal_consistency_score = 0.0;
                }
            };
           
            // Private member functions
            double psnr(const cv::Mat& src, const cv::Mat& dst);
            cv::Mat simple_colour_balance(const cv::Mat& src);
            cv::Rect BoundingBoxFromContour(const std::vector<cv::Point>& contour);
            void setDynamicPSNRThreshold(CameraData& camera, const cv::Mat& working_image);
            // Multi-camera data storage
            std::unordered_map<std::string, CameraData> cameras;
            mutable std::recursive_mutex cameras_mutex;

            // Helper functions
            CameraData& getCameraData(const std::string& camera_id);
            const CameraData* getCameraDataConst(const std::string& camera_id) const;
            bool cameraExists(const std::string& camera_id) const;

            // Processing helpers
            cv::Mat computeMovementMask(const cv::Mat& control_frame, const cv::Mat& current_frame,
                const cv::Size& output_size, int morphology_iterations, const CameraData& camera);
            std::vector<Object> extractObjectsFromMask(const cv::Mat& mask, const cv::Mat& image,
                CameraData& camera, const std::string& camera_id);
            void updateControlFrames(CameraData& camera, size_t frame_index, const cv::Mat& thumbnail);
            void updateStatistics(CameraData& camera, double psnr, bool movement_detected,
                std::chrono::milliseconds processing_time);

            // Temporal consistency helpers
            bool checkTemporalConsistency(CameraData& camera, const cv::Mat& current_mask);
            double calculateMaskOverlap(const cv::Mat& mask1, const cv::Mat& mask2);
            cv::Point calculateMaskCentroid(const cv::Mat& mask);
            double calculateLocationStability(const std::deque<cv::Point>& centroids);
            void updateTemporalHistory(CameraData& camera, const cv::Mat& mask);

            std::vector<Object> MovementDetectInternal(const std::string& camera_id,const size_t frame_index,const cv::Mat& image,CameraData& camera);
            // Main detection methods
            std::vector<Object> MovementDetect(const std::string& camera_id, const cv::Mat& next_image);
            std::vector<Object> MovementDetect(const std::string& camera_id, const size_t frame_index, const cv::Mat& image);

            // Camera management
            bool hasCameraData(const std::string& camera_id) const;
            void removeCamera(const std::string& camera_id);
            std::vector<std::string> getCameraIds() const;

            // Configuration methods
            void setThreshold(const std::string& camera_id, double threshold);
            void setKeyFrameFrequency(const std::string& camera_id, size_t frequency);
            void setNumberOfControlFrames(const std::string& camera_id, size_t count);
            void setThumbnailRatio(const std::string& camera_id, double ratio);
            //void setMaskEnabled(const std::string& camera_id, bool enabled);
            //void setContoursEnabled(const std::string& camera_id, bool enabled);
            //void setBboxEnabled(const std::string& camera_id, bool enabled);
            //void setContourThickness(const std::string& camera_id, int thickness);
            //void setBboxThickness(const std::string& camera_id, int thickness);
            void setMinObjectArea(const std::string& camera_id, double area);
            void setMinObjectSize(const std::string& camera_id, int size);
            void setMorphologyIterations(const std::string& camera_id, int iterations);

            // Temporal consistency settings
            void setTemporalConsistency(const std::string& camera_id, bool enabled);
            void setMaskOverlapThreshold(const std::string& camera_id, double threshold);
            void setTemporalHistorySize(const std::string& camera_id, size_t size);
            void setMinConsistentFrames(const std::string& camera_id, size_t frames);
            void setLocationStabilityEnabled(const std::string& camera_id, bool enabled);
            void setMaxLocationJitter(const std::string& camera_id, double pixels);

            // Getters for configuration
            double getThreshold(const std::string& camera_id) const;
            size_t getKeyFrameFrequency(const std::string& camera_id) const;
            size_t getNumberOfControlFrames(const std::string& camera_id) const;
            double getThumbnailRatio(const std::string& camera_id) const;
            bool isMaskEnabled(const std::string& camera_id) const;
            bool isContoursEnabled(const std::string& camera_id) const;
            bool isBboxEnabled(const std::string& camera_id) const;
            bool isTemporalConsistencyEnabled(const std::string& camera_id) const;

            // State query methods
            bool isMovementDetected(const std::string& camera_id) const;
            bool wasTransitionDetected(const std::string& camera_id) const;
            double getPSNRScore(const std::string& camera_id) const;
            size_t getFrameIndexWithMovement(const std::string& camera_id) const;
            std::chrono::milliseconds getTimeSinceLastMovement(const std::string& camera_id) const;
            size_t getControlFrameCount(const std::string& camera_id) const;
            size_t getNextFrameIndex(const std::string& camera_id) const;
            double getTemporalConsistencyScore(const std::string& camera_id) const;

            // Public utility methods
            bool empty(const std::string& camera_id) const;
            void clear(const std::string& camera_id);
            void clearAll();
            /*cv::Mat getOutput(const std::string& camera_id) const;*/
            const cv::Mat& getOutput(const std::string& camera_id) const;
            const cv::Mat getMask(const std::string& camera_id) const;
            std::vector<std::vector<cv::Point>> getContours(const std::string& camera_id) const;
            CameraStats getStats(const std::string& camera_id) const;
            void resetStats(const std::string& camera_id);

        protected:
            std::string     _modelFilePath;
        public:
            virtual bool Initialize(std::string licenseKey, ModelConfig modelConfig, const std::string& modelZipFilePath, const std::string& modelZipPassword, std::string& labelMap) override;
            virtual bool LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) override;
            virtual bool LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig, std::string modelName, std::string className, const std::string& modelFolder, std::string& labelMap)override;
            virtual bool OptimizeModel(bool fp16, std::string& optimizedModelFolder);
            std::vector<Object> RunInference(const cv::Mat& input);
            std::vector<Object> RunInference(const cv::Mat& input, const std::string& camera_id);

            bool Destroy();
            ~ANSMOTIONDETECTOR();
    };
}

#endif
