use image_rs::ImageError as IError;
use shared::core::image::PixelFormat;
use std::fmt;
use std::sync::Arc;
use thiserror::Error;

#[derive(Clone, Debug)]
pub struct FileLoc {
    pub filename: Arc<str>,
    pub line: i32,
    pub column: i32,
}

impl Default for FileLoc {
    fn default() -> Self {
        Self {
            filename: Arc::from(""),
            line: 1,
            column: 0,
        }
    }
}

impl FileLoc {
    pub fn new(filename: &str) -> Self {
        Self {
            filename: Arc::from(filename),
            line: 1,
            column: 0,
        }
    }
}

impl fmt::Display for FileLoc {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}:{}:{}", self.filename, self.line, self.column)
    }
}

#[derive(Error, Debug)]
pub enum ImageError {
    #[error("I/O error: {0}")]
    Io(#[from] std::io::Error),

    #[error("Image file error: {0}")]
    Image(#[from] IError),

    #[error("EXR file error: {0}")]
    Exr(#[from] exr::error::Error),

    #[error("QOI file error: {0}")]
    Qoi(String),

    #[error("Pixel format error: {0}")]
    PixelFormat(#[from] PixelFormatError),

    #[error("Unsupported operation: {0}")]
    Unsupported(String),

    #[error("Mismatched dimensions or channels: {0}")]
    Mismatch(String),

    #[error("Channel '{0}' not found in image")]
    ChannelNotFound(String),
}

/// Describes an error related to an unexpected or unsupported pixel format.
#[derive(Error, Debug, Clone, PartialEq, Eq)]
pub enum PixelFormatError {
    #[error("Unsupported operation '{operation}' for pixel format {actual:?}. Reason: {reason}")]
    UnsupportedOperation {
        operation: String,
        actual: PixelFormat,
        reason: String,
    },

    #[error("Invalid conversion from pixel format {from:?} to {to:?}.")]
    InvalidConversion { from: PixelFormat, to: PixelFormat },

    #[error(
        "Internal invariant violated: image format is {expected:?} but pixel data is of a different type."
    )]
    InternalFormatMismatch { expected: PixelFormat },
}
