1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//! Generic Buffered Reader, for efficient record iteration,
//! with powerful internal Hatanaka / Gz decompression.
use std::io::{BufReader}; // Seek, SeekFrom};
use crate::hatanaka::Decompressor;
#[cfg(feature = "with-gzip")]
use flate2::read::GzDecoder;


#[derive(Debug)]
pub enum ReaderWrapper {
    /// Readable `RINEX`
    PlainFile(BufReader<std::fs::File>),
    /// gzip compressed RINEX
    #[cfg(feature = "with-gzip")]
    GzFile(BufReader<GzDecoder<std::fs::File>>),
}

pub struct BufferedReader {
    /// Internal reader,
    /// supports Plain RINEX, CRINEX, .gz
    reader: ReaderWrapper,
    /// Internal struct in case of CRINEX decompression 
    decompressor: Option<Decompressor>,
}

impl BufferedReader {
    /// Builds a new BufferedReader for efficient file interation,
    /// with possible .gz and .gz + hatanaka decompression
    pub fn new (path: &str) -> std::io::Result<Self> {
        let f = std::fs::File::open(path)?;
        if path.ends_with(".gz") {
            // --> gzip encoded
            #[cfg(feature = "with-gzip")] {
                // .gz
                // example : i.gz, .n.gz, .crx.gz 
                Ok(Self {
                    reader: ReaderWrapper::GzFile(BufReader::new(GzDecoder::new(f))),
                    decompressor: None,
                })
            }
            #[cfg(not(feature = "with-gzip"))] {
                panic!("gzip compressed data require the --with-gzip build feature")
            }
        
        } else if path.ends_with(".Z") {
            panic!(".z compressed files not supported yet, uncompress manually")
        
        } else { // Assumes no extra compression
            Ok(Self {
                reader: ReaderWrapper::PlainFile(BufReader::new(f)),
                decompressor: None,
            })
        }
    }
    /// Enhances self for hatanaka internal decompression,
    /// preserves inner pointer state
    pub fn with_hatanaka (&self, m: usize) -> std::io::Result<Self> {
        match &self.reader {
            ReaderWrapper::PlainFile(bufreader) => {
                let inner = bufreader.get_ref();
                let fd = inner.try_clone()?; // preserves pointer
                Ok(BufferedReader {
                    reader: ReaderWrapper::PlainFile(BufReader::new(fd)),
                    decompressor: Some(Decompressor::new(m)),
                })
            },
            #[cfg(feature = "with-gzip")]
            ReaderWrapper::GzFile(bufreader) => {
                let inner = bufreader.get_ref().get_ref();
                let fd = inner.try_clone()?; // preserves pointer
                Ok(BufferedReader {
                    reader: ReaderWrapper::GzFile(BufReader::new(GzDecoder::new(fd))),
                    decompressor: Some(Decompressor::new(m)),
                })
            },
        }
    }
/*
    /// Modifies inner file pointer position
    pub fn seek (&mut self, pos: SeekFrom) -> Result<u64, std::io::Error> {
        match self.reader {
            ReaderWrapper::PlainFile(ref mut bufreader) => bufreader.seek(pos),
            #[cfg(feature = "with-gzip")]
            ReaderWrapper::GzFile(ref mut bufreader) => bufreader.seek(pos),
        }
    }
    /// rewind filer inner pointer, to offset = 0 
    pub fn rewind (&mut self) -> Result<(), std::io::Error> {
        match self.reader {
            ReaderWrapper::PlainFile(ref mut bufreader) => bufreader.rewind(),
            #[cfg(feature = "with-gzip")]
            ReaderWrapper::GzFile(ref mut bufreader) => bufreader.rewind(),
        }
    }
*/
}

impl std::io::Read for BufferedReader {
    fn read (&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> { 
        match self.reader {
            ReaderWrapper::PlainFile(ref mut h) => h.read(buf),
            #[cfg(feature = "with-gzip")]
            ReaderWrapper::GzFile(ref mut h) => h.read(buf),
        }
    }
}

impl std::io::BufRead for BufferedReader {
    fn fill_buf (&mut self) -> Result<&[u8], std::io::Error> { 
        match self.reader {
            ReaderWrapper::PlainFile(ref mut bufreader) => bufreader.fill_buf(),
            #[cfg(feature = "with-gzip")]
            ReaderWrapper::GzFile(ref mut bufreader) => bufreader.fill_buf(),
        }
    }
    
    fn consume (&mut self, s: usize) { 
        match self.reader {
            ReaderWrapper::PlainFile(ref mut bufreader) => bufreader.consume(s),
            #[cfg(feature = "with-gzip")]
            ReaderWrapper::GzFile(ref mut bufreader) => bufreader.consume(s),
        }
    }
}