use super::tty_ldisc::LineDisciplineType;

/// ## 窗口大小
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct WindowSize {
    /// 行
    pub row: u16,
    /// 列
    pub col: u16,
    /// x方向像素数
    pub xpixel: u16,
    /// y方向像素数
    pub ypixel: u16,
}

impl WindowSize {
    pub const DEFAULT: WindowSize = WindowSize::new(24, 80, 1, 1);
    pub const fn new(row: u16, col: u16, xpixel: u16, ypixel: u16) -> Self {
        Self {
            row,
            col,
            xpixel,
            ypixel,
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Termios {
    pub input_mode: InputMode,
    pub output_mode: OutputMode,
    pub control_mode: ControlMode,
    pub local_mode: LocalMode,
    pub control_characters: [u8; CONTORL_CHARACTER_NUM],
    pub line: LineDisciplineType,
    pub input_speed: u32,
    pub output_speed: u32,
}

#[derive(Clone, Copy, Default)]
pub struct PosixTermios {
    pub c_iflag: u32,
    pub c_oflag: u32,
    pub c_cflag: u32,
    pub c_lflag: u32,
    pub c_cc: [u8; CONTORL_CHARACTER_NUM],
    pub c_line: u8,
    pub c_ispeed: u32,
    pub c_ospeed: u32,
}

impl PosixTermios {
    pub fn from_kernel_termios(termios: Termios) -> Self {
        Self {
            c_iflag: termios.input_mode.bits,
            c_oflag: termios.output_mode.bits,
            c_cflag: termios.control_mode.bits,
            c_lflag: termios.local_mode.bits,
            c_cc: termios.control_characters,
            c_line: termios.line as u8,
            c_ispeed: termios.input_speed,
            c_ospeed: termios.output_speed,
        }
    }

    pub fn to_kernel_termios(self) -> Termios {
        // TODO：这里没有考虑非规范模式
        Termios {
            input_mode: InputMode::from_bits_truncate(self.c_iflag),
            output_mode: OutputMode::from_bits_truncate(self.c_oflag),
            control_mode: ControlMode::from_bits_truncate(self.c_cflag),
            local_mode: LocalMode::from_bits_truncate(self.c_lflag),
            control_characters: self.c_cc,
            line: LineDisciplineType::from_line(self.c_line),
            input_speed: self.c_ispeed,
            output_speed: self.c_ospeed,
        }
    }
}

pub const INIT_CONTORL_CHARACTERS: [u8; CONTORL_CHARACTER_NUM] = [
    b'C' - 0x40,  // VINTR
    b'\\' - 0x40, // VQUIT
    0o177,        // VERASE
    b'U' - 0x40,  // VKILL
    b'D' - 0x40,  // VEOF
    1,            // VMIN
    0,            // VEOL
    0,            // VTIME
    0,            // VEOL2
    0,            // VSWTC
    b'W' - 0x40,  // VWERASE
    b'R' - 0x40,  // VREPRINT
    b'Z' - 0x40,  // VSUSP
    b'Q' - 0x40,  // VSTART
    b'S' - 0x40,  // VSTOP
    b'V' - 0x40,  // VLNEXT
    b'O' - 0x40,  // VDISCARD
    0,
    0,
];

// pub const INIT_CONTORL_CHARACTERS: [u8; CONTORL_CHARACTER_NUM] = {
//     let mut chs: [u8; CONTORL_CHARACTER_NUM] = Default::default();
//     chs[ControlCharIndex::VINTR] = b'C' - 0x40;
//     chs[ControlCharIndex::VQUIT] = b'\\' - 0x40;
//     chs[ControlCharIndex::VERASE] = 0o177;
//     chs[ControlCharIndex::VKILL] = b'U' - 0x40;
//     chs[ControlCharIndex::VEOF] = b'D' - 0x40;
//     chs[ControlCharIndex::VSTART] = b'Q' - 0x40;
//     chs[ControlCharIndex::VSTOP] = b'S' - 0x40;
//     chs[ControlCharIndex::VSUSP] = b'Z' - 0x40;
//     chs[ControlCharIndex::VREPRINT] = b'R' - 0x40;
//     chs[ControlCharIndex::VDISCARD] = b'O' - 0x40;
//     chs[ControlCharIndex::VWERASE] = b'W' - 0x40;
//     chs[ControlCharIndex::VLNEXT] = b'V' - 0x40;
//     // chs[ContorlCharIndex::VDSUSP] = b'Y'  - 0x40;
//     chs[ControlCharIndex::VMIN] = 1;
//     return chs;
// };

lazy_static! {
    pub static ref TTY_STD_TERMIOS: Termios = {
        Termios {
            input_mode: InputMode::ICRNL | InputMode::IXON,
            output_mode: OutputMode::OPOST | OutputMode::ONLCR,
            control_mode: ControlMode::B38400
                | ControlMode::CREAD
                | ControlMode::HUPCL
                | ControlMode::CS8,
            local_mode: LocalMode::ISIG
                | LocalMode::ICANON
                | LocalMode::ECHO
                | LocalMode::ECHOE
                | LocalMode::ECHOK
                | LocalMode::ECHOCTL
                | LocalMode::ECHOKE
                | LocalMode::IEXTEN,
            control_characters: INIT_CONTORL_CHARACTERS,
            line: LineDisciplineType::NTty,
            input_speed: 38400,
            output_speed: 38400,
        }
    };
}

pub const CONTORL_CHARACTER_NUM: usize = 19;

bitflags! {
    /// termios输入特性
    pub struct InputMode: u32 {
        /// 如果设置了该标志，表示启用软件流控制。
        const IXON = 0x0400;
        /// 如果设置了该标志，表示启用输入流控制。
        const IXOFF = 0x1000;
        /// Map Uppercase to Lowercase on Input 将大写转换为小写
        /// 表示不区分大小写
        const IUCLC = 0x0200;
        /// 如果设置了该标志，表示当输入队列满时，产生一个响铃信号。
        const IMAXBEL = 0x2000;
        /// 如果设置了该标志，表示输入数据被视为 UTF-8 编码。
        const IUTF8 = 0x4000;

        /// 忽略中断信号
        const IGNBRK	= 0x001;
        /// 检测到中断信号时生成中断（产生中断信号）
        const BRKINT	= 0x002;
        /// 忽略具有奇偶校验错误的字符
        const IGNPAR	= 0x004;
        /// 在检测到奇偶校验错误或帧错误时，将字符以 \377 标记
        const PARMRK	= 0x008;
        /// 启用输入奇偶校验检查
        const INPCK	= 0x010;
        /// 从输入字符中剥离第 8 位，即只保留低 7 位
        const ISTRIP	= 0x020;
        /// 表示将输入的换行符 (\n) 映射为回车符 (\r)
        const INLCR	= 0x040;
        /// 表示忽略回车符 (\r)
        const IGNCR	= 0x080;
        /// 表示将输入的回车符 (\r) 映射为换行符 (\n)
        const ICRNL	= 0x100;
        /// 表示在输入被停止（Ctrl-S）后，任何字符的输入都将重新启动输入
        const IXANY	= 0x800;
    }

    /// termios输出特性
    pub struct OutputMode: u32 {
        /// 在输出时将换行符替换\r\n
        const ONLCR	= 0x00004;
        /// Map Lowercase to Uppercase on Output 输出字符时将小写字母映射为大写字母
        const OLCUC	= 0x00002;

        /// 与NL协同 配置换行符的处理方式
        const NLDLY	= 0x00100;
        const   NL0	= 0x00000;  // 不延迟换行
        const   NL1	= 0x00100;  // 延迟换行（输出回车后等待一段时间再输出换行）

        /// 配置水平制表符的处理方式
        const TABDLY = 0x01800;
        const  TAB0 = 0x00000;  // 不延迟水平制表符
        const  TAB1 = 0x00800;  // 在输出水平制表符时，延迟到下一个设置的水平制表符位置
        const  TAB2 = 0x01000;  // 在输出水平制表符时，延迟到下一个设置的 8 的倍数的位置
        const  TAB3 = 0x01800;  // TAB3 和 XTABS（与 TAB3 等效）保留，暂未使用
        const XTABS = 0x01800;

        /// 配置回车符的处理方式
        const CRDLY	= 0x00600;
        const   CR0	= 0x00000;  // 不延迟回车
        const   CR1	= 0x02000;  //  延迟回车（输出回车后等待一段时间再输出换行）
        const   CR2	= 0x04000;  // CR2 和 CR3保留，暂未使用
        const   CR3	= 0x06000;

        /// 配置换页符（form feed）的处理方式
        const FFDLY	= 0x08000;
        const   FF0	= 0x00000;  // 不延迟换页
        const   FF1	= 0x08000;  // 延迟换页

        /// 配置退格符（backspace）的处理方式
        const BSDLY	= 0x02000;
        const   BS0	= 0x00000;  // 不延迟退格
        const   BS1	= 0x02000;  // 延迟退格

        /// 配置垂直制表符（vertical tab）的处理方式
        const VTDLY	= 0x04000;
        const   VT0	= 0x00000;  // 不延迟垂直制表符
        const   VT1	= 0x04000;  // 延迟垂直制表符

        /// 表示执行输出处理，即启用输出处理函数
        const OPOST	= 0x01;
        /// 表示将输出的回车符 (\r) 映射为换行符 (\n)
        const OCRNL	= 0x08;
        /// 表示在输出时，如果光标在第 0 列，则不输出回车符 (\r)
        const ONOCR	= 0x10;
        /// 表示将回车符 (\r) 映射为换行符 (\n)
        const ONLRET	= 0x20;
        /// 表示使用填充字符进行延迟。这个填充字符的默认值是空格。
        const OFILL	= 0x40;
        /// 表示使用删除字符 (DEL, \177) 作为填充字符
        const OFDEL	= 0x80;
    }

    /// 配置终端设备的基本特性和控制参数
    pub struct ControlMode: u32 {
        /// Baud Rate Mask 指定波特率的掩码
        const CBAUD		= 0x0000100f;
        /// Extra Baud Bits 指定更高的波特率位
        const CBAUDEX	= 0x00001000;
        /// Custom Baud Rate 指定自定义波特率 如果设置了 BOTHER，则通过以下位来设置自定义的波特率值
        const BOTHER	= 0x00001000;

        /* Common CBAUD rates */
        const     B0	= 0x00000000;	/* hang up */
        const    B50	= 0x00000001;
        const    B75	= 0x00000002;
        const   B110	= 0x00000003;
        const   B134	= 0x00000004;
        const   B150	= 0x00000005;
        const   B200	= 0x00000006;
        const   B300	= 0x00000007;
        const   B600	= 0x00000008;
        const  B1200	= 0x00000009;
        const  B1800	= 0x0000000a;
        const  B2400	= 0x0000000b;
        const  B4800	= 0x0000000c;
        const  B9600	= 0x0000000d;
        const B19200	= 0x0000000e;
        const B38400	= 0x0000000f;

        const     B57600 = 0x00001001;
        const    B115200 = 0x00001002;
        const    B230400 = 0x00001003;
        const    B460800 = 0x00001004;
        const    B500000 = 0x00001005;
        const    B576000 = 0x00001006;
        const    B921600 = 0x00001007;
        const   B1000000 = 0x00001008;
        const   B1152000 = 0x00001009;
        const   B1500000 = 0x0000100a;
        const   B2000000 = 0x0000100b;
        const   B2500000 = 0x0000100c;
        const   B3000000 = 0x0000100d;
        const   B3500000 = 0x0000100e;
        const   B4000000 = 0x0000100f;

        /// 指定字符大小的掩码 以下位为特定字符大小
        const CSIZE		= 0x00000030;
        const   CS5		= 0x00000000;
        const   CS6		= 0x00000010;
        const   CS7		= 0x00000020;
        const   CS8		= 0x00000030;

        /// Stop Bit Select 表示使用两个停止位；否则，表示使用一个停止位
        const CSTOPB	= 0x00000040;
        /// 表示启用接收器。如果未设置，则禁用接收器。
        const CREAD		= 0x00000080;
        /// 表示启用奇偶校验。如果未设置，则禁用奇偶校验。
        const PARENB	= 0x00000100;
        /// 表示启用奇校验。如果未设置，则表示启用偶校验。
        const PARODD	= 0x00000200;
        /// 表示在终端设备被关闭时挂断线路（执行挂断操作）
        const HUPCL		= 0x00000400;
        /// 表示忽略调制解调器的状态（DCD、DSR、CTS 等）
        const CLOCAL	= 0x00000800;
        /// 指定输入波特率的掩码
        const CIBAUD	= 0x100f0000;

        const ADDRB = 0x20000000;
    }

    /// 配置终端设备的本地模式（local mode）或控制输入处理的行为
    pub struct LocalMode: u32 {
        /// 启用中断字符（Ctrl-C、Ctrl-Z）
        const ISIG	 = 0x00001;
        /// 表示启用规范模式，即启用行缓冲和回显。在规范模式下，输入被缓冲，并且只有在输入回车符时才会传递给应用程序。
        const ICANON = 0x00002;
        /// 表示启用大写模式，即输入输出都将被转换为大写。
        const XCASE	 = 0x00004;
        /// 表示启用回显（显示用户输入的字符）
        const ECHO	 = 0x00008;
        /// 表示在回显时将擦除的字符用 backspace 和空格字符显示。
        const ECHOE	 = 0x00010;
        /// 表示在回显时将换行符后的字符用空格字符显示。
        const ECHOK	 = 0x00020;
        /// 表示在回显时将换行符显示为换行和回车符。
        const ECHONL = 0x00040;
        /// 表示在收到中断（Ctrl-C）和退出（Ctrl-\）字符后，不清空输入和输出缓冲区。
        const NOFLSH = 0x00080;
        /// 表示在后台进程尝试写入终端时，发送停止信号（Ctrl-S）
        const TOSTOP = 0x00100;
        /// 表示在回显时，显示控制字符为 ^ 加字符。
        const ECHOCTL= 0x00200;
        /// 表示在回显时显示带有 # 的换行符（为了与 echo -n 命令兼容）。
        const ECHOPRT= 0x00400;
        /// 表示在回显时将 KILL 字符（Ctrl-U）用空格字符显示。
        const ECHOKE = 0x00800;
        /// 表示输出正在被冲刷（flush），通常是由于输入/输出流的状态变化。
        const FLUSHO = 0x01000;
        /// 表示在规范模式下，存在需要重新打印的字符。
        const PENDIN = 0x04000;
        /// 表示启用实现定义的输入处理。
        const IEXTEN = 0x08000;
        /// 表示启用扩展的处理函数
        const EXTPROC= 0x10000;
    }

    pub struct TtySetTermiosOpt: u8 {
        const TERMIOS_FLUSH	=1;
        const TERMIOS_WAIT	=2;
        const TERMIOS_TERMIO	=4;
        const TERMIOS_OLD	=8;
    }
}

/// 对应termios中控制字符的索引
pub struct ControlCharIndex;
#[allow(dead_code)]
impl ControlCharIndex {
    pub const DISABLE_CHAR: u8 = b'\0';
    /// 中断信号
    pub const VINTR: usize = 0;
    /// 退出信号
    pub const VQUIT: usize = 1;
    /// 退格
    pub const VERASE: usize = 2;
    /// 终止输入信号
    pub const VKILL: usize = 3;
    /// 文件结束信号 \0?
    pub const VEOF: usize = 4;
    /// 指定非规范模式下的最小字符数
    pub const VMIN: usize = 5;
    /// 换行符
    pub const VEOL: usize = 6;
    /// 指定非规范模式下的超时时间
    pub const VTIME: usize = 7;
    /// 换行符
    pub const VEOL2: usize = 8;
    /// 未使用，保留
    pub const VSWTC: usize = 9;
    /// 擦除前一个单词
    pub const VWERASE: usize = 10;
    /// 重新打印整行
    pub const VREPRINT: usize = 11;
    /// 挂起信号
    pub const VSUSP: usize = 12;
    /// 启动输出信号
    pub const VSTART: usize = 13;
    /// 停止输出信号
    pub const VSTOP: usize = 14;
    /// 将下一个字符视为字面值，而不是特殊字符
    pub const VLNEXT: usize = 15;
    /// 对应于字符丢弃信号，用于丢弃当前输入的行
    pub const VDISCARD: usize = 16;
}
