#pragma once

#include <depthai/pipeline/datatype/CameraControl.hpp>

#include "depthai/pipeline/Node.hpp"

// shared
#include <depthai-shared/properties/ColorCameraProperties.hpp>

namespace dai {
namespace node {

/**
 * @brief ColorCamera node. For use with color sensors.
 */
class ColorCamera : public NodeCRTP<Node, ColorCamera, ColorCameraProperties> {
   public:
    constexpr static const char* NAME = "ColorCamera";

   protected:
    Properties& getProperties();

   private:
    std::shared_ptr<RawCameraControl> rawControl;

   public:
    /**
     * Constructs ColorCamera node.
     */
    ColorCamera(const std::shared_ptr<PipelineImpl>& par, int64_t nodeId);
    ColorCamera(const std::shared_ptr<PipelineImpl>& par, int64_t nodeId, std::unique_ptr<Properties> props);

    /**
     * Computes the scaled size given numerator and denominator
     */
    int getScaledSize(int input, int num, int denom) const;

    /**
     * Initial control options to apply to sensor
     */
    CameraControl initialControl;

    /**
     * Input for ImageManipConfig message, which can modify crop parameters in runtime
     *
     * Default queue is non-blocking with size 8
     */
    Input inputConfig{*this, "inputConfig", Input::Type::SReceiver, false, 8, {{DatatypeEnum::ImageManipConfig, false}}};

    /**
     * Input for CameraControl message, which can modify camera parameters in runtime
     *
     * Default queue is blocking with size 8
     */
    Input inputControl{*this, "inputControl", Input::Type::SReceiver, true, 8, {{DatatypeEnum::CameraControl, false}}};

    /**
     * Outputs ImgFrame message that carries NV12 encoded (YUV420, UV plane interleaved) frame data.
     *
     * Suitable for use with VideoEncoder node
     */
    Output video{*this, "video", Output::Type::MSender, {{DatatypeEnum::ImgFrame, false}}};

    /**
     * Outputs ImgFrame message that carries BGR/RGB planar/interleaved encoded frame data.
     *
     * Suitable for use with NeuralNetwork node
     */
    Output preview{*this, "preview", Output::Type::MSender, {{DatatypeEnum::ImgFrame, false}}};

    /**
     * Outputs ImgFrame message that carries NV12 encoded (YUV420, UV plane interleaved) frame data.
     *
     * The message is sent only when a CameraControl message arrives to inputControl with captureStill command set.
     */
    Output still{*this, "still", Output::Type::MSender, {{DatatypeEnum::ImgFrame, false}}};

    /**
     * Outputs ImgFrame message that carries YUV420 planar (I420/IYUV) frame data.
     *
     * Generated by the ISP engine, and the source for the 'video', 'preview' and 'still' outputs
     */
    Output isp{*this, "isp", Output::Type::MSender, {{DatatypeEnum::ImgFrame, false}}};

    /**
     * Outputs ImgFrame message that carries RAW10-packed (MIPI CSI-2 format) frame data.
     *
     * Captured directly from the camera sensor, and the source for the 'isp' output.
     */
    Output raw{*this, "raw", Output::Type::MSender, {{DatatypeEnum::ImgFrame, false}}};

    /**
     * Specify which board socket to use
     * @param boardSocket Board socket to use
     */
    void setBoardSocket(CameraBoardSocket boardSocket);

    /**
     * Retrieves which board socket to use
     * @returns Board socket to use
     */
    CameraBoardSocket getBoardSocket() const;

    /// Set which color camera to use
    [[deprecated("Use 'setBoardSocket()' instead")]] void setCamId(int64_t id);

    /// Get which color camera to use
    [[deprecated("Use 'setBoardSocket()' instead")]] int64_t getCamId() const;

    /// Set camera image orientation
    void setImageOrientation(CameraImageOrientation imageOrientation);

    /// Get camera image orientation
    CameraImageOrientation getImageOrientation() const;

    /// Set color order of preview output images. RGB or BGR
    void setColorOrder(ColorCameraProperties::ColorOrder colorOrder);

    /// Get color order of preview output frames. RGB or BGR
    ColorCameraProperties::ColorOrder getColorOrder() const;

    /// Set planar or interleaved data of preview output frames
    void setInterleaved(bool interleaved);

    /// Get planar or interleaved data of preview output frames
    bool getInterleaved() const;

    /// Set fp16 (0..255) data type of preview output frames
    void setFp16(bool fp16);

    /// Get fp16 (0..255) data of preview output frames
    bool getFp16() const;

    /// Set preview output size
    void setPreviewSize(int width, int height);

    /// Set preview output size, as a tuple <width, height>
    void setPreviewSize(std::tuple<int, int> size);

    /// Set number of frames in preview pool
    void setPreviewNumFramesPool(int num);

    /// Set video output size
    void setVideoSize(int width, int height);

    /// Set video output size, as a tuple <width, height>
    void setVideoSize(std::tuple<int, int> size);

    /// Set number of frames in preview pool
    void setVideoNumFramesPool(int num);

    /// Set still output size
    void setStillSize(int width, int height);

    /// Set still output size, as a tuple <width, height>
    void setStillSize(std::tuple<int, int> size);

    /// Set number of frames in preview pool
    void setStillNumFramesPool(int num);

    /// Set sensor resolution
    void setResolution(Properties::SensorResolution resolution);

    /// Get sensor resolution
    Properties::SensorResolution getResolution() const;

    /// Set number of frames in raw pool
    void setRawNumFramesPool(int num);

    /// Set number of frames in isp pool
    void setIspNumFramesPool(int num);

    /// Set number of frames in all pools
    void setNumFramesPool(int raw, int isp, int preview, int video, int still);

    /**
     * Set 'isp' output scaling (numerator/denominator), preserving the aspect ratio.
     * The fraction numerator/denominator is simplified first to a irreducible form,
     * then a set of hardware scaler constraints applies:
     * max numerator = 16, max denominator = 63
     */
    void setIspScale(int numerator, int denominator);

    /// Set 'isp' output scaling, as a tuple <numerator, denominator>
    void setIspScale(std::tuple<int, int> scale);

    /**
     * Set 'isp' output scaling, per each direction. If the horizontal scaling factor
     * (horizNum/horizDen) is different than the vertical scaling factor
     * (vertNum/vertDen), a distorted (stretched or squished) image is generated
     */
    void setIspScale(int horizNum, int horizDenom, int vertNum, int vertDenom);

    /// Set 'isp' output scaling, per each direction, as <numerator, denominator> tuples
    void setIspScale(std::tuple<int, int> horizScale, std::tuple<int, int> vertScale);

    /**
     * Set rate at which camera should produce frames
     * @param fps Rate in frames per second
     */
    void setFps(float fps);

    /**
     * Get rate at which camera should produce frames
     * @returns Rate in frames per second
     */
    float getFps() const;

    /// Get preview size as tuple
    std::tuple<int, int> getPreviewSize() const;
    /// Get preview width
    int getPreviewWidth() const;
    /// Get preview height
    int getPreviewHeight() const;

    /// Get video size as tuple
    std::tuple<int, int> getVideoSize() const;
    /// Get video width
    int getVideoWidth() const;
    /// Get video height
    int getVideoHeight() const;

    /// Get still size as tuple
    std::tuple<int, int> getStillSize() const;
    /// Get still width
    int getStillWidth() const;
    /// Get still height
    int getStillHeight() const;

    /// Get sensor resolution as size
    std::tuple<int, int> getResolutionSize() const;
    /// Get sensor resolution width
    int getResolutionWidth() const;
    /// Get sensor resolution height
    int getResolutionHeight() const;

    /// Get 'isp' output resolution as size, after scaling
    std::tuple<int, int> getIspSize() const;
    /// Get 'isp' output width
    int getIspWidth() const;
    /// Get 'isp' output height
    int getIspHeight() const;

    /**
     * Specify sensor center crop.
     * Resolution size / video size
     */
    void sensorCenterCrop();

    /**
     * Specifies sensor crop rectangle
     * @param x Top left X coordinate
     * @param y Top left Y coordinate
     */
    void setSensorCrop(float x, float y);

    /**
     * @returns Sensor top left crop coordinates
     */
    std::tuple<float, float> getSensorCrop() const;
    /// Get sensor top left x crop coordinate
    float getSensorCropX() const;
    /// Get sensor top left y crop coordinate
    float getSensorCropY() const;

    // Node properties configuration
    /**
     * Specify to wait until inputConfig receives a configuration message,
     * before sending out a frame.
     * @param wait True to wait for inputConfig message, false otherwise
     */
    [[deprecated("Use 'inputConfig.setWaitForMessage()' instead")]] void setWaitForConfigInput(bool wait);

    /**
     * @see setWaitForConfigInput
     * @returns True if wait for inputConfig message, false otherwise
     */
    [[deprecated("Use 'inputConfig.setWaitForMessage()' instead")]] bool getWaitForConfigInput() const;

    /**
     * Specifies whether preview output should preserve aspect ratio,
     * after downscaling from video size or not.
     *
     * @param keep If true, a larger crop region will be considered to still be able to
     * create the final image in the specified aspect ratio. Otherwise video size is resized to fit preview size
     */
    void setPreviewKeepAspectRatio(bool keep);

    /**
     * @see setPreviewKeepAspectRatio
     * @returns Preview keep aspect ratio option
     */
    bool getPreviewKeepAspectRatio();

    /// Get number of frames in preview pool
    int getPreviewNumFramesPool();
    /// Get number of frames in video pool
    int getVideoNumFramesPool();
    /// Get number of frames in still pool
    int getStillNumFramesPool();
    /// Get number of frames in raw pool
    int getRawNumFramesPool();
    /// Get number of frames in isp pool
    int getIspNumFramesPool();
};

}  // namespace node
}  // namespace dai
