
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
use rand::Rng;
use reqwest::header::HeaderMap;
use reqwest::{Client, Response};
use scraper::{Html, Selector};
use std::error::Error;
use std::io::{self, Write};
use std::time::Duration;
use tokio::sync::Mutex;
use std::sync::Arc;
use tokio::{task, time};
use std::collections::BTreeMap;
extern crate log;
use serde_json::{self, Value};
use serde::Serialize;
use std::fs::{self, File};
use std::io::BufReader;
use dirs;
use std::path::Path;
use std::time::UNIX_EPOCH;
use std::io::Read;

pub mod wclip;
pub mod sys_res;
pub mod encrypt;

use sys_res::*;

#[derive(Debug, Serialize, Default)]
pub struct Config {
    user_name: String,
    password: String,
    cookies: String,
    magic: String,

    save_path: String,
}

impl Config {
    pub fn new(json_file: &str) -> Result<Config, Box<dyn Error>> {
        let file = File::open(json_file)?;
        let reader = BufReader::new(file);
        let json: Value = serde_json::from_reader(reader)?;
        let user_name = json.get("user_name").expect("not found user_name in info.json").as_str().unwrap().to_string();
        log::info!("user_name is {}", user_name);
        let mut password = json.get("password").expect("not found password in info.json").as_str().unwrap().to_string();
        let magic = match json.get("magic") {
            Some(magic) => magic.as_str().unwrap().to_string(),
            None => "~".to_string(),
        };
        let cookies = match json.get("cookies") {
            Some(cookies) => cookies.as_str().unwrap().to_string(),
            None => "".to_string(),
        };
        let gen_cfg = move |user_name:String, password:String|{
            let cfg = Config {
                user_name,
                password,
                cookies,
                magic,
                save_path: json_file.to_string()
            };
            cfg
        };
        drop(json);
        if encrypt::is_base64(&password) {
            let key = encrypt::get_key();
            let _ = encrypt::decrypt(&password, &key)?;
            Ok(gen_cfg(user_name, password))
        } else {
            let key = encrypt::get_key();
            password = encrypt::encrypt(&password, &key, &user_name);
            let cfg = gen_cfg(user_name, password);
            cfg.update();
            Ok(cfg)
        }
    }

    pub fn update(&self) {
        let json = serde_json::json!({
            "user_name": self.user_name,
            "password": self.password,
            "cookies": self.cookies,
            "magic": self.magic,
        });
        let json = serde_json::to_string_pretty(&json).unwrap();
        let mut f = File::options().truncate(true).write(true).create(true).open(&self.save_path).unwrap();
        let _ = f.write(json.as_bytes());
    }

    pub fn user_name(&mut self, value: Option<&str>) -> &str {
        if let Some(value) = value {
            self.user_name = value.to_string();
            self.update();
        }
        &self.user_name
    }
    pub fn password(&mut self, value: Option<&str>) -> String {
        if let Some(value) = value {
            let key = encrypt::get_key();
            self.password = encrypt::encrypt(&value, &key, &self.user_name);
            self.update();
        }
        let key = encrypt::get_key();
        encrypt::decrypt(&self.password, &key).expect("password cant decrypt")
    }

    pub fn cookies(&mut self, value: Option<&str>) -> &str {
        if let Some(value) = value {
            self.cookies = value.to_string();
            self.update();
        }
        &self.cookies
    }

    pub fn magic(&mut self, value: Option<&str>) -> &str {
        if let Some(value) = value {
            self.magic = value.to_string();
            self.update();
        }
        &self.magic
    }
}

pub struct ClipboardSync {
    web: Client,
    ip: String,
    clip: wclip::Wclip,
    cfg: Config,
    pipe: MsgSyncPipe,
    cursor: CursorCtrl,
    characters: Character,
    recent_msg: String,
}

const USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.42";

fn quote(input: &str) -> String {
    percent_encode(input.as_bytes(), NON_ALPHANUMERIC).to_string()
}

pub struct ClipboardMsgs {
    pub msgs: Vec<String>,
}

#[derive(Debug)]
enum MsgSyncState {
    Idle,
    FocusOnVM,
    WaitingSendMsg,
    WaitingRecMsg,
}

#[derive(Clone)]
pub enum Character {
    Producer,
    Consumer,
}
struct MsgSyncPipe {
    pub pipe_file: String,
    pub status_file: String,
    atime: Duration,
}

/*
 * 这里使用一个共享的pipe文件的atime来同步消息状态，
 * 当消费者请求消息时，将尝试访问pipe文件，更新访问时间。使得生产者知道此时应该要发送消息了。
 * 生产者尝试访问pipe文件发现atime被更新，说明此时有消费者请求发送消息，那么就发送消息并将atime更新,使得消费者知道此时有一条消息更新了，
 */
impl MsgSyncPipe {
    pub fn new(c:&Character) -> Result<MsgSyncPipe, Box<dyn Error>> {
        let (pipe_file,status_file) = match c {
            Character::Producer => Self::get_producer_pipe()?,
            Character::Consumer => Self::get_consumer_pipe()?,
        };
        let atime = Self::get_pipe_atime(&pipe_file);
        Ok(MsgSyncPipe {
            pipe_file,
            status_file,
            atime,
        })
    }

    fn get_pipe_atime(path:&str) -> Duration {
        let metadata = fs::metadata(path).unwrap();
        // 获取最后访问时间
        if let Ok(accessed) = metadata.accessed() {
            accessed.duration_since(UNIX_EPOCH).unwrap()
        } else {
            Duration::new(0, 0)
        }
    }

    pub fn is_atime_update(&mut self) -> bool {
        let atime = Self::get_pipe_atime(&self.pipe_file);
        log::trace!("pipe file access time: {:?}", atime);
        if atime > self.atime {
            self.atime = atime;
            return true;
        }
        return false;
    }

    /* 请求发送消息，向status文件写入请求状态 */
    pub fn request_msg(&mut self) {
        let mut file = File::options().write(true).open(&self.status_file).unwrap();
        let _ = file.write("REQUEST".as_bytes());
        drop(file);
    }
    /* 检查对方是否已经发送了消息 */
    pub fn is_sent_msg(&mut self) -> bool {
        self.is_atime_update()
    }
    /* 清除发送消息的状态，在知道对方已经发送了消息的时候进行清除 */
    pub fn clean_request(&mut self) {
        let mut file = File::options().write(true).open(&self.status_file).unwrap();
        let _ = file.write("IDLE".as_bytes());
        drop(file);
    }
    /* sync一下目前的pipe的atime，用来同步一下两边记录的值 */
    pub fn status_sync(&mut self) -> bool {
        self.is_atime_update()
    }

    pub fn is_request_msg(&mut self) -> bool {
        let mut file = File::options().read(true).open(&self.status_file).unwrap();
        let mut buffer = Vec::new();
        let _ = file.read_to_end(&mut buffer);
        let status = String::from_utf8(buffer).unwrap();
        if status == "REQUEST" {
            return true;
        }
        return false;
    }
    pub fn reply_msg(&mut self) {
        // read pipe file to update access time
        #[cfg(not(feature = "ekko_simulated"))]
        {
            print!("read pipe file to update access time");
            let mut file = File::options().read(true).open(&self.pipe_file).unwrap();
            let mut buffer = Vec::new();
            let _ = file.read_to_end(&mut buffer);
        }
        #[cfg(feature = "ekko_simulated")]
        {
            print!("write pipe file to update access time");
            let mut file = File::options().write(true).open(&self.pipe_file).unwrap();
            let _ = file.write("sstar_l0l3clip_sync".as_bytes());
            drop(file);
        }
        self.status_sync();
        log::trace!("pipe file access time updated: {:?}", self.atime);
    }

    fn get_consumer_pipe() -> Result<(String, String), Box<dyn Error>> {
        let path = match dirs::home_dir() {
            Some(path) => path,
            None => return Err("can not get user HOME dir".into()),
        };
        // 这里希望通过两个文件来实现同步，一个是共享的pipe 标志文件，一个是请求状态文件。
        // 共享的pipe标志文件用于标识有新的消息需要发送，请求状态文件用于记录当前请求状态。
        let path = path.join("sstar_l0l3clip_sync");
        let sync_pipe = path.join("sync_pipe");
        let req_status = path.join("req_status");
        if !path.exists() {
            fs::create_dir(&path)?;
            let mut file = fs::OpenOptions::new().create(true).write(true).open(&sync_pipe)?;
            let _ = file.write("sync_pipe".as_bytes());
            let mut file = fs::OpenOptions::new().create(true).write(true).open(&req_status)?;
            let _ = file.write("IDLE".as_bytes());
        }
        Ok((sync_pipe.to_str().unwrap().to_string(), req_status.to_str().unwrap().to_string()))
    }

    fn get_producer_pipe() -> Result<(String,String), Box<dyn Error>> {
        let path = match dirs::home_dir() {
            Some(path) => path,
            None => return Err("can not get home dir".into()),
        };
        let path = path.join("sstar_l0l3clip_sync").to_str().unwrap().to_string();
        // 切割一下将盘符去掉留下剩下的部分方便匹配
        let mut path = path.split(':').into_iter();
        let _ = path.next(); //ignore driver letter
        let sub_path = path.next().unwrap().to_string();
        for drive in 'C'..='Z' {
            let sub_path = format!("{}:{}", drive, sub_path);
            #[cfg(feature = "ekko_simulated")]
            let sub_path = sub_path.replace("baoyucang", "BYC10");
            log::trace!("trying to find pipe file in {}", sub_path);
            let sync_pipe = Path::new(&sub_path).join("sync_pipe");
            let req_status = Path::new(&sub_path).join("req_status");
            if req_status.exists() && sync_pipe.exists(){
                return Ok((sync_pipe.to_str().unwrap().to_string(), req_status.to_str().unwrap().to_string()))
            }
        };
        return Err("You should mapping l0 c:\\ to l3".into())
    }
}
#[cfg(feature = "ekko_simulated")]
static VM_EXE_NAME:&str = "mstsc.exe";
#[cfg(not(feature = "ekko_simulated"))]
static VM_EXE_NAME:&str = "vmware_view.exe";
// 同步获取消息的线程
async fn msg_reciver(ctx:Arc<Mutex<ClipboardSync>>) {
    let mut ctx = ctx.lock().await;
    let mut sta = MsgSyncState::Idle;
    let mut turn_time;
    fn on_vm() -> bool {
        let vm_exe_name =VM_EXE_NAME.to_string();
        let prog_name = get_foredround_window_name();
        prog_name == vm_exe_name
    }
    let mut state_time = time::Instant::now();
    ctx.pipe.status_sync();
    loop{
        // log::trace!("current state is {:?}", sta);
        match sta{
            MsgSyncState::Idle => {
                turn_time = 200;
                // 如果当前前台的窗口是VM的窗口说明鼠标在VM窗口内切换到等待移出的逻辑
                if on_vm() {
                    sta = MsgSyncState::FocusOnVM;
                    turn_time = 0;
                } else {
                    log::trace!("Idle, waiting for mouse move to vm window");
                }
            },
            MsgSyncState::FocusOnVM => {
                turn_time = 100;
                // 如果已经移出VM窗口，说明从L3切换到了L0，可能存在需要更新msg的情况那么就开始请求对方发送消息
                if !on_vm() {
                    turn_time = 0;
                    //请求远端发送消息
                    ctx.pipe.request_msg();
                    sta = MsgSyncState::WaitingSendMsg;
                    state_time = time::Instant::now();
                    log::trace!("waiting for remote send msg...");
                }
            },
            MsgSyncState::WaitingSendMsg => {
                turn_time = 50;
                // 如果这里检测到了对方更新了文件状态，说明其已经发送完毕消息，我们开始时接收。
                if ctx.pipe.is_sent_msg() {
                    ctx.pipe.clean_request();
                    sta = MsgSyncState::WaitingRecMsg;
                    turn_time = 0;
                } else {
                    if on_vm() {
                        // 如果鼠标移到了VM窗口，那么就切换
                        sta = MsgSyncState::FocusOnVM;
                        turn_time = 0;
                    }
                    //最多等待1秒钟，如果超过1秒钟还没收到消息，那么就切换到空闲状态
                    if time::Instant::now() - state_time > Duration::new(1, 0) {
                        sta = MsgSyncState::Idle;
                        turn_time = 0;
                    }
                }
            },
            MsgSyncState::WaitingRecMsg => {
                turn_time = 0;
                log::trace!("now we can recv msg");
                // 接收完毕消息后切换到新的一轮判断中来
                let _ = ctx.recv_msg().await;
                sta = MsgSyncState::Idle;
            },
        }
        if turn_time != 0 {
            time::sleep(Duration::new(0, turn_time*1000*1000)).await;
        }
    }
}

struct CursorCtrl{
    pub x: i32,
    pub y: i32,
    is_edge: bool,
    state: CursorState,
}

#[derive(PartialEq,Copy, Clone)]
enum CursorState {
    Standby,
    StandbyEdge,
    StandbyCenter,
    Move2Edge,
    Move2Center,
    MovingCenter,
    MovingEdge,
}

impl CursorCtrl{
    pub fn new() -> Self {
        let (x,y) = sys_res::cursor_pos();
        Self {
            x,
            y,
            is_edge: false,
            state: CursorState::Standby,
        }
    }

    pub fn on_edge(x:i32, y:i32) -> bool {
        let (width, height) = sys_res::screen_size();
        // let (x,y) = sys_res::cursor_pos();
        let margin = 20;
        if x < margin ||  y< margin || x > width - margin || y > height - margin {
            return true
        }
        log::trace!("cursor pos:({},{})", x, y);
        false
    }

    pub fn get_sta(&mut self) -> CursorState {
        let (x,y) = sys_res::cursor_pos();
        let mut is_moving = false;
        if x != self.x || y != self.y {
            is_moving = true;
            self.is_edge = CursorCtrl::on_edge(x,y);
            self.x = x;
            self.y = y;
        }
        self.state = match self.state {
            CursorState::Standby => {
                if is_moving {
                    if self.is_edge {
                        CursorState::MovingEdge
                    } else {
                        CursorState::MovingCenter
                    }
                } else {
                    if self.is_edge {
                        CursorState::StandbyEdge
                    } else {
                        CursorState::StandbyCenter
                    }
                }
            },
            CursorState::StandbyEdge => {
                if is_moving {
                    if self.is_edge {
                        CursorState::MovingEdge
                    } else {
                        CursorState::Move2Center
                    }
                } else {
                    CursorState::StandbyEdge
                }
            },
            CursorState::StandbyCenter => {
                if is_moving {
                    if self.is_edge {
                        CursorState::Move2Edge
                    } else {
                        CursorState::MovingCenter
                    }
                } else {
                    CursorState::StandbyCenter
                }
            },
            CursorState::Move2Edge => {
                if is_moving {
                    if self.is_edge {
                        CursorState::MovingEdge
                    } else {
                        CursorState::Move2Center
                    }
                } else {
                    CursorState::StandbyEdge
                }
            },
            CursorState::Move2Center => {
                if is_moving {
                    if self.is_edge {
                        CursorState::Move2Edge
                    } else {
                        CursorState::MovingCenter
                    }
                } else {
                    CursorState::StandbyCenter
                }
            },
            CursorState::MovingCenter => {
                if is_moving {
                    if self.is_edge {
                        CursorState::Move2Edge
                    } else {
                        CursorState::MovingCenter
                    }
                } else {
                    CursorState::StandbyCenter
                }
            },
            CursorState::MovingEdge => {
                if is_moving {
                    if self.is_edge {
                        CursorState::MovingEdge
                    } else {
                        CursorState::Move2Center
                    }
                } else {
                    CursorState::StandbyEdge
                }
            },
        };
        self.state
    }
}

// 发送消息的线程
async fn msg_sender(ctx:Arc<Mutex<ClipboardSync>>) {
    let mut ctx = ctx.lock().await;
    let mut sta = MsgSyncState::Idle;
    let mut turn_time;
    let mut state_time = time::Instant::now();
    loop{
        // log::trace!("current state is {:?}", sta);
        match sta{
            MsgSyncState::Idle => {
                turn_time = 0;
                // 更新下最新的文件状态，保证后续判断准确
                ctx.pipe.status_sync(); // refresh pipe file status
                sta = MsgSyncState::FocusOnVM;
            },
            MsgSyncState::FocusOnVM => {
                let cursor = ctx.cursor.get_sta();
                match cursor {
                    CursorState::MovingCenter => {
                        turn_time = 200;
                        state_time = time::Instant::now();
                    },
                    CursorState::Move2Center => {
                        turn_time = 200;
                    },
                    CursorState::StandbyCenter => {
                        turn_time = 100;
                        if state_time.elapsed() > Duration::new(1, 0) {
                            turn_time = 200;
                        }
                    },
                    CursorState::MovingEdge => {
                        turn_time = 100;
                        state_time = time::Instant::now();
                    },
                    CursorState::Move2Edge => {
                        turn_time = 100;
                    },
                    // 当前处于窗口边缘，尝试切换到发送消息的逻辑：等待对方请求发送
                    CursorState::StandbyEdge => {
                        turn_time = 50;
                        if state_time.elapsed() > Duration::new(1, 0) {
                            turn_time = 200;
                        }
                    },
                    _ => {
                        turn_time = 100;
                    }
                }
                if ctx.pipe.is_request_msg() {
                    sta = MsgSyncState::WaitingSendMsg;
                    turn_time = 0;
                }
            },
            MsgSyncState::WaitingSendMsg => {
                let _ = ctx.clip2msg().await;
                log::trace!("send msg done");
                turn_time = 0;
                sta = MsgSyncState::WaitingRecMsg;
                ctx.pipe.reply_msg();
            },
            MsgSyncState::WaitingRecMsg => {
                turn_time = 0;
                sta = MsgSyncState::Idle;
                // 发送消息后鼠标还是保持在边缘说明当前鼠标还是在VM窗口外面，需要等待鼠标移动到VM窗口内，然后再发送消息
                let cursor = ctx.cursor.get_sta();
                if cursor == CursorState::Standby ||
                    cursor == CursorState::StandbyEdge  ||
                    cursor == CursorState::StandbyCenter {
                    turn_time = 100;
                    sta = MsgSyncState::WaitingRecMsg;
                }
            },
        }
        if turn_time != 0 {
            time::sleep(Duration::new(0, turn_time*1000*1000)).await;
        }
    }
}

pub async fn start_msg_sync(ctx:Arc<Mutex<ClipboardSync>>) -> Result<task::JoinHandle<()>, Box<dyn Error>>{
    match ctx.lock().await.characters {
        Character::Consumer => {
            let handle = task::spawn(msg_reciver(ctx.clone()));
            Ok(handle)
        },
        Character::Producer => {
            let handle = task::spawn(msg_sender(ctx.clone()));
            Ok(handle)
        },
    }
}

enum Msg <'a>{
    // magic str, message context
    Msg(&'a str),
    //index of message pack, total message pack number, magic str, message context
    Sub((u8, u8, String, &'a str)),
}

impl ClipboardSync {
    pub fn new(ip: &str, info_file:&str, c:&Character) -> Result<ClipboardSync, Box<dyn Error>> {
        Ok(ClipboardSync {
            web: Client::new(),
            ip: ip.to_string(),
            clip: wclip::Wclip::new(),
            cfg: Config::new(info_file)?,
            pipe: MsgSyncPipe::new(c)?,
            cursor: CursorCtrl::new(),
            characters: c.clone(),
            recent_msg: String::new(),
        })
    }


    async fn verify(&mut self) -> Result<Response, Box<dyn Error>> {
        // 开始验证
        let security_check_url = format!("http://{}/SSWeb/rd/j_security_check", self.ip);
        let mut headers = HeaderMap::new();
        headers.insert("Host", "ssweb".parse().unwrap());
        headers.insert("Connection", "keep-alive".parse().unwrap());
        headers.insert("Cache-Control", "max-age=0".parse().unwrap());
        headers.insert("Origin", "http://ssweb".parse().unwrap());
        headers.insert("DNT", "1".parse().unwrap());
        headers.insert("Upgrade-Insecure-Requests", "1".parse().unwrap());
        headers.insert(
            "Content-Type",
            "application/x-www-form-urlencoded".parse().unwrap(),
        );
        headers.insert("User-Agent", USER_AGENT.parse().unwrap());
        headers.insert("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9".parse().unwrap());
        headers.insert(
            "Referer",
            "http://ssweb/SSWeb/rd/copy_paste.jsp".parse().unwrap(),
        );
        headers.insert("Accept-Encoding", "gzip, deflate".parse().unwrap());
        headers.insert(
            "Accept-Language",
            "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"
                .parse()
                .unwrap(),
        );
        headers.insert("Cookie", self.cfg.cookies(None).parse().unwrap());
        let contents = format!(
            "j_username={}&j_password={}",
            quote(self.cfg.user_name(None)),
            quote(&self.cfg.password(None))
        );
        let resp = self
            .web
            .post(security_check_url)
            .headers(headers)
            .body(contents)
            .send()
            .await?;
        Ok(resp)
    }
    fn get_cookies(response: &Response) -> String {
        let mut session = String::new();
        let headers: &HeaderMap = response.headers();
        if let Some(header_value) = headers.get("Set-Cookie") {
            if let Ok(value) = header_value.to_str() {
                log::info!("Set-Cookie: {}", value);
                session = value.split(';').next().unwrap().to_string();
            }
        } else {
            log::warn!("cookies not found");
        }
        session
    }
    pub async fn login(&mut self) -> Result<(), Box<dyn Error>> {
        let mut headers = HeaderMap::new();
        headers.insert("User-Agent", USER_AGENT.parse().unwrap());
        let url = format!("http://{}/SSWeb/rd/copy_paste.jsp", self.ip);

        let response = self.web.get(url).headers(headers).send().await?;
        self.cfg.cookies(Some(&ClipboardSync::get_cookies(&response)));
        log::info!("cookies is [{}]", self.cfg.cookies(None));
        let mut resp = response.text().await?;
        log::trace!("Response: {}", resp);
        if resp.contains("loginForm") {
            let response = self.verify().await?;
            resp = response.text().await?;
            log::trace!("verify Response: {}", resp);
        }
        if resp.contains("<title>Copy & Paste</title>") {
            Ok(())
        } else {
            Err(Box::new(io::Error::new(io::ErrorKind::Other, "登录失败")))
        }
    }

    pub async fn get_newest_msg(&mut self) -> Result<String, Box<dyn Error>> {
        //http://ssweb/SSWeb/rd/copy_paste.jsp?d-16544-p=1&d-16544-s=2
        //d-16544-s 2:时间从靠近现在开始排序 1:时间从最早开始排序
        //d-16544-o mesage排序，不要设置
        //d-16544-p=1 页码
        let url = format!("http://{}/SSWeb/rd/copy_paste.jsp?d-16544-p=1&d-16544-s=2", self.ip);
        let mut headers = HeaderMap::new();
        headers.insert("Host", "ssweb".parse().unwrap());
        headers.insert("Connection", "keep-alive".parse().unwrap());
        headers.insert("Cache-Control", "max-age=0".parse().unwrap());
        headers.insert("DNT", "1".parse().unwrap());
        headers.insert("Upgrade-Insecure-Requests", "1".parse().unwrap());
        headers.insert("User-Agent", USER_AGENT.parse().unwrap());
        headers.insert("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9".parse().unwrap());
        headers.insert("Accept-Encoding", "gzip, deflate".parse().unwrap());
        headers.insert("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6".parse().unwrap());
        let response = self.web.get(url).headers(headers).send().await?;
        let resp = response.text().await?;
        Ok(resp)
    }

    pub async fn recv_msg(&mut self) -> Result<(), Box<dyn Error>> {
        let resp = self.get_newest_msg().await?;
        self.msg2clip(&resp).await;
        Ok(())
    }

    //解析可能存在的消息数据
    fn parse_msg<'a>(msg: &'a str) -> Msg {
        //x|x|x| -> index|total|magic|
        let bmsg = msg.as_bytes();
        let head = &bmsg[0..7];
        let check = head.split(|c| *c == b'|').filter(|text| {
            if text.len() == 1 {
                if text[0] >= b'!' && text[0] < b'|' {true} else {false}
            } else {
                false
            }
        }).map(|x|x[0] - b'!').collect::<Vec<_>>();

        if check.len() != 3 {
            return Msg::Msg(msg)
        }

        return Msg::Sub((check[0], check[1], check[2].to_string(), &msg[7..]));
    }

    //将message解析并放置到剪切板上
    pub async fn msg2clip(&mut self, html: &str) {
        // log::info!("html: {}", html);
        let document = Html::parse_document(html);
        // 所有剪切板的数据都是在code这个html lable上的
        let selector = Selector::parse("code").unwrap();
        //对于单个消息我们希望发送的时间作为magic，所以这里通过构建一个date selector获得
        let date_selector = Selector::parse("td.fixline").unwrap();
        let mut msg_map = BTreeMap::new();
        let mut magic_ref = "0xff".to_string();
        let mut msg_index = 0;
        for element in document.select(&selector).into_iter() {
            //let text = element.text().collect::<Vec<_>>().concat();
            let text = element.text().collect::<String>();
            log::trace!("Found: [{}]", text);
            let msg = ClipboardSync::parse_msg(text.as_str());
            match msg {
                //一整个完整的消息。直接复制到剪切板即可
                Msg::Msg(msg) => {
                    if magic_ref == "0xff" {
                        // 因为每个date和str都是对应的所以这里直接获取指定位置的date作为magic
                        let magic = document.select(&date_selector).into_iter().nth(msg_index).unwrap().text().collect::<String>();
                        if self.cfg.magic(None) != magic {
                            self.clip.set(msg);
                            self.cfg.magic(Some(&magic));
                            log::info!("magic is {}, msg is {}", self.cfg.magic(None), msg);
                        }
                        break;//处理完毕。直接退出
                    }
                },
                Msg::Sub((index, total, magic, body)) => {
                    if magic_ref == "0xff" {
                        magic_ref = magic;
                    } else {
                        if magic_ref != magic { // other message ignore
                            msg_index += 1;
                            continue
                        }
                    }
                    if msg_map.len() == 0 && (
                        self.cfg.magic(None) == magic_ref // message is repeated ignore it
                        || (msg_index != 0) // we only process the newest msg
                    ) {
                        break;
                    }
                    // same msg pack, append to list
                    msg_map.insert(index, body.to_string());
                    if msg_map.len() as u8 == total {
                        let msg = msg_map.into_iter().map(|(_key, v)| v).collect::<String>();
                        if self.cfg.magic(None) != magic_ref {
                            log::info!("magic is {}, msg is {}", self.cfg.magic(None), msg);
                            self.clip.set(&msg);
                            self.cfg.magic(Some(&magic_ref)); // save for next msg
                        }
                        break;
                    }
                }
            }
            msg_index += 1;
        }
    }

    async fn send_msg(&mut self, msg: &str) -> Result<(), Box<dyn Error>> {
        let url = format!("http://{}/SSWeb/rd/copy_paste.jsp?send=y", self.ip);
        let mut headers = HeaderMap::new();
        headers.insert("Host", "ssweb".parse().unwrap());
        headers.insert("Connection", "keep-alive".parse().unwrap());
        headers.insert("Cache-Control", "max-age=0".parse().unwrap());
        headers.insert("Origin", "http://ssweb".parse().unwrap());
        headers.insert("DNT", "1".parse().unwrap());
        headers.insert("Upgrade-Insecure-Requests", "1".parse().unwrap());
        headers.insert("Content-Type", "application/x-www-form-urlencoded".parse().unwrap());
        headers.insert("User-Agent", USER_AGENT.parse().unwrap());
        headers.insert("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9".parse().unwrap());
        headers.insert("Referer", "http://ssweb/SSWeb/rd/copy_paste.jsp?d-16544-p=1&d-16544-s=2".parse().unwrap());
        headers.insert("Accept-Encoding", "gzip, deflate".parse().unwrap());
        headers.insert("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6".parse().unwrap());
        headers.insert("Cookie", format!("{}", self.cfg.cookies(None)).parse().unwrap());

        let contents = format!("message={}", msg);
        let _ = self.web.post(url).headers(headers).body(contents).send().await?;
        // let resp = response.text().await?;
        // Ok(resp)
        Ok(())
    }

    async fn split_msg(&mut self, msg: &str) -> Result<Vec<String>, i32>  {
        let mut start = 0;
        let mut msgs = Vec::new();
        let mut cur = 0;
        for c in msg.chars() {
            let byte_count = c.len_utf8();
            if cur - start + byte_count + 7 >= 300 {
                //超过限额300个字符，构造一个消息进行发送
                msgs.push(&msg[start..cur]);
                start = cur;
                if msgs.len() > b'|' as usize - b'!' as usize {
                    log::warn!("msg is too long, ignore it");
                    return Err(-1);
                }
            }
            cur += byte_count;
        }
        //如果拆包了的话。那么要把最后一点点数据打包一下，不然会丢msg
        if msgs.len() > 0 && cur != start {
            msgs.push(&msg[start..cur]);
        }
        fn gen_magic(cache: char) -> char {
            let mut rng = rand::thread_rng();
            // ASCII 范围从 '!' (33) 到 '|' (124)
            let start = b'!' as u8;
            let end = b'|' as u8;
            let mut ret = cache;
            //随机生成一个和上次不一样的值即可
            while ret == cache {
                ret = rng.gen_range(start..end) as char;
            }
            ret
        }
        let mut ret = Vec::new();
        let total = msgs.len() as u32;
        if total > 0 { //大消息切片为多个小消息
            let magic = gen_magic(self.cfg.magic(None).chars().next().unwrap());
            for index in 0..total {
                //x|x|x| -> index|total|magic|
                let msg = format!("{}|{}|{}", char::from_u32(b'0' as u32 + index).unwrap(),
                    char::from_u32(b'0' as u32 + total).unwrap(), magic);
                ret.push(msg + msgs[index as usize]);
            }
            self.cfg.magic(Some(&magic.to_string()));
        } else { //单个消息
            ret.push(msg.to_string());
        }
        Ok(ret)
    }
    pub async fn clip2msg(&mut self) {
        let msg = match self.clip.get() {
            Some(msg) => {
                let split = match self.split_msg(&msg).await {
                    Ok(msgs) => msgs,
                    Err(_) => return,
                };
                if self.recent_msg != msg {
                    self.recent_msg = msg;
                    split
                } else {
                    log::info!("msg is same as last time, ignore it");
                    Vec::<String>::new()
                }
            },
            None => {
                log::info!("clipboard is not string, ignore it");
                return
            },
        };
        //遍历所有分包并进行发送
        for item in msg {
            let _ = self.send_msg(&item).await;
        }
    }
}

#[tokio::test]
async fn test_message2clip()
{
    use std::fs::File;
    use std::io::Read;
    let mut ins = ClipboardSync::new("127.0.0.1", "info.json", &Character::Producer).unwrap();
    let mut html = String::new();
    File::open("message.html").unwrap().read_to_string(&mut html).unwrap();
    ins.msg2clip(&html).await;
}

#[tokio::test]
async fn test_start_msg_sync()
{
    let ins = ClipboardSync::new("127.0.0.1:5000", "info.json", &Character::Producer).unwrap();
    let ctx = Arc::new(Mutex::new(ins));
    let handle = start_msg_sync(ctx.clone()).await.unwrap();
    time::sleep(Duration::new(10, 0)).await;
    handle.abort();
}

#[tokio::test]
async fn test_split_msg() {
    let mut ins = ClipboardSync::new("127.0.0.1:5000", "info.json", &Character::Producer).unwrap();
    let raw_msg = "在这个快速发展的世界中，technology is evolving at an unprecedented rate! 我们需要不断学习和适应新的变化。#创新@未来$梦想%实现^挑战&机遇*合作+成长=成功~体验|分享<知识>探索{可能性}。Let's work together to create a better tomorrow! 生活充满了各种可能性，我们要勇敢面对每一个挑战。1234567890!@#$%^&*()_+-=<>?[]{},.。；：‘’“”（）【】《》——";
    let msgs = ins.split_msg(raw_msg).await.unwrap();
    println!("{:?}", msgs);
}

#[tokio::test]
async fn test_send_send_msg() {
    let mut ins = ClipboardSync::new("127.0.0.1:5000", "info.json", &Character::Producer).unwrap();
    let raw_msg = "在这个快速发展的世界中，technology is evolving at an unprecedented rate! 我们需要不断学习和适应新的变化。#创新@未来$梦想%实现^挑战&机遇*合作+成长=成功~体验|分享<知识>探索{可能性}。Let's work together to create a better tomorrow! 生活充满了各种可能性，我们要勇敢面对每一个挑战。1234567890!@#$%^&*()_+-=<>?[]{},.。；：‘’“”（）【】《》——";
    ins.clip.set(&raw_msg);
    ins.clip2msg().await;
}