use num_traits::identities::Zero;
use scoped_threadpool::Pool;
#[cfg(test)]
use std::borrow::Cow;
use std::convert::TryFrom;
use std::io::{self, BufRead, Cursor, Read, Seek};
use std::iter::Iterator;
use std::marker::PhantomData;
use std::{error, fmt, mem};
use std::num::{ParseFloatError, ParseIntError};
use std::path::Path;
use crate::Primitive;
use crate::color::{ColorType, Rgb};
use crate::error::{DecodingError, ImageError, ImageFormatHint, ImageResult, ParameterError, ParameterErrorKind, UnsupportedError, UnsupportedErrorKind};
use crate::image::{self, ImageDecoder, ImageDecoderExt, ImageFormat, Progress};
#[derive(Debug, Clone, PartialEq, Eq)]
enum DecoderError {
    
    RadianceHdrSignatureInvalid,
    
    TruncatedHeader,
    
    TruncatedDimensions,
    
    UnparsableF32(LineType, ParseFloatError),
    
    UnparsableU32(LineType, ParseIntError),
    
    LineTooShort(LineType),
    
    ExtraneousColorcorrNumbers,
    
    DimensionsLineTooShort(usize, usize),
    
    DimensionsLineTooLong(usize),
    
    WrongScanlineLength(usize, usize),
    
    FirstPixelRlMarker,
}
impl fmt::Display for DecoderError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            DecoderError::RadianceHdrSignatureInvalid =>
                f.write_str("Radiance HDR signature not found"),
            DecoderError::TruncatedHeader =>
                f.write_str("EOF in header"),
            DecoderError::TruncatedDimensions =>
                f.write_str("EOF in dimensions line"),
            DecoderError::UnparsableF32(line, pe) =>
                f.write_fmt(format_args!("Cannot parse {} value as f32: {}", line, pe)),
            DecoderError::UnparsableU32(line, pe) =>
                f.write_fmt(format_args!("Cannot parse {} value as u32: {}", line, pe)),
            DecoderError::LineTooShort(line) =>
                f.write_fmt(format_args!("Not enough numbers in {}", line)),
            DecoderError::ExtraneousColorcorrNumbers =>
                f.write_str("Extra numbers in COLORCORR"),
            DecoderError::DimensionsLineTooShort(elements, expected) =>
                f.write_fmt(format_args!("Dimensions line too short: have {} elements, expected {}", elements, expected)),
            DecoderError::DimensionsLineTooLong(expected) =>
                f.write_fmt(format_args!("Dimensions line too long, expected {} elements", expected)),
            DecoderError::WrongScanlineLength(len, expected) =>
                f.write_fmt(format_args!("Wrong length of decoded scanline: got {}, expected {}", len, expected)),
            DecoderError::FirstPixelRlMarker =>
                f.write_str("First pixel of a scanline shouldn't be run length marker"),
        }
    }
}
impl From<DecoderError> for ImageError {
    fn from(e: DecoderError) -> ImageError {
        ImageError::Decoding(DecodingError::new(ImageFormat::Hdr.into(), e))
    }
}
impl error::Error for DecoderError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match self {
            DecoderError::UnparsableF32(_, err) => Some(err),
            DecoderError::UnparsableU32(_, err) => Some(err),
            _ => None,
        }
    }
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
enum LineType {
    Exposure,
    Pixaspect,
    Colorcorr,
    DimensionsHeight,
    DimensionsWidth,
}
impl fmt::Display for LineType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(match self {
            LineType::Exposure => "EXPOSURE",
            LineType::Pixaspect => "PIXASPECT",
            LineType::Colorcorr => "COLORCORR",
            LineType::DimensionsHeight => "height dimension",
            LineType::DimensionsWidth => "width dimension",
        })
    }
}
#[derive(Debug)]
pub struct HdrAdapter<R: BufRead> {
    inner: Option<HdrDecoder<R>>,
    
    meta: HdrMetadata,
}
#[allow(dead_code)]
#[deprecated(note = "Use `HdrAdapter` instead")]
pub type HDRAdapter<R> = HdrAdapter<R>;
impl<R: BufRead> HdrAdapter<R> {
    
    pub fn new(r: R) -> ImageResult<HdrAdapter<R>> {
        let decoder = HdrDecoder::new(r)?;
        let meta = decoder.metadata();
        Ok(HdrAdapter {
            inner: Some(decoder),
            meta,
        })
    }
    
    pub fn new_nonstrict(r: R) -> ImageResult<HdrAdapter<R>> {
        let decoder = HdrDecoder::with_strictness(r, false)?;
        let meta = decoder.metadata();
        Ok(HdrAdapter {
            inner: Some(decoder),
            meta,
        })
    }
    
    fn read_image_data(&mut self, buf: &mut [u8]) -> ImageResult<()> {
        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
        match self.inner.take() {
            Some(decoder) => {
                let img: Vec<Rgb<u8>> = decoder.read_image_ldr()?;
                for (i, Rgb(data)) in img.into_iter().enumerate() {
                    buf[(i*3)..][..3].copy_from_slice(&data);
                }
                Ok(())
            }
            None => Err(ImageError::Parameter(
                ParameterError::from_kind(ParameterErrorKind::NoMoreData)
            )),
        }
    }
}
pub struct HdrReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
impl<R> Read for HdrReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        if self.0.position() == 0 && buf.is_empty() {
            mem::swap(buf, self.0.get_mut());
            Ok(buf.len())
        } else {
            self.0.read_to_end(buf)
        }
    }
}
impl<'a, R: 'a + BufRead> ImageDecoder<'a> for HdrAdapter<R> {
    type Reader = HdrReader<R>;
    fn dimensions(&self) -> (u32, u32) {
        (self.meta.width, self.meta.height)
    }
    fn color_type(&self) -> ColorType {
        ColorType::Rgb8
    }
    fn into_reader(self) -> ImageResult<Self::Reader> {
        Ok(HdrReader(Cursor::new(image::decoder_to_vec(self)?), PhantomData))
    }
    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
        self.read_image_data(buf)
    }
}
impl<'a, R: 'a + BufRead + Seek> ImageDecoderExt<'a> for HdrAdapter<R> {
    fn read_rect_with_progress<F: Fn(Progress)>(
        &mut self,
        x: u32,
        y: u32,
        width: u32,
        height: u32,
        buf: &mut [u8],
        progress_callback: F,
    ) -> ImageResult<()> {
        image::load_rect(x, y, width, height, buf, progress_callback, self, |_, _| unreachable!(),
                         |s, buf| s.read_image_data(buf))
    }
}
pub const SIGNATURE: &[u8] = b"#?RADIANCE";
const SIGNATURE_LENGTH: usize = 10;
#[derive(Debug)]
pub struct HdrDecoder<R> {
    r: R,
    width: u32,
    height: u32,
    meta: HdrMetadata,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Rgbe8Pixel {
    
    pub c: [u8; 3],
    
    pub e: u8,
}
#[allow(dead_code)]
#[deprecated(note = "Use `Rgbe8Pixel` instead")]
pub type RGBE8Pixel = Rgbe8Pixel;
pub fn rgbe8(r: u8, g: u8, b: u8, e: u8) -> Rgbe8Pixel {
    Rgbe8Pixel { c: [r, g, b], e }
}
impl Rgbe8Pixel {
    
    #[inline]
    pub fn to_hdr(self) -> Rgb<f32> {
        if self.e == 0 {
            Rgb([0.0, 0.0, 0.0])
        } else {
            
            let exp = f32::exp2(<f32 as From<_>>::from(self.e) - (128.0 + 8.0));
            Rgb([
                exp * <f32 as From<_>>::from(self.c[0]),
                exp * <f32 as From<_>>::from(self.c[1]),
                exp * <f32 as From<_>>::from(self.c[2]),
            ])
        }
    }
    
    
    
    
    
    
    
    #[inline]
    pub fn to_ldr<T: Primitive + Zero>(self) -> Rgb<T> {
        self.to_ldr_scale_gamma(1.0, 2.2)
    }
    
    
    
    
    
    
    
    
    #[inline]
    pub fn to_ldr_scale_gamma<T: Primitive + Zero>(self, scale: f32, gamma: f32) -> Rgb<T> {
        let Rgb(data) = self.to_hdr();
        let (r, g, b) = (data[0], data[1], data[2]);
        #[inline]
        fn sg<T: Primitive + Zero>(v: f32, scale: f32, gamma: f32) -> T {
            let t_max = T::max_value();
            
            let t_max_f32: f32 = num_traits::NumCast::from(t_max)
                .expect("to_ldr_scale_gamma: maximum value of type is not representable as f32");
            let fv = f32::powf(v * scale, gamma) * t_max_f32 + 0.5;
            if fv < 0.0 {
                T::zero()
            } else if fv > t_max_f32 {
                t_max
            } else {
                num_traits::NumCast::from(fv)
                    .expect("to_ldr_scale_gamma: cannot convert f32 to target type. NaN?")
            }
        }
        Rgb([
            sg(r, scale, gamma),
            sg(g, scale, gamma),
            sg(b, scale, gamma),
        ])
    }
}
impl<R: BufRead> HdrDecoder<R> {
    
    
    
    pub fn new(reader: R) -> ImageResult<HdrDecoder<R>> {
        HdrDecoder::with_strictness(reader, true)
    }
    
    
    
    
    
    
    
    pub fn with_strictness(mut reader: R, strict: bool) -> ImageResult<HdrDecoder<R>> {
        let mut attributes = HdrMetadata::new();
        {
            
            let r = &mut reader;
            if strict {
                let mut signature = [0; SIGNATURE_LENGTH];
                r.read_exact(&mut signature)?;
                if signature != SIGNATURE {
                    return Err(DecoderError::RadianceHdrSignatureInvalid.into());
                } 
                  
                read_line_u8(r)?;
            } else {
                
                
            }
            
            loop {
                match read_line_u8(r)? {
                    None => {
                        
                        return Err(DecoderError::TruncatedHeader.into());
                    }
                    Some(line) => {
                        if line.is_empty() {
                            
                            break;
                        } else if line[0] == b'#' {
                            
                            
                            continue;
                        } 
                          
                        let line = String::from_utf8_lossy(&line[..]);
                        attributes.update_header_info(&line, strict)?;
                    } 
                } 
            } 
        } 
          
        let (width, height) = match read_line_u8(&mut reader)? {
            None => {
                
                return Err(DecoderError::TruncatedDimensions.into());
            }
            Some(dimensions) => {
                let dimensions = String::from_utf8_lossy(&dimensions[..]);
                parse_dimensions_line(&dimensions, strict)?
            }
        };
        Ok(HdrDecoder {
            r: reader,
            width,
            height,
            meta: HdrMetadata {
                width,
                height,
                ..attributes
            },
        })
    } 
    
    pub fn metadata(&self) -> HdrMetadata {
        self.meta.clone()
    }
    
    pub fn read_image_native(mut self) -> ImageResult<Vec<Rgbe8Pixel>> {
        
        if self.width == 0 || self.height == 0 {
            return Ok(vec![]);
        }
        
        let pixel_count = self.width as usize * self.height as usize;
        let mut ret = vec![Default::default(); pixel_count];
        for chunk in ret.chunks_mut(self.width as usize) {
            read_scanline(&mut self.r, chunk)?;
        }
        Ok(ret)
    }
    
    pub fn read_image_transform<T: Send, F: Send + Sync + Fn(Rgbe8Pixel) -> T>(
        mut self,
        f: F,
        output_slice: &mut [T],
    ) -> ImageResult<()> {
        assert_eq!(
            output_slice.len(),
            self.width as usize * self.height as usize
        );
        
        if self.width == 0 || self.height == 0 {
            return Ok(());
        }
        let chunks_iter = output_slice.chunks_mut(self.width as usize);
        let mut pool = Pool::new(8); 
        (pool.scoped(|scope| {
            for chunk in chunks_iter {
                let mut buf = vec![Default::default(); self.width as usize];
                read_scanline(&mut self.r, &mut buf[..])?;
                let f = &f;
                scope.execute(move || {
                    for (dst, &pix) in chunk.iter_mut().zip(buf.iter()) {
                        *dst = f(pix);
                    }
                });
            }
            Ok(())
        }) as Result<(), ImageError>)?;
        Ok(())
    }
    
    
    pub fn read_image_ldr(self) -> ImageResult<Vec<Rgb<u8>>> {
        let mut ret = vec![Rgb([0, 0, 0]); self.width as usize * self.height as usize];
        self.read_image_transform(|pix| pix.to_ldr(), &mut ret[..])?;
        Ok(ret)
    }
    
    
    pub fn read_image_hdr(self) -> ImageResult<Vec<Rgb<f32>>> {
        let mut ret = vec![Rgb([0.0, 0.0, 0.0]); self.width as usize * self.height as usize];
        self.read_image_transform(|pix| pix.to_hdr(), &mut ret[..])?;
        Ok(ret)
    }
}
impl<R: BufRead> IntoIterator for HdrDecoder<R> {
    type Item = ImageResult<Rgbe8Pixel>;
    type IntoIter = HdrImageDecoderIterator<R>;
    fn into_iter(self) -> Self::IntoIter {
        HdrImageDecoderIterator {
            r: self.r,
            scanline_cnt: self.height as usize,
            buf: vec![Default::default(); self.width as usize],
            col: 0,
            scanline: 0,
            trouble: true, 
            error_encountered: false,
        }
    }
}
pub struct HdrImageDecoderIterator<R: BufRead> {
    r: R,
    scanline_cnt: usize,
    buf: Vec<Rgbe8Pixel>, 
    col: usize,           
    scanline: usize,      
    trouble: bool,        
    error_encountered: bool,
}
#[allow(dead_code)]
#[deprecated(note = "Use `HdrImageDecoderIterator` instead")]
pub type HDRImageDecoderIterator<R> = HdrImageDecoderIterator<R>;
impl<R: BufRead> HdrImageDecoderIterator<R> {
    
    #[inline]
    fn advance(&mut self) {
        self.col += 1;
        if self.col == self.buf.len() {
            self.col = 0;
            self.scanline += 1;
            self.trouble = true;
        }
    }
}
impl<R: BufRead> Iterator for HdrImageDecoderIterator<R> {
    type Item = ImageResult<Rgbe8Pixel>;
    fn next(&mut self) -> Option<Self::Item> {
        if !self.trouble {
            let ret = self.buf[self.col];
            self.advance();
            Some(Ok(ret))
        } else {
            
            if self.buf.is_empty() || self.scanline == self.scanline_cnt {
                
                return None;
            } 
            if self.error_encountered {
                self.advance();
                
                
                return Some(Err(ImageError::Parameter(
                    ParameterError::from_kind(ParameterErrorKind::FailedAlready)
                )));
            } 
            if self.col == 0 {
                
                match read_scanline(&mut self.r, &mut self.buf[..]) {
                    Ok(_) => {
                        
                    }
                    Err(err) => {
                        self.advance();
                        self.error_encountered = true;
                        self.trouble = true;
                        return Some(Err(err));
                    }
                }
            } 
            self.trouble = false;
            let ret = self.buf[0];
            self.advance();
            Some(Ok(ret))
        }
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
        let total_cnt = self.buf.len() * self.scanline_cnt;
        let cur_cnt = self.buf.len() * self.scanline + self.col;
        let remaining = total_cnt - cur_cnt;
        (remaining, Some(remaining))
    }
}
impl<R: BufRead> ExactSizeIterator for HdrImageDecoderIterator<R> {}
fn read_scanline<R: BufRead>(r: &mut R, buf: &mut [Rgbe8Pixel]) -> ImageResult<()> {
    assert!(!buf.is_empty());
    let width = buf.len();
    
    let fb = read_rgbe(r)?;
    if fb.c[0] == 2 && fb.c[1] == 2 && fb.c[2] < 128 {
        
        
        
        decode_component(r, width, |offset, value| buf[offset].c[0] = value)?;
        decode_component(r, width, |offset, value| buf[offset].c[1] = value)?;
        decode_component(r, width, |offset, value| buf[offset].c[2] = value)?;
        decode_component(r, width, |offset, value| buf[offset].e = value)?;
    } else {
        
        decode_old_rle(r, fb, buf)?;
    }
    Ok(())
}
#[inline(always)]
fn read_byte<R: BufRead>(r: &mut R) -> io::Result<u8> {
    let mut buf = [0u8];
    r.read_exact(&mut buf[..])?;
    Ok(buf[0])
}
#[inline]
fn decode_component<R: BufRead, S: FnMut(usize, u8)>(
    r: &mut R,
    width: usize,
    mut set_component: S,
) -> ImageResult<()> {
    let mut buf = [0; 128];
    let mut pos = 0;
    while pos < width {
        
        pos += {
            let rl = read_byte(r)?;
            if rl <= 128 {
                
                if pos + rl as usize > width {
                    return Err(DecoderError::WrongScanlineLength(pos + rl as usize, width).into());
                }
                
                r.read_exact(&mut buf[0..rl as usize])?;
                for (offset, &value) in buf[0..rl as usize].iter().enumerate() {
                    set_component(pos + offset, value);
                }
                rl as usize
            } else {
                
                let rl = rl - 128;
                
                if pos + rl as usize > width {
                    return Err(DecoderError::WrongScanlineLength(pos + rl as usize, width).into());
                }
                
                let value = read_byte(r)?;
                for offset in 0..rl as usize {
                    set_component(pos + offset, value);
                }
                rl as usize
            }
        };
    }
    if pos != width {
        return Err(DecoderError::WrongScanlineLength(pos, width).into());
    }
    Ok(())
}
fn decode_old_rle<R: BufRead>(
    r: &mut R,
    fb: Rgbe8Pixel,
    buf: &mut [Rgbe8Pixel],
) -> ImageResult<()> {
    assert!(!buf.is_empty());
    let width = buf.len();
    
    
    #[inline]
    fn rl_marker(pix: Rgbe8Pixel) -> Option<usize> {
        if pix.c == [1, 1, 1] {
            Some(pix.e as usize)
        } else {
            None
        }
    }
    
    
    if rl_marker(fb).is_some() {
        return Err(DecoderError::FirstPixelRlMarker.into());
    }
    buf[0] = fb; 
    let mut x_off = 1; 
    let mut rl_mult = 1; 
    let mut prev_pixel = fb;
    while x_off < width {
        let pix = read_rgbe(r)?;
        
        x_off += {
            if let Some(rl) = rl_marker(pix) {
                
                let rl = rl * rl_mult;
                rl_mult *= 256;
                if x_off + rl <= width {
                    
                    for b in &mut buf[x_off..x_off + rl] {
                        *b = prev_pixel;
                    }
                } else {
                    return Err(DecoderError::WrongScanlineLength(x_off + rl, width).into());
                };
                rl 
            } else {
                rl_mult = 1; 
                prev_pixel = pix;
                buf[x_off] = pix;
                1 
            }
        };
    }
    if x_off != width {
        return Err(DecoderError::WrongScanlineLength(x_off, width).into());
    }
    Ok(())
}
fn read_rgbe<R: BufRead>(r: &mut R) -> io::Result<Rgbe8Pixel> {
    let mut buf = [0u8; 4];
    r.read_exact(&mut buf[..])?;
    Ok(Rgbe8Pixel {
        c: [buf[0], buf[1], buf[2]],
        e: buf[3],
    })
}
#[derive(Debug, Clone)]
pub struct HdrMetadata {
    
    
    pub width: u32,
    
    pub height: u32,
    
    
    
    pub orientation: ((i8, i8), (i8, i8)),
    
    
    
    
    pub exposure: Option<f32>,
    
    
    
    
    pub color_correction: Option<(f32, f32, f32)>,
    
    pub pixel_aspect_ratio: Option<f32>,
    
    
    
    pub custom_attributes: Vec<(String, String)>,
}
#[allow(dead_code)]
#[deprecated(note = "Use `HdrMetadata` instead")]
pub type HDRMetadata = HdrMetadata;
impl HdrMetadata {
    fn new() -> HdrMetadata {
        HdrMetadata {
            width: 0,
            height: 0,
            orientation: ((1, 0), (0, 1)),
            exposure: None,
            color_correction: None,
            pixel_aspect_ratio: None,
            custom_attributes: vec![],
        }
    }
    
    
    fn update_header_info(&mut self, line: &str, strict: bool) -> ImageResult<()> {
        
        
        let maybe_key_value = split_at_first(line, "=").map(|(key, value)| (key.trim(), value));
        
        match maybe_key_value {
            Some((key, val)) => self
                .custom_attributes
                .push((key.to_owned(), val.to_owned())),
            None => self.custom_attributes.push(("".into(), line.to_owned())),
        }
        
        match maybe_key_value {
            Some(("FORMAT", val)) => {
                if val.trim() != "32-bit_rle_rgbe" {
                    
                    return Err(ImageError::Unsupported(UnsupportedError::from_format_and_kind(
                        ImageFormat::Hdr.into(),
                        UnsupportedErrorKind::Format(ImageFormatHint::Name(limit_string_len(val, 20)))
                    )));
                }
            }
            Some(("EXPOSURE", val)) => {
                match val.trim().parse::<f32>() {
                    Ok(v) => {
                        self.exposure = Some(self.exposure.unwrap_or(1.0) * v); 
                    }
                    Err(parse_error) => {
                        if strict {
                            return Err(DecoderError::UnparsableF32(LineType::Exposure, parse_error).into());
                        } 
                    }
                };
            }
            Some(("PIXASPECT", val)) => {
                match val.trim().parse::<f32>() {
                    Ok(v) => {
                        self.pixel_aspect_ratio = Some(self.pixel_aspect_ratio.unwrap_or(1.0) * v);
                        
                    }
                    Err(parse_error) => {
                        if strict {
                            return Err(DecoderError::UnparsableF32(LineType::Pixaspect, parse_error).into());
                        } 
                    }
                };
            }
            Some(("COLORCORR", val)) => {
                let mut rgbcorr = [1.0, 1.0, 1.0];
                match parse_space_separated_f32(val, &mut rgbcorr, LineType::Colorcorr) {
                    Ok(extra_numbers) => {
                        if strict && extra_numbers {
                            return Err(DecoderError::ExtraneousColorcorrNumbers.into());
                        } 
                        let (rc, gc, bc) = self.color_correction.unwrap_or((1.0, 1.0, 1.0));
                        self.color_correction =
                            Some((rc * rgbcorr[0], gc * rgbcorr[1], bc * rgbcorr[2]));
                    }
                    Err(err) => {
                        if strict {
                            return Err(err);
                        } 
                    }
                }
            }
            None => {
                
                
            }
            _ => {
                
            }
        } 
        Ok(())
    }
}
fn parse_space_separated_f32(line: &str, vals: &mut [f32], line_tp: LineType) -> ImageResult<bool> {
    let mut nums = line.split_whitespace();
    for val in vals.iter_mut() {
        if let Some(num) = nums.next() {
            match num.parse::<f32>() {
                Ok(v) => *val = v,
                Err(err) => return Err(DecoderError::UnparsableF32(line_tp, err).into()),
            }
        } else {
            
            return Err(DecoderError::LineTooShort(line_tp).into());
        }
    }
    Ok(nums.next().is_some())
}
fn parse_dimensions_line(line: &str, strict: bool) -> ImageResult<(u32, u32)> {
    const DIMENSIONS_COUNT: usize = 4;
    let mut dim_parts = line.split_whitespace();
    let c1_tag = dim_parts.next().ok_or(DecoderError::DimensionsLineTooShort(0, DIMENSIONS_COUNT))?;
    let c1_str = dim_parts.next().ok_or(DecoderError::DimensionsLineTooShort(1, DIMENSIONS_COUNT))?;
    let c2_tag = dim_parts.next().ok_or(DecoderError::DimensionsLineTooShort(2, DIMENSIONS_COUNT))?;
    let c2_str = dim_parts.next().ok_or(DecoderError::DimensionsLineTooShort(3, DIMENSIONS_COUNT))?;
    if strict && dim_parts.next().is_some() {
        
        return Err(DecoderError::DimensionsLineTooLong(DIMENSIONS_COUNT).into());
    } 
      
      
    match (c1_tag, c2_tag) {
        ("-Y", "+X") => {
            
            
            let height = c1_str.parse::<u32>().map_err(|pe| DecoderError::UnparsableU32(LineType::DimensionsHeight, pe))?;
            let width = c2_str.parse::<u32>().map_err(|pe| DecoderError::UnparsableU32(LineType::DimensionsWidth, pe))?;
            Ok((width, height))
        }
        _ => Err(ImageError::Unsupported(UnsupportedError::from_format_and_kind(
            ImageFormat::Hdr.into(),
            UnsupportedErrorKind::GenericFeature(format!(
                "Orientation {} {}",
                limit_string_len(c1_tag, 4),
                limit_string_len(c2_tag, 4)
            )),
        ))),
    } 
}
fn limit_string_len(s: &str, len: usize) -> String {
    let s_char_len = s.chars().count();
    if s_char_len > len {
        s.chars().take(len).chain("...".chars()).collect()
    } else {
        s.into()
    }
}
fn split_at_first<'a>(s: &'a str, separator: &str) -> Option<(&'a str, &'a str)> {
    match s.find(separator) {
        None | Some(0) => None,
        Some(p) if p >= s.len() - separator.len() => None,
        Some(p) => Some((&s[..p], &s[(p + separator.len())..])),
    }
}
#[test]
fn split_at_first_test() {
    assert_eq!(split_at_first(&Cow::Owned("".into()), "="), None);
    assert_eq!(split_at_first(&Cow::Owned("=".into()), "="), None);
    assert_eq!(split_at_first(&Cow::Owned("= ".into()), "="), None);
    assert_eq!(
        split_at_first(&Cow::Owned(" = ".into()), "="),
        Some((" ", " "))
    );
    assert_eq!(
        split_at_first(&Cow::Owned("EXPOSURE= ".into()), "="),
        Some(("EXPOSURE", " "))
    );
    assert_eq!(
        split_at_first(&Cow::Owned("EXPOSURE= =".into()), "="),
        Some(("EXPOSURE", " ="))
    );
    assert_eq!(
        split_at_first(&Cow::Owned("EXPOSURE== =".into()), "=="),
        Some(("EXPOSURE", " ="))
    );
    assert_eq!(split_at_first(&Cow::Owned("EXPOSURE".into()), ""), None);
}
fn read_line_u8<R: BufRead>(r: &mut R) -> ::std::io::Result<Option<Vec<u8>>> {
    let mut ret = Vec::with_capacity(16);
    match r.read_until(b'\n', &mut ret) {
        Ok(0) => Ok(None),
        Ok(_) => {
            if let Some(&b'\n') = ret[..].last() {
                let _ = ret.pop();
            }
            Ok(Some(ret))
        }
        Err(err) => Err(err),
    }
}
#[test]
fn read_line_u8_test() {
    let buf: Vec<_> = (&b"One\nTwo\nThree\nFour\n\n\n"[..]).into();
    let input = &mut ::std::io::Cursor::new(buf);
    assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"One"[..]);
    assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"Two"[..]);
    assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"Three"[..]);
    assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"Four"[..]);
    assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b""[..]);
    assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b""[..]);
    assert_eq!(read_line_u8(input).unwrap(), None);
}
pub fn read_raw_file<P: AsRef<Path>>(path: P) -> ::std::io::Result<Vec<Rgb<f32>>> {
    use byteorder::{LittleEndian as LE, ReadBytesExt};
    use std::fs::File;
    use std::io::BufReader;
    let mut r = BufReader::new(File::open(path)?);
    let w = r.read_u32::<LE>()? as usize;
    let h = r.read_u32::<LE>()? as usize;
    let c = r.read_u32::<LE>()? as usize;
    assert_eq!(c, 3);
    let cnt = w * h;
    let mut ret = Vec::with_capacity(cnt);
    for _ in 0..cnt {
        let cr = r.read_f32::<LE>()?;
        let cg = r.read_f32::<LE>()?;
        let cb = r.read_f32::<LE>()?;
        ret.push(Rgb([cr, cg, cb]));
    }
    Ok(ret)
}