use alloc::{sync::Arc, vec::Vec};
use log::warn;
use system_error::SystemError;

use crate::{
    driver::{
        tty::{
            console::ConsoleSwitch,
            virtual_terminal::{
                virtual_console::{CursorOperation, ScrollDir, VcCursor, VirtualConsoleData},
                Color, VirtConsole,
            },
        },
        video::fbdev::base::{
            CopyAreaData, FbCursor, FbCursorSetMode, FbImage, FbVisual, FillRectData, FillRectROP,
            FrameBuffer, ScrollMode, FRAME_BUFFER_SET,
        },
    },
    libs::{
        font::FontDesc,
        spinlock::{SpinLock, SpinLockGuard},
    },
};

use super::{FbConAttr, FrameBufferConsole, FrameBufferConsoleData};

#[derive(Debug)]
pub struct BlittingFbConsole {
    fb: SpinLock<Option<Arc<dyn FrameBuffer>>>,
    fbcon_data: SpinLock<FrameBufferConsoleData>,
}

unsafe impl Send for BlittingFbConsole {}
unsafe impl Sync for BlittingFbConsole {}

impl BlittingFbConsole {
    pub fn new() -> Result<Self, SystemError> {
        Ok(Self {
            fb: SpinLock::new(None),
            fbcon_data: SpinLock::new(FrameBufferConsoleData::default()),
        })
    }

    fn fb(&self) -> Arc<dyn FrameBuffer> {
        self.fb.lock().clone().unwrap()
    }

    fn get_color(&self, vc_data: &VirtualConsoleData, c: u16, is_fg: bool) -> u32 {
        let fb_info = self.fb();
        let mut color = 0;

        let depth = fb_info.color_depth();

        if depth != 1 {
            if is_fg {
                let fg_shift = if vc_data.hi_font_mask != 0 { 9 } else { 8 };
                color = (c as u32 >> fg_shift) & 0x0f
            } else {
                let bg_shift = if vc_data.hi_font_mask != 0 { 13 } else { 12 };
                color = (c as u32 >> bg_shift) & 0x0f
            }
        }

        match depth {
            1 => {
                let col = self.mono_color();
                let fg;
                let bg;
                if fb_info.current_fb_fix().visual != FbVisual::Mono01 {
                    fg = col;
                    bg = 0;
                } else {
                    fg = 0;
                    bg = col;
                }
                color = if is_fg { fg } else { bg };
            }
            2 => {
                /*
                    颜色深度为2，即16色，
                   将16色的颜色值映射到4色的灰度，
                   其中颜色0映射为黑色，颜色1到6映射为白色，
                   颜色7到8映射为灰色，其他颜色映射为强烈的白色。
                */
                if (1..=6).contains(&color) {
                    // 白色
                    color = 2;
                } else if (7..=8).contains(&color) {
                    // 灰色
                    color = 1;
                } else {
                    // 强白
                    color = 3;
                }
            }
            3 => {
                /*
                   颜色深度为3，即256色，仅保留颜色的低3位，即颜色 0 到 7
                */
                color &= 7;
            }
            _ => {}
        }
        color
    }

    /// ## 计算单色调的函数
    fn mono_color(&self) -> u32 {
        let fb_info = self.fb();
        let mut max_len = fb_info
            .current_fb_var()
            .green
            .length
            .max(fb_info.current_fb_var().red.length);

        max_len = max_len.max(fb_info.current_fb_var().blue.length);

        return (!(0xfff << max_len)) & 0xff;
    }

    fn bit_put_string(
        &self,
        vc_data: &VirtualConsoleData,
        buf: &[u16],
        attr: FbConAttr,
        cnt: u32,
        cellsize: u32,
        image: &mut FbImage,
    ) {
        let charmask = if vc_data.hi_font_mask != 0 {
            0x1ff
        } else {
            0xff
        };

        let mut offset;
        let image_line_byte = image.width as usize / 8;

        let byte_width = vc_data.font.width as usize / 8;
        let font_height = vc_data.font.height as usize;
        // let mut char_offset = 0;
        for (char_offset, char_item) in buf.iter().enumerate().take(cnt as usize) {
            // 在字符表中的index
            let ch = char_item & charmask;
            // 计算出在font表中的偏移量
            let font_offset = ch as usize * cellsize as usize;
            let font_offset_end = font_offset + cellsize as usize;
            // 设置image的data

            let src = &vc_data.font.data[font_offset..font_offset_end];
            let mut dst = vec![0; src.len()];
            dst.copy_from_slice(src);

            if !attr.is_empty() {
                attr.update_attr(&mut dst, src, vc_data)
            }

            offset = char_offset * byte_width;
            let mut dst_offset = 0;
            for _ in 0..font_height {
                let dst_offset_next = dst_offset + byte_width;
                image.data[offset..offset + byte_width]
                    .copy_from_slice(&dst[dst_offset..dst_offset_next]);

                offset += image_line_byte;
                dst_offset = dst_offset_next;
            }
        }

        self.fb().fb_image_blit(image);
    }
}

impl ConsoleSwitch for BlittingFbConsole {
    fn con_init(
        &self,
        _vc: &Arc<VirtConsole>,
        vc_data: &mut VirtualConsoleData,
        init: bool,
    ) -> Result<(), system_error::SystemError> {
        let fb_set_guard = FRAME_BUFFER_SET.read();
        let fb = fb_set_guard.get(vc_data.index);
        if fb.is_none() {
            return Err(SystemError::EINVAL);
        }
        let fb = fb.unwrap();
        if fb.is_none() {
            log::warn!(
                "The Framebuffer with FbID {} has not been initialized yet.",
                vc_data.index
            );

            return Err(SystemError::ENODEV);
        }

        let fb = fb.as_ref().unwrap().clone();

        if init {
            // 初始化字体
            let var = fb.current_fb_var();
            let font = FontDesc::get_default_font(var.xres, var.yres, 0, 0);
            vc_data.font.data = font.data.to_vec();
            vc_data.font.width = font.width;
            vc_data.font.height = font.height;
            vc_data.font.count = font.char_count;
        } else {
            warn!("The frontend Framebuffer is not implemented");
        }

        vc_data.color_mode = fb.color_depth() != 1;
        vc_data.complement_mask = if vc_data.color_mode { 0x7700 } else { 0x0800 };

        if vc_data.font.count == 256 {
            // ascii
            vc_data.hi_font_mask = 0;
        } else {
            vc_data.hi_font_mask = 0x100;
            if vc_data.color_mode {
                vc_data.complement_mask <<= 1;
            }
        }

        // TODO: 考虑rotate
        if init {
            vc_data.cols = (fb.current_fb_var().xres / vc_data.font.width) as usize;
            vc_data.rows = (fb.current_fb_var().yres / vc_data.font.height) as usize;

            vc_data.pos = vc_data.state.x + vc_data.state.y * vc_data.cols;

            let new_size = vc_data.cols * vc_data.rows;
            vc_data.screen_buf.resize(new_size, 0);
        } else {
            unimplemented!("Resize is not supported at the moment!");
        }

        // 初始化fb
        *self.fb.lock() = Some(fb);

        Ok(())
    }

    fn con_deinit(&self) -> Result<(), system_error::SystemError> {
        todo!()
    }

    fn con_clear(
        &self,
        vc_data: &mut VirtualConsoleData,
        sy: usize,
        sx: usize,
        height: usize,
        width: usize,
    ) -> Result<(), system_error::SystemError> {
        let fb_data = self.fbcon_data();

        if height == 0 || width == 0 {
            return Ok(());
        }

        let y_break = (fb_data.display.virt_rows - fb_data.display.yscroll) as usize;
        if sy < y_break && sy + height > y_break {
            // 分两次clear
            let b = y_break - sy;
            let _ = self.clear(
                vc_data,
                fb_data.display.real_y(sy as u32),
                sx as u32,
                b as u32,
                width as u32,
            );
            let _ = self.clear(
                vc_data,
                fb_data.display.real_y((sy + b) as u32),
                sx as u32,
                (height - b) as u32,
                width as u32,
            );
        } else {
            let _ = self.clear(
                vc_data,
                fb_data.display.real_y(sy as u32),
                sx as u32,
                height as u32,
                width as u32,
            );
        }

        Ok(())
    }

    fn con_putc(
        &self,
        vc_data: &VirtualConsoleData,
        ch: u16,
        xpos: u32,
        ypos: u32,
    ) -> Result<(), system_error::SystemError> {
        self.con_putcs(vc_data, &[ch], 1, ypos, xpos)
    }

    fn con_putcs(
        &self,
        vc_data: &VirtualConsoleData,
        buf: &[u16],
        count: usize,
        ypos: u32,
        xpos: u32,
    ) -> Result<(), SystemError> {
        if count == 0 {
            return Ok(());
        }
        let fbcon_data = self.fbcon_data();
        let c = buf[0];
        self.put_string(
            vc_data,
            buf,
            count as u32,
            fbcon_data.display.real_y(ypos),
            xpos,
            self.get_color(vc_data, c, true),
            self.get_color(vc_data, c, false),
        )
    }

    fn con_getxy(
        &self,
        vc_data: &VirtualConsoleData,
        pos: usize,
    ) -> Result<(usize, usize, usize), SystemError> {
        if pos < vc_data.screen_buf.len() {
            let x = pos % vc_data.cols;
            let y = pos / vc_data.cols;
            let mut next_line_start = pos + (vc_data.cols - x);
            if next_line_start >= vc_data.screen_buf.len() {
                next_line_start = 0
            }
            return Ok((next_line_start, x, y));
        } else {
            return Ok((0, 0, 0));
        }
    }

    #[allow(clippy::if_same_then_else)]
    fn con_cursor(
        &self,
        vc_data: &VirtualConsoleData,
        op: crate::driver::tty::virtual_terminal::virtual_console::CursorOperation,
    ) {
        let mut fbcon_data = self.fbcon_data();

        let c = vc_data.screen_buf[vc_data.pos];

        if vc_data.cursor_type.contains(VcCursor::CUR_SW) {
            // 取消硬光标Timer，但是现在没有硬光标，先写在这
        } else {
            // 添加硬光标Timer
        }

        fbcon_data.cursor_flash = op != CursorOperation::Erase;

        drop(fbcon_data);

        self.cursor(
            vc_data,
            op,
            self.get_color(vc_data, c, true),
            self.get_color(vc_data, c, false),
        );
    }

    fn con_set_palette(
        &self,
        vc_data: &VirtualConsoleData,
        color_table: &[u8],
    ) -> Result<(), SystemError> {
        let fb_info = self.fb();
        let depth = fb_info.color_depth();
        let mut palette = Vec::new();
        palette.resize(16, Color::default());
        if depth > 3 {
            let vc_palette = &vc_data.palette;
            for i in 0..16 {
                let idx = color_table[i];
                let col = palette.get_mut(idx as usize).unwrap();
                col.red = (vc_palette[i].red << 8) | vc_palette[i].red;
                col.green = (vc_palette[i].green << 8) | vc_palette[i].green;
                col.blue = (vc_palette[i].blue << 8) | vc_palette[i].blue;
            }
        } else {
            todo!()
        }

        self.fb().set_color_map(palette)?;

        Ok(())
    }

    #[inline(never)]
    fn con_scroll(
        &self,
        vc_data: &mut VirtualConsoleData,
        top: usize,
        bottom: usize,
        dir: crate::driver::tty::virtual_terminal::virtual_console::ScrollDir,
        mut count: usize,
    ) -> bool {
        self.con_cursor(vc_data, CursorOperation::Erase);

        let fbcon_data = self.fbcon_data();
        let scroll_mode = fbcon_data.display.scroll_mode;

        drop(fbcon_data);

        match dir {
            ScrollDir::Up => {
                if count > vc_data.rows {
                    count = vc_data.rows;
                }

                match scroll_mode {
                    ScrollMode::Move => {
                        let start = top * vc_data.cols;
                        let end = bottom * vc_data.cols;
                        vc_data.screen_buf[start..end].rotate_left(count * vc_data.cols);

                        let _ = self.bmove(
                            vc_data,
                            top as i32,
                            0,
                            top as i32 - count as i32,
                            0,
                            (bottom - top) as u32,
                            vc_data.cols as u32,
                        );

                        let _ = self.con_clear(vc_data, bottom - count, 0, count, vc_data.cols);

                        let offset = vc_data.cols * (bottom - count);
                        for i in
                            vc_data.screen_buf[offset..(offset + (vc_data.cols * count))].iter_mut()
                        {
                            *i = vc_data.erase_char;
                        }

                        return true;
                    }
                    ScrollMode::PanMove => todo!(),
                    ScrollMode::WrapMove => todo!(),
                    ScrollMode::Redraw => {
                        let start = top * vc_data.cols;
                        let end = bottom * vc_data.cols;
                        vc_data.screen_buf[start..end].rotate_left(count * vc_data.cols);

                        let data = &vc_data.screen_buf[start..(bottom - count) * vc_data.cols];

                        for line in top..(bottom - count) {
                            let mut start = line * vc_data.cols;
                            let end = start + vc_data.cols;
                            let mut offset = start;
                            let mut attr = 1;
                            let mut x = 0;
                            while offset < end {
                                let c = data[offset];

                                if attr != c & 0xff00 {
                                    // 属性变化，输出完上一个的并且更新属性
                                    attr = c & 0xff00;

                                    let count = offset - start;
                                    let _ = self.con_putcs(
                                        vc_data,
                                        &data[start..offset],
                                        count,
                                        line as u32,
                                        x,
                                    );
                                    start = offset;
                                    x += count as u32;
                                }

                                offset += 1;
                            }
                            let _ = self.con_putcs(
                                vc_data,
                                &data[start..offset],
                                offset - start,
                                line as u32,
                                x,
                            );
                        }

                        let _ = self.con_clear(vc_data, bottom - count, 0, count, vc_data.cols);

                        let offset = vc_data.cols * (bottom - count);
                        for i in
                            vc_data.screen_buf[offset..(offset + (vc_data.cols * count))].iter_mut()
                        {
                            *i = vc_data.erase_char;
                        }

                        return true;
                    }
                    ScrollMode::PanRedraw => todo!(),
                }
            }
            ScrollDir::Down => {
                if count > vc_data.rows {
                    count = vc_data.rows;
                }

                match scroll_mode {
                    ScrollMode::Move => {
                        let start = top * vc_data.cols;
                        let end = bottom * vc_data.cols;
                        vc_data.screen_buf[start..end].rotate_right(count * vc_data.cols);

                        let _ = self.bmove(
                            vc_data,
                            top as i32,
                            0,
                            top as i32 + count as i32,
                            0,
                            (bottom - top - count) as u32,
                            vc_data.cols as u32,
                        );

                        let _ = self.con_clear(vc_data, top, 0, count, vc_data.cols);

                        let offset = vc_data.cols * count;
                        for i in vc_data.screen_buf[start..(start + offset)].iter_mut() {
                            *i = vc_data.erase_char;
                        }

                        return true;
                    }
                    ScrollMode::PanMove => todo!(),
                    ScrollMode::WrapMove => todo!(),
                    ScrollMode::Redraw => {
                        // self.scroll_redraw(
                        //     vc_data,
                        //     bottom - 1,
                        //     bottom - top - count,
                        //     count * vc_data.cols,
                        //     false,
                        // );

                        let _ = self.con_clear(vc_data, top, 0, count, vc_data.cols);

                        let offset = vc_data.cols * top;
                        for i in
                            vc_data.screen_buf[offset..(offset + (vc_data.cols * count))].iter_mut()
                        {
                            *i = vc_data.erase_char;
                        }

                        return true;
                    }
                    ScrollMode::PanRedraw => todo!(),
                }
            }
        }
    }
}

impl FrameBufferConsole for BlittingFbConsole {
    fn bmove(
        &self,
        vc_data: &VirtualConsoleData,
        sy: i32,
        sx: i32,
        dy: i32,
        dx: i32,
        height: u32,
        width: u32,
    ) -> Result<(), SystemError> {
        let area = CopyAreaData::new(
            dx * vc_data.font.width as i32,
            dy * vc_data.font.height as i32,
            width * vc_data.font.width,
            height * vc_data.font.height,
            sx * vc_data.font.width as i32,
            sy * vc_data.font.height as i32,
        );

        self.fb().fb_copyarea(area);
        Ok(())
    }

    fn clear(
        &self,
        vc_data: &VirtualConsoleData,
        sy: u32,
        sx: u32,
        height: u32,
        width: u32,
    ) -> Result<(), SystemError> {
        let region = FillRectData::new(
            sx * vc_data.font.width,
            sy * vc_data.font.height,
            width * vc_data.font.width,
            height * vc_data.font.height,
            self.get_color(vc_data, vc_data.erase_char, false),
            FillRectROP::Copy,
        );

        self.fb().fb_fillrect(region)?;

        Ok(())
    }

    fn put_string(
        &self,
        vc_data: &VirtualConsoleData,
        data: &[u16],
        mut count: u32,
        y: u32,
        x: u32,
        fg: u32,
        bg: u32,
    ) -> Result<(), SystemError> {
        // 向上取整
        let width = (vc_data.font.width + 7) / 8;
        let cellsize = width * vc_data.font.height;
        let fb_info = self.fb();
        // 一次能输出的最大字数，避免帧缓冲区溢出
        let max_cnt = (fb_info.current_fb_var().xres * fb_info.current_fb_var().yres) / cellsize;
        let attr = FbConAttr::get_attr(data[0], fb_info.color_depth());

        let mut image = FbImage {
            x: x * vc_data.font.width,
            y: y * vc_data.font.height,
            width: 0,
            height: vc_data.font.height,
            fg,
            bg,
            depth: 1,
            data: Default::default(),
        };

        image.data.resize(cellsize as usize * count as usize, 0);

        while count > 0 {
            let cnt = count.min(max_cnt);

            image.width = vc_data.font.width * cnt;

            self.bit_put_string(vc_data, data, attr, cnt, cellsize, &mut image);

            image.x += cnt * vc_data.font.width;
            count -= cnt;
        }

        Ok(())
    }

    fn fbcon_data(&self) -> SpinLockGuard<super::FrameBufferConsoleData> {
        self.fbcon_data.lock()
    }

    fn cursor(&self, vc_data: &VirtualConsoleData, op: CursorOperation, fg: u32, bg: u32) {
        let mut fbcon_data = self.fbcon_data();
        let fb_info = self.fb();
        let mut cursor = FbCursor::default();
        let charmask = if vc_data.hi_font_mask != 0 {
            0x1ff
        } else {
            0xff
        };

        // 向上取整
        let w = (vc_data.font.width + 7) / 8;
        let y = fbcon_data.display.real_y(vc_data.state.y as u32);

        let c = vc_data.screen_buf[vc_data.pos];
        let attr = FbConAttr::get_attr(c, fb_info.color_depth());
        let char_offset = (c as usize & charmask) * ((w * vc_data.font.height) as usize);

        if fbcon_data.cursor_state.image.data != vc_data.font.data[char_offset..]
            || fbcon_data.cursor_reset
        {
            fbcon_data.cursor_state.image.data = vc_data.font.data[char_offset..].to_vec();
            cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETIMAGE);
        }

        if !attr.is_empty() {
            fbcon_data
                .cursor_data
                .resize(w as usize * vc_data.font.height as usize, 0);

            attr.update_attr(
                &mut fbcon_data.cursor_data,
                &vc_data.font.data[char_offset..],
                vc_data,
            );
        }

        if fbcon_data.cursor_state.image.fg != fg
            || fbcon_data.cursor_state.image.bg != bg
            || fbcon_data.cursor_reset
        {
            fbcon_data.cursor_state.image.fg = fg;
            fbcon_data.cursor_state.image.bg = bg;
            cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETCMAP);
        }

        if fbcon_data.cursor_state.image.x != (vc_data.font.width * vc_data.state.x as u32)
            || fbcon_data.cursor_state.image.y != (vc_data.font.height * y)
            || fbcon_data.cursor_reset
        {
            fbcon_data.cursor_state.image.x = vc_data.font.width * vc_data.state.x as u32;
            fbcon_data.cursor_state.image.y = vc_data.font.height * y;
            cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETPOS);
        }

        if fbcon_data.cursor_state.image.height != vc_data.font.height
            || fbcon_data.cursor_state.image.width != vc_data.font.width
            || fbcon_data.cursor_reset
        {
            fbcon_data.cursor_state.image.height = vc_data.font.height;
            fbcon_data.cursor_state.image.width = vc_data.font.width;
            cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETSIZE);
        }

        if fbcon_data.cursor_state.hot_x > 0
            || fbcon_data.cursor_state.hot_y > 0
            || fbcon_data.cursor_reset
        {
            fbcon_data.cursor_state.hot_x = 0;
            cursor.hot_y = 0;
            cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETHOT);
        }

        if cursor.set_mode.contains(FbCursorSetMode::FB_CUR_SETSIZE)
            || vc_data.cursor_type != fbcon_data.display.cursor_shape
            || fbcon_data.cursor_state.mask.is_empty()
            || fbcon_data.cursor_reset
        {
            fbcon_data.display.cursor_shape = vc_data.cursor_type;
            cursor.set_mode.insert(FbCursorSetMode::FB_CUR_SETSHAPE);

            let cur_height;
            match fbcon_data.display.cursor_shape.cursor_size() {
                VcCursor::CUR_NONE => {
                    cur_height = 0;
                }
                VcCursor::CUR_UNDERLINE => {
                    if vc_data.font.height < 10 {
                        cur_height = 1;
                    } else {
                        cur_height = 2;
                    }
                }
                VcCursor::CUR_LOWER_THIRD => {
                    cur_height = vc_data.font.height / 3;
                }
                VcCursor::CUR_LOWER_HALF => {
                    cur_height = vc_data.font.height >> 1;
                }
                VcCursor::CUR_TWO_THIRDS => {
                    cur_height = (vc_data.font.height << 1) / 3;
                }
                _ => {
                    cur_height = vc_data.font.height;
                }
            }

            // 表示空白部分
            let mut size = (vc_data.font.height - cur_height) * w;
            while size > 0 {
                size -= 1;
                fbcon_data.cursor_state.mask.push(0x00);
            }
            size = cur_height * w;
            // 表示光标显示部分
            while size > 0 {
                size -= 1;
                fbcon_data.cursor_state.mask.push(0xff);
            }
        }

        match op {
            CursorOperation::Erase => {
                fbcon_data.cursor_state.enable = false;
            }
            _ => {
                fbcon_data.cursor_state.enable = !vc_data.cursor_type.contains(VcCursor::CUR_SW);
            }
        }

        if !attr.is_empty() {
            cursor.image.data = fbcon_data.cursor_data.clone();
        } else {
            cursor.image.data = vc_data.font.data
                [char_offset..char_offset + (w as usize * vc_data.font.height as usize)]
                .to_vec();
        }
        cursor.image.fg = fbcon_data.cursor_state.image.fg;
        cursor.image.bg = fbcon_data.cursor_state.image.bg;
        cursor.image.x = fbcon_data.cursor_state.image.x;
        cursor.image.y = fbcon_data.cursor_state.image.y;
        cursor.image.height = fbcon_data.cursor_state.image.height;
        cursor.image.width = fbcon_data.cursor_state.image.width;
        cursor.hot_x = fbcon_data.cursor_state.hot_x;
        cursor.hot_y = fbcon_data.cursor_state.hot_y;
        cursor.mask = fbcon_data.cursor_state.mask.clone();
        cursor.enable = fbcon_data.cursor_state.enable;
        cursor.image.depth = 1;
        cursor.rop = true;

        if fb_info.fb_cursor(&cursor).is_err() {
            let _ = fb_info.soft_cursor(cursor);
        }

        fbcon_data.cursor_reset = false;
    }
}
