use crate::debug_log::{LogLevel, LogState, log};
use std::fmt::Write;
use std::io::{self, Read, Write as IOWrite};
use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream};
use std::str::FromStr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
use std::thread;
use std::time::{Duration, Instant};

const SLEEP_DURATION: Duration = Duration::from_millis(100);
const POLL_DURATION: Duration = Duration::from_secs(5);
const BUF_SIZE: usize = 1024 * 4;
const CONNECT_TIMEOUT: Duration = Duration::from_secs(5);
const RESTART_ZERO_BYTES_COUNT: u32 = 30;
const PRE_RESTART_WAIT: Duration = Duration::from_secs(2);

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
enum PollState {
    None,
    Password,
    CurrentSong,
    Status,
    ReadPicture,
    ReadPictureInDir,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MPDPlayState {
    Playing,
    Paused,
    Stopped,
}

#[derive(Debug, Clone)]
pub struct InfoFromShared {
    pub filename: String,
    pub title: String,
    pub artist: String,
    pub album: String,
    pub length: f64,
    pub pos: f64,
    pub error_text: String,
    pub mpd_play_state: MPDPlayState,
}

#[derive(Clone)]
pub struct MPDHandler {
    state: Arc<RwLock<MPDHandlerState>>,
}

type SelfThreadT = Option<Arc<Mutex<thread::JoinHandle<Result<(), String>>>>>;

pub struct MPDHandlerState {
    art_data: Vec<u8>,
    art_data_size: usize,
    art_data_type: String,
    current_song_filename: String,
    current_song_title: String,
    current_song_artist: String,
    current_song_album: String,
    current_song_length: f64,
    current_song_position: f64,
    current_binary_size: usize,
    poll_state: PollState,
    stream: TcpStream,
    password: String,
    error_text: String,
    can_authenticate: bool,
    is_authenticated: bool,
    can_get_album_art: bool,
    can_get_album_art_in_dir: bool,
    can_get_status: bool,
    is_init: bool,
    did_check_overtime: bool,
    force_get_status: bool,
    force_get_current_song: bool,
    song_title_get_time: Instant,
    song_pos_get_time: Instant,
    song_length_get_time: Instant,
    self_thread: SelfThreadT,
    dirty_flag: Arc<AtomicBool>,
    pub stop_flag: Arc<AtomicBool>,
    log_level: LogLevel,
    mpd_play_state: MPDPlayState,
    recv_zero_bytes_count: u32,
}

fn check_next_chars(
    buf: &[u8],
    idx: usize,
    saved: &mut Vec<u8>,
) -> Result<(char, u8), (String, u8)> {
    if idx >= buf.len() {
        return Err((String::from("idx out of bounds"), 0u8));
    }
    if buf[idx] & 0b10000000 == 0 {
        let result_str = String::from_utf8(vec![buf[idx]]);
        if let Ok(mut s) = result_str {
            let popped_char = s.pop();
            if s.is_empty() {
                Ok((popped_char.unwrap(), 1u8))
            } else {
                Err((String::from("Not one-byte UTF-8 char"), 0u8))
            }
        } else {
            Err((String::from("Not one-byte UTF-8 char"), 0u8))
        }
    } else if buf[idx] & 0b11100000 == 0b11000000 {
        if idx + 1 >= buf.len() {
            saved.push(buf[idx]);
            return Err((
                String::from("Is two-byte UTF-8, but not enough bytes provided"),
                1u8,
            ));
        }
        let result_str = String::from_utf8(vec![buf[idx], buf[idx + 1]]);
        if let Ok(mut s) = result_str {
            let popped_char = s.pop();
            if s.is_empty() {
                Ok((popped_char.unwrap(), 2u8))
            } else {
                Err((String::from("Not two-byte UTF-8 char"), 0u8))
            }
        } else {
            Err((String::from("Not two-byte UTF-8 char"), 0u8))
        }
    } else if buf[idx] & 0b11110000 == 0b11100000 {
        if idx + 2 >= buf.len() {
            for c in buf.iter().skip(idx) {
                saved.push(*c);
            }
            return Err((
                String::from("Is three-byte UTF-8, but not enough bytes provided"),
                (idx + 3 - buf.len()) as u8,
            ));
        }
        let result_str = String::from_utf8(vec![buf[idx], buf[idx + 1], buf[idx + 2]]);
        if let Ok(mut s) = result_str {
            let popped_char = s.pop();
            if s.is_empty() {
                Ok((popped_char.unwrap(), 3u8))
            } else {
                Err((String::from("Not three-byte UTF-8 char"), 0u8))
            }
        } else {
            Err((String::from("Not three-byte UTF-8 char"), 0u8))
        }
    } else if buf[idx] & 0b11111000 == 0b11110000 {
        if idx + 3 >= buf.len() {
            for c in buf.iter().skip(idx) {
                saved.push(*c);
            }
            return Err((
                String::from("Is four-byte UTF-8, but not enough bytes provided"),
                (idx + 4 - buf.len()) as u8,
            ));
        }
        let result_str = String::from_utf8(vec![buf[idx], buf[idx + 1], buf[idx + 2]]);
        if let Ok(mut s) = result_str {
            let popped_char = s.pop();
            if s.is_empty() {
                Ok((popped_char.unwrap(), 4u8))
            } else {
                Err((String::from("Not four-byte UTF-8 char"), 0u8))
            }
        } else {
            Err((String::from("Not four-byte UTF-8 char"), 0u8))
        }
    } else {
        Err((String::from("Invalid UTF-8 char"), 0u8))
    }
}

fn read_line(
    buf: &mut Vec<u8>,
    saved: &mut Vec<u8>,
    init: bool,
) -> Result<String, (String, String)> {
    let count = buf.len();
    let mut result = String::new();

    if count == 0 {
        return Err((
            String::from("Empty string passed to read_line"),
            String::new(),
        ));
    }

    let mut buf_to_read: Vec<u8> = Vec::with_capacity(saved.len() + buf.len());

    if !saved.is_empty() {
        buf_to_read.append(saved);
    }
    buf_to_read.append(buf);

    let mut prev_three: Vec<char> = Vec::with_capacity(4);

    let mut skip_count = 0;
    for idx in 0..count {
        if skip_count > 0 {
            skip_count -= 1;
            continue;
        }
        let next_char_result = check_next_chars(&buf_to_read, idx, saved);
        if let Ok((c, s)) = next_char_result {
            if !init {
                prev_three.push(c);
                if prev_three.len() > 3 {
                    prev_three.remove(0);
                }
                if ['O', 'K', '\n'] == prev_three.as_slice() && idx + 1 == count {
                    buf_to_read = buf_to_read.split_off(2);
                    result = String::from("OK");
                    buf.append(&mut buf_to_read);
                    //println!("Warning: OK was reached"); // DEBUG
                    return Ok(result);
                }
            }
            if c == '\n' {
                buf_to_read = buf_to_read.split_off(idx + s as usize);
                buf.append(&mut buf_to_read);
                return Ok(result);
            }
            result.push(c);
            skip_count = s - 1;
        } else if let Err((msg, count)) = next_char_result {
            //println!("Error: {}", msg); // DEBUG
            for i in 0..count {
                saved.push(buf_to_read[idx + i as usize]);
            }
            buf_to_read = buf_to_read.split_off(idx);
            buf.append(&mut buf_to_read);
            return Err((msg, result));
        } else {
            unreachable!();
        }
    }

    *saved = buf_to_read;
    Err((String::from("Newline not reached"), result))
}

fn restart_stream(
    state_handle: &mut RwLockWriteGuard<'_, MPDHandlerState>,
    log_level: LogLevel,
) -> Result<(), String> {
    thread::sleep(PRE_RESTART_WAIT);

    let fn_impl = |state_handle: &mut RwLockWriteGuard<'_, MPDHandlerState>| -> Result<(), String> {
        let peer = state_handle
            .stream
            .peer_addr()
            .map_err(|_| String::from("Failed to get TCP stream peer addr/port"))?;
        state_handle
            .stream
            .shutdown(std::net::Shutdown::Both)
            .map_err(|_| String::from("Failed to cleanup TCP stream"))?;
        state_handle.stream = TcpStream::connect_timeout(&peer, CONNECT_TIMEOUT)
            .map_err(|_| String::from("Failed to reconnect"))?;
        state_handle
            .stream
            .set_nonblocking(true)
            .map_err(|_| String::from("Failed to set non-blocking on restarted TCP stream"))?;
        Ok(())
    };

    let result = fn_impl(state_handle);
    if let Err(e) = result {
        log("Failed to reconnect.", LogState::Error, LogLevel::Error);
        state_handle.stop_flag.store(true, Ordering::Release);
        Err(e)
    } else {
        log("Connection restarted.", LogState::Warning, log_level);
        state_handle.is_authenticated = false;
        state_handle.can_authenticate = true;
        state_handle.recv_zero_bytes_count = 0;
        Ok(())
    }
}

impl MPDHandler {
    pub fn new(
        host: Ipv4Addr,
        port: u16,
        password: String,
        log_level: LogLevel,
    ) -> Result<Self, String> {
        let stream =
            TcpStream::connect_timeout(&SocketAddr::new(IpAddr::V4(host), port), CONNECT_TIMEOUT)
                .map_err(|_| String::from("Failed to get TCP connection (is MPD running?)"))?;

        let password_is_empty = password.is_empty();

        let s = MPDHandler {
            state: Arc::new(RwLock::new(MPDHandlerState {
                art_data: Vec::new(),
                art_data_size: 0,
                art_data_type: String::new(),
                current_song_filename: String::new(),
                current_song_title: String::new(),
                current_song_artist: String::new(),
                current_song_length: 0.0,
                current_song_position: 0.0,
                current_binary_size: 0,
                poll_state: PollState::None,
                stream,
                password,
                error_text: String::new(),
                can_authenticate: true,
                is_authenticated: password_is_empty,
                can_get_album_art: true,
                can_get_album_art_in_dir: true,
                can_get_status: true,
                is_init: true,
                did_check_overtime: false,
                force_get_status: false,
                force_get_current_song: false,
                song_title_get_time: Instant::now().checked_sub(Duration::from_secs(10)).unwrap(),
                song_pos_get_time: Instant::now().checked_sub(Duration::from_secs(10)).unwrap(),
                song_length_get_time: Instant::now().checked_sub(Duration::from_secs(10)).unwrap(),
                self_thread: None,
                dirty_flag: Arc::new(AtomicBool::new(true)),
                stop_flag: Arc::new(AtomicBool::new(false)),
                log_level,
                mpd_play_state: MPDPlayState::Stopped,
                current_song_album: String::new(),
                recv_zero_bytes_count: 0,
            })),
        };

        let s_clone = s.clone();
        let thread = Arc::new(Mutex::new(thread::spawn(|| s_clone.handler_loop())));

        loop {
            if let Ok(mut write_handle) = s.state.try_write() {
                write_handle.self_thread = Some(thread);
                break;
            } else {
                thread::sleep(Duration::from_millis(1));
            }
        }

        Ok(s)
    }

    pub fn get_mpd_handler_shared_state(&self) -> Result<InfoFromShared, ()> {
        if let Ok(read_lock) = self.state.try_read() {
            return Ok(InfoFromShared {
                filename: read_lock.current_song_filename.clone(),
                title: read_lock.current_song_title.clone(),
                artist: read_lock.current_song_artist.clone(),
                album: read_lock.current_song_album.clone(),
                length: read_lock.current_song_length,
                pos: read_lock.current_song_position
                    + read_lock.song_pos_get_time.elapsed().as_secs_f64(),
                error_text: read_lock.error_text.clone(),
                mpd_play_state: read_lock.mpd_play_state,
            });
        }

        Err(())
    }

    pub fn get_dirty_flag(&self) -> Result<Arc<AtomicBool>, ()> {
        if let Ok(read_lock) = self.state.try_read() {
            return Ok(read_lock.dirty_flag.clone());
        }

        Err(())
    }

    #[allow(dead_code)]
    pub fn is_dirty(&self) -> Result<bool, ()> {
        if let Ok(write_lock) = self.state.try_write() {
            return Ok(write_lock.dirty_flag.swap(false, Ordering::AcqRel));
        }

        Err(())
    }

    #[allow(dead_code)]
    pub fn force_get_current_song(&self) {
        loop {
            if let Ok(mut write_lock) = self.state.try_write() {
                write_lock.force_get_current_song = true;
                break;
            } else {
                thread::sleep(Duration::from_millis(10));
            }
        }
    }

    pub fn is_authenticated(&self) -> Result<bool, ()> {
        let read_handle = self.state.try_read().map_err(|_| ())?;
        Ok(read_handle.is_authenticated)
    }

    pub fn failed_to_authenticate(&self) -> Result<bool, ()> {
        let read_handle = self.state.try_read().map_err(|_| ())?;
        Ok(!read_handle.can_authenticate)
    }

    #[allow(dead_code)]
    pub fn has_image_data(&self) -> Result<bool, ()> {
        let read_handle = self.state.try_read().map_err(|_| ())?;
        Ok(read_handle.is_art_data_ready())
    }

    pub fn get_state_read_guard(&self) -> Result<RwLockReadGuard<'_, MPDHandlerState>, ()> {
        self.state.try_read().map_err(|_| ())
    }

    pub fn stop_thread(&self) -> Result<(), ()> {
        let read_handle = self.state.try_read().map_err(|_| ())?;
        read_handle.stop_flag.store(true, Ordering::Release);
        Ok(())
    }

    pub fn force_try_other_album_art(&self) -> Result<(), ()> {
        let mut write_handle = self.state.try_write().map_err(|_| ())?;
        write_handle.art_data.clear();
        write_handle.art_data_size = 0;
        write_handle.can_get_album_art = false;
        write_handle.can_get_album_art_in_dir = true;
        Ok(())
    }

    fn handler_loop(self) -> Result<(), String> {
        let log_level = self
            .state
            .read()
            .expect("Failed to get log_level")
            .log_level;
        let mut buf: [u8; BUF_SIZE] = [0; BUF_SIZE];
        let mut saved: Vec<u8> = Vec::new();
        let mut saved_str: String = String::new();

        loop {
            if let Ok(write_handle) = self.state.try_write() {
                write_handle
                    .stream
                    .set_nonblocking(true)
                    .map_err(|_| String::from("Failed to set non-blocking on TCP stream"))?;
                break;
            } else {
                thread::sleep(POLL_DURATION);
            }
        }

        'main: loop {
            if !self.is_reading_picture()
                && self.is_authenticated().unwrap_or(true)
                && !self.failed_to_authenticate().unwrap_or(false)
            {
                thread::sleep(SLEEP_DURATION);
                if let Ok(write_handle) = self.state.try_write()
                    && write_handle.self_thread.is_none()
                {
                    // main thread failed to store handle to this thread
                    log(
                        "MPDHandle thread stopping due to failed handle storage",
                        LogState::Error,
                        write_handle.log_level,
                    );
                    break 'main;
                }
            }

            if let Err(err_string) = self.handler_read_block(&mut buf, &mut saved, &mut saved_str) {
                log(
                    format!("read_block error: {err_string}"),
                    LogState::Warning,
                    log_level,
                );
            } else if let Err(err_string) = self.handler_write_block() {
                log(
                    format!("write_block error: {err_string}"),
                    LogState::Warning,
                    log_level,
                );
            }

            if let Ok(read_handle) = self.state.try_read()
                && (read_handle.stop_flag.load(Ordering::Acquire) || !read_handle.can_authenticate)
            {
                break 'main;
            }

            io::stdout().flush().unwrap();
        }

        log(
            "MPDHandler thread entering exit loop",
            LogState::Debug,
            log_level,
        );
        'exit: loop {
            if let Ok(mut write_handle) = self.state.try_write() {
                write_handle.self_thread = None;
                break 'exit;
            }
            thread::sleep(SLEEP_DURATION);
        }

        Ok(())
    }

    fn handler_read_block(
        &self,
        buf: &mut [u8; BUF_SIZE],
        saved: &mut Vec<u8>,
        saved_str: &mut String,
    ) -> Result<(), String> {
        let mut write_handle = self
            .state
            .try_write()
            .map_err(|_| String::from("Failed to get MPDHandler write lock (read_block)"))?;
        let mut read_amount: usize = 0;
        let read_result = write_handle.stream.read(buf);
        if let Err(io_err) = read_result {
            if io_err.kind() != io::ErrorKind::WouldBlock {
                return Err(format!("TCP stream error: {io_err}"));
            } else {
                return Ok(());
            }
        } else if let Ok(read_amount_result) = read_result {
            if read_amount_result == 0 {
                write_handle.recv_zero_bytes_count += 1;
                if write_handle.recv_zero_bytes_count > RESTART_ZERO_BYTES_COUNT {
                    write_handle.recv_zero_bytes_count = 0;
                    let log_level = write_handle.log_level;
                    log(
                        "Too many recv-zero-bytes, restarting connection...",
                        LogState::Warning,
                        log_level,
                    );
                    return restart_stream(&mut write_handle, log_level);
                }
                return Err(String::from("Got zero bytes from TCP stream"));
            } else {
                write_handle.recv_zero_bytes_count = 0;
                read_amount = read_amount_result;
            }
        }
        let mut buf_vec: Vec<u8> = Vec::from(&buf[0..read_amount]);

        let mut got_mpd_state: MPDPlayState = MPDPlayState::Playing;

        'handle_buf: loop {
            if write_handle.current_binary_size > 0 {
                if write_handle.current_binary_size <= buf_vec.len() {
                    let count = write_handle.current_binary_size;
                    write_handle.art_data.extend_from_slice(&buf_vec[0..count]);
                    buf_vec = buf_vec.split_off(count + 1);
                    write_handle.current_binary_size = 0;
                    write_handle.poll_state = PollState::None;
                    log(
                        format!(
                            "Album art recv progress: {}/{}",
                            write_handle.art_data.len(),
                            write_handle.art_data_size
                        ),
                        LogState::Debug,
                        write_handle.log_level,
                    );
                    if write_handle.art_data.len() == write_handle.art_data_size {
                        write_handle.dirty_flag.store(true, Ordering::Release);
                    }
                } else {
                    write_handle.art_data.extend_from_slice(&buf_vec);
                    write_handle.current_binary_size -= buf_vec.len();
                    log(
                        format!(
                            "Album art recv progress: {}/{}",
                            write_handle.art_data.len(),
                            write_handle.art_data_size
                        ),
                        LogState::Debug,
                        write_handle.log_level,
                    );
                    if write_handle.art_data.len() == write_handle.art_data_size {
                        write_handle.dirty_flag.store(true, Ordering::Release);
                    }
                    break 'handle_buf;
                }
            }
            let read_line_result = read_line(&mut buf_vec, saved, write_handle.is_init);
            if let Ok(mut line) = read_line_result {
                line = saved_str.clone() + &line;
                *saved_str = String::new();
                if write_handle.is_init {
                    if line.starts_with("OK MPD ") {
                        write_handle.is_init = false;
                        log(
                            "Got initial \"OK\" from MPD",
                            LogState::Debug,
                            write_handle.log_level,
                        );
                        write_handle.poll_state = PollState::None;
                        break 'handle_buf;
                    } else {
                        return Err(String::from("Did not get expected init message from MPD"));
                    }
                } // write_handle.is_init

                if line.starts_with("OK") {
                    log(
                        format!("Got OK when poll state is {:?}", write_handle.poll_state),
                        LogState::Debug,
                        write_handle.log_level,
                    );
                    match write_handle.poll_state {
                        PollState::Password => write_handle.is_authenticated = true,
                        PollState::ReadPicture => {
                            if write_handle.art_data.is_empty() {
                                write_handle.can_get_album_art = false;
                                write_handle.dirty_flag.store(true, Ordering::Release);
                                log(
                                    "No embedded album art",
                                    LogState::Warning,
                                    write_handle.log_level,
                                );
                            }
                        }
                        PollState::ReadPictureInDir if write_handle.art_data.is_empty() => {
                            write_handle.can_get_album_art_in_dir = false;
                            write_handle.dirty_flag.store(true, Ordering::Release);
                            log(
                                "No album art in dir",
                                LogState::Warning,
                                write_handle.log_level,
                            );
                        }
                        _ => (),
                    }
                    write_handle.poll_state = PollState::None;
                    break 'handle_buf;
                } else if line.starts_with("ACK") {
                    log(&line, LogState::Warning, write_handle.log_level);
                    match write_handle.poll_state {
                        PollState::Password => {
                            write_handle.can_authenticate = false;
                            write_handle.dirty_flag.store(true, Ordering::Release);
                            write_handle.error_text = "Failed to authenticate to MPD".into();
                            write_handle.stop_flag.store(true, Ordering::Release);
                        }
                        PollState::CurrentSong | PollState::Status => {
                            write_handle.can_get_status = false;
                            write_handle.dirty_flag.store(true, Ordering::Release);
                            write_handle.error_text = "Failed to get MPD status".into();
                            if line.contains("don't have permission") {
                                write_handle.can_authenticate = false;
                                write_handle.error_text.push_str(" (not authenticated?)");
                            }
                        }
                        PollState::ReadPicture => {
                            write_handle.can_get_album_art = false;
                            write_handle.dirty_flag.store(true, Ordering::Release);
                            log(
                                "Failed to get readpicture",
                                LogState::Warning,
                                write_handle.log_level,
                            );
                            // Not setting error_text here since
                            // ReadPictureInDir is tried next
                        }
                        PollState::ReadPictureInDir => {
                            write_handle.can_get_album_art_in_dir = false;
                            write_handle.dirty_flag.store(true, Ordering::Release);
                            log(
                                "Failed to get albumart",
                                LogState::Warning,
                                write_handle.log_level,
                            );
                            write_handle.error_text = "Failed to get album art from MPD".into();
                        }
                        _ => (),
                    }
                    write_handle.poll_state = PollState::None;
                } else if line.starts_with("state: ") {
                    let remaining = line.split_off(7);
                    let remaining = remaining.trim();
                    if remaining == "stop" {
                        write_handle.current_song_filename.clear();
                        write_handle.art_data.clear();
                        write_handle.art_data_size = 0;
                        write_handle.art_data_type.clear();
                        write_handle.can_get_album_art = true;
                        write_handle.can_get_album_art_in_dir = true;
                        write_handle.current_song_title.clear();
                        write_handle.current_song_artist.clear();
                        write_handle.current_song_album.clear();
                        write_handle.current_song_length = 0.0;
                        write_handle.current_song_position = 0.0;
                        write_handle.did_check_overtime = false;
                        write_handle.force_get_status = true;
                    }
                    if remaining == "stop" || remaining == "pause" {
                        got_mpd_state = if remaining == "stop" {
                            MPDPlayState::Stopped
                        } else {
                            MPDPlayState::Paused
                        };
                        write_handle.error_text.clear();
                        write!(&mut write_handle.error_text, "MPD has {got_mpd_state:?}").ok();
                        log(
                            format!("MPD is {got_mpd_state:?}"),
                            LogState::Warning,
                            write_handle.log_level,
                        );
                        break 'handle_buf;
                    }
                } else if line.starts_with("file: ") {
                    let song_file = line.split_off(6);
                    if song_file != write_handle.current_song_filename {
                        write_handle.current_song_filename = song_file;
                        write_handle.art_data.clear();
                        write_handle.art_data_size = 0;
                        write_handle.art_data_type.clear();
                        write_handle.can_get_album_art = true;
                        write_handle.can_get_album_art_in_dir = true;
                        write_handle.current_song_title.clear();
                        write_handle.current_song_artist.clear();
                        write_handle.current_song_album.clear();
                        write_handle.current_song_length = 0.0;
                        write_handle.current_song_position = 0.0;
                        write_handle.did_check_overtime = false;
                        write_handle.force_get_status = true;
                        write_handle.error_text.clear();
                    }
                    write_handle.dirty_flag.store(true, Ordering::Release);
                    write_handle.song_title_get_time = Instant::now();
                } else if line.starts_with("elapsed: ") {
                    let parse_pos_result = f64::from_str(&line.split_off(9));
                    if let Ok(value) = parse_pos_result {
                        write_handle.current_song_position = value;
                        write_handle.dirty_flag.store(true, Ordering::Release);
                        write_handle.song_pos_get_time = Instant::now();
                    } else {
                        log(
                            "Failed to parse current song position",
                            LogState::Warning,
                            write_handle.log_level,
                        );
                    }
                } else if line.starts_with("duration: ") {
                    let parse_pos_result = f64::from_str(&line.split_off(10));
                    if let Ok(value) = parse_pos_result {
                        write_handle.current_song_length = value;
                        write_handle.dirty_flag.store(true, Ordering::Release);
                        write_handle.song_length_get_time = Instant::now();
                    } else {
                        log(
                            "Failed to parse current song duration",
                            LogState::Warning,
                            write_handle.log_level,
                        );
                    }
                } else if line.starts_with("size: ") {
                    let parse_artsize_result = usize::from_str(&line.split_off(6));
                    if let Ok(value) = parse_artsize_result {
                        write_handle.art_data_size = value;
                        write_handle.dirty_flag.store(true, Ordering::Release);
                    } else {
                        log(
                            "Failed to parse album art byte size",
                            LogState::Warning,
                            write_handle.log_level,
                        );
                    }
                } else if line.starts_with("binary: ") {
                    let parse_artbinarysize_result = usize::from_str(&line.split_off(8));
                    if let Ok(value) = parse_artbinarysize_result {
                        write_handle.current_binary_size = value;
                    } else {
                        log(
                            "Failed to parse album art chunk byte size",
                            LogState::Warning,
                            write_handle.log_level,
                        );
                    }
                } else if line.starts_with("Title: ") {
                    write_handle.current_song_title = line.split_off(7);
                } else if line.starts_with("Artist: ") {
                    write_handle.current_song_artist = line.split_off(8);
                } else if line.starts_with("Album: ") {
                    write_handle.current_song_album = line.split_off(7);
                } else if line.starts_with("type: ") {
                    write_handle.art_data_type = line.split_off(6);
                } else {
                    log(
                        format!("Got unrecognized/ignored line: {line}"),
                        LogState::Warning,
                        write_handle.log_level,
                    );
                }
            } else if let Err((msg, read_line_in_progress)) = read_line_result {
                log(
                    format!(
                        "read_line: {}, saved size == {}, in_progress size == {}",
                        msg,
                        saved.len(),
                        read_line_in_progress.len()
                    ),
                    LogState::Warning,
                    write_handle.log_level,
                );
                *saved_str = read_line_in_progress;
                break 'handle_buf;
            } else {
                unreachable!();
            }
        } // 'handle_buf: loop

        if got_mpd_state != write_handle.mpd_play_state {
            write_handle.dirty_flag.store(true, Ordering::Release);
            if got_mpd_state == MPDPlayState::Playing {
                write_handle.error_text.clear();
            }
        }
        write_handle.mpd_play_state = got_mpd_state;
        if got_mpd_state != MPDPlayState::Playing {
            write_handle.poll_state = PollState::None;
            write_handle.song_pos_get_time = Instant::now();
            write_handle.current_song_length = 30.0;
            write_handle.current_song_position = 0.0;
        }

        Ok(())
    }

    fn handler_write_block(&self) -> Result<(), String> {
        let mut write_handle = self
            .state
            .try_write()
            .map_err(|_| String::from("Failed to get MPDHandler write lock (write_block)"))?;
        if write_handle.poll_state == PollState::None {
            if !write_handle.did_check_overtime
                && write_handle.current_song_position
                    + write_handle.song_pos_get_time.elapsed().as_secs_f64()
                    - 0.2
                    > write_handle.current_song_length
            {
                write_handle.did_check_overtime = true;
                write_handle.force_get_current_song = true;
            }

            if !write_handle.is_authenticated
                && !write_handle.password.is_empty()
                && write_handle.can_authenticate
            {
                let p = write_handle.password.clone();
                let write_result = write_handle
                    .stream
                    .write(format!("password {p}\n").as_bytes());
                if write_result.is_ok() {
                    write_handle.poll_state = PollState::Password;
                } else if let Err(e) = write_result {
                    log(
                        format!("Failed to send password for authentication: {e}"),
                        LogState::Error,
                        write_handle.log_level,
                    );
                }
            } else if write_handle.can_get_status
                && (write_handle.song_title_get_time.elapsed() > POLL_DURATION
                    || write_handle.force_get_current_song)
                && write_handle.mpd_play_state == MPDPlayState::Playing
            {
                write_handle.force_get_current_song = false;
                let write_result = write_handle.stream.write(b"currentsong\n");
                if write_result.is_ok() {
                    write_handle.poll_state = PollState::CurrentSong;
                } else if let Err(e) = write_result {
                    log(
                        format!("Failed to request song info over stream: {e}"),
                        LogState::Error,
                        write_handle.log_level,
                    );
                }
            } else if write_handle.can_get_status
                && (write_handle.song_length_get_time.elapsed() > POLL_DURATION
                    || write_handle.song_pos_get_time.elapsed() > POLL_DURATION
                    || write_handle.force_get_status)
            {
                write_handle.force_get_status = false;
                let write_result = write_handle.stream.write(b"status\n");
                if write_result.is_ok() {
                    write_handle.poll_state = PollState::Status;
                } else if let Err(e) = write_result {
                    log(
                        format!("Failed to request status over stream: {e}"),
                        LogState::Error,
                        write_handle.log_level,
                    );
                }
            } else if (write_handle.art_data.is_empty()
                || write_handle.art_data.len() != write_handle.art_data_size)
                && !write_handle.current_song_filename.is_empty()
            {
                let title = write_handle.current_song_filename.clone();
                let art_data_length = write_handle.art_data.len();
                if write_handle.can_get_album_art {
                    let write_result = write_handle
                        .stream
                        .write(format!("readpicture \"{title}\" {art_data_length}\n").as_bytes());
                    if write_result.is_ok() {
                        write_handle.poll_state = PollState::ReadPicture;
                    } else if let Err(e) = write_result {
                        log(
                            format!("Failed to request album art: {e}"),
                            LogState::Error,
                            write_handle.log_level,
                        );
                    }
                } else if write_handle.can_get_album_art_in_dir {
                    let write_result = write_handle
                        .stream
                        .write(format!("albumart \"{title}\" {art_data_length}\n").as_bytes());
                    if write_result.is_ok() {
                        write_handle.poll_state = PollState::ReadPictureInDir;
                    } else if let Err(e) = write_result {
                        log(
                            format!("Failed to request album art in dir: {e}"),
                            LogState::Error,
                            write_handle.log_level,
                        );
                    }
                }
            }
        }

        Ok(())
    }

    fn is_reading_picture(&self) -> bool {
        loop {
            if let Ok(read_handle) = self.state.try_read() {
                return read_handle.poll_state == PollState::ReadPicture
                    || read_handle.poll_state == PollState::ReadPictureInDir;
            } else {
                thread::sleep(Duration::from_millis(5));
            }
        }
    }
}

impl MPDHandlerState {
    pub fn get_art_type(&self) -> String {
        self.art_data_type.clone()
    }

    pub fn is_art_data_ready(&self) -> bool {
        log(
            format!(
                "is_art_data_ready(): art_data_size == {}, art_data.len() == {}",
                self.art_data_size,
                self.art_data.len()
            ),
            LogState::Debug,
            self.log_level,
        );
        self.art_data_size != 0 && self.art_data.len() == self.art_data_size
    }

    pub fn get_art_data(&self) -> &[u8] {
        &self.art_data
    }
}
