use std::io::Write;
use std::mem::{self};

use crate::app_error::AppError;
use crate::pe_parse::header::{ImageSectionHeader, SectionCharacteristics};
use crate::pe_parse::pe::MutablePE;
use crate::services::file::expand_file_size;
use crate::{pe_parse::pe::ReadOnlyPE, services::file};
use memmap2::Mmap;

/// 应用的状态结构体
#[derive(Default)]
pub struct AppState {
    /// 文件路径
    pub file_path: Option<String>,
    /// 文件Mmap的只读内存映射
    mmap: Option<Mmap>,
    /// 文件是否是64位
    pub is_64_bit: Option<bool>,
}

impl AppState {
    /// 初始化打开文件
    pub fn init_open_file(&mut self, file_path: &str) -> Result<(), AppError> {
        self.file_path = Some(file_path.to_string());
        self.mmap = Some(file::mmap_file(file_path)?);
        // 读取PE格式来判断是否是64位
        let mmap: &Mmap = self.mmap.as_ref().unwrap();
        self.is_64_bit = Some(mmap.is_64_bit()?);
        Ok(())
    }

    /// close_file 关闭文件
    pub fn close_file(&mut self) -> Result<(), AppError> {
        self.file_path = None;
        self.mmap = None;
        self.is_64_bit = None;
        Ok(())
    }

    /// 获取文件内存映射的引用
    pub fn get_mmap_ref(&self) -> Result<&Mmap, AppError> {
        self.mmap.as_ref().ok_or(AppError::NoFileOpened)
    }

    /// 设置文件内存映射
    pub fn set_mmap(&mut self, mmap: Mmap) {
        self.mmap = Some(mmap);
    }

    /// 在指定偏移处写入数据，此举会同步修改文件内容
    pub fn write_data(&mut self, offset: usize, data: &[u8]) -> Result<(), AppError> {
        // 1. 重新以可读可写打开文件
        let file_path = self.file_path.as_ref().ok_or(AppError::NoFileOpened)?;
        let mut rw_mmap = file::mmap_file_rw(file_path)?;
        // 2. 向内存映射中写入数据
        rw_mmap[offset..(offset + data.len())].copy_from_slice(data);
        rw_mmap.flush()?;

        // 3. 重新打开文件
        self.mmap = Some(file::mmap_file(file_path)?);
        self.is_64_bit = Some(self.get_mmap_ref()?.is_64_bit()?);

        Ok(())
    }

    pub fn add_section(
        &mut self,
        section_name: &str,
        section_size: u32,
        section_characteristics: u32,
    ) -> Result<(), AppError> {
        if section_name.len() > 8 {
            // section_name不能超过8个字符
            return Err(AppError::SectionNameTooLong);
        }
        let mut section_name_bytes = [0u8; 8];
        section_name_bytes[..section_name.len()].copy_from_slice(section_name.as_bytes());
        let mmap = self.get_mmap_ref()?;
        let file_path = self.file_path.as_ref().ok_or(AppError::NoFileOpened)?;
        // 将section_size 进行文件对齐
        let file_alignment = mmap.align_size_with_file_alignment(section_size)?;
        // 判断是否可以添加节
        // 1. 判断头部空余空间是否足够
        const SIZE_OF_SECTION_HEADER: u32 = mem::size_of::<ImageSectionHeader>() as u32;
        let header_empty_space = mmap.get_header_empty_space_size()?;
        let mut rw_mmap_option = None;

        if header_empty_space < SIZE_OF_SECTION_HEADER {
            // 头部空余空间不足 需要判断是否可以拓展
            // 获取最大可拓展的大小
            let max_expand_size = mmap.get_header_max_expand_size()?;
            let file_alignment = mmap.align_size_with_file_alignment(SIZE_OF_SECTION_HEADER)?;
            if file_alignment > max_expand_size {
                return Err(AppError::CannotModifyFile("无法拓展文件大小!".to_string()));
            }
            // 否则可以通过拓展头部大小的方式进行节的添加
            // 1. 拓展文件大小
            expand_file_size(file_path, file_alignment)?;

            // 2. 以可读可写的方式重新映射文件
            rw_mmap_option = Some(file::mmap_file_rw(file_path)?);
            // 拓展头部大小 拓展一个节的大小
            rw_mmap_option.as_mut().unwrap().expand_headers(file_alignment)?;
        }
        // 2. 添加节
        // 先拓展文件大小
        expand_file_size(file_path, file_alignment)?;
        // 如果rw_mmap_option为None，则需要重新映射文件
        if rw_mmap_option.is_none() {
            rw_mmap_option = Some(file::mmap_file_rw(self.file_path.as_ref().unwrap())?);
        }
        let rw_mmap = rw_mmap_option.as_mut().unwrap();

        let characteristics =  SectionCharacteristics::from_bits(section_characteristics).unwrap();

        rw_mmap.add_section(&section_name_bytes, section_size, characteristics)?;

        // 3. 重新映射文件
        self.mmap = Some(file::mmap_file(self.file_path.as_ref().unwrap())?);

        Ok(())
    }
}