/*
 * Copyright (C) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
//! daemon
#![allow(missing_docs)]

pub mod auth;
pub mod daemon_app;
pub mod daemon_unity;
pub mod mount;
pub mod shell;
pub mod task;
pub mod task_manager;
pub mod sys_para;

#[cfg(feature = "emulator")]
pub mod bridge;

use std::io::{self, ErrorKind};
use std::sync::Arc;
use std::ffi::c_int;
use crate::utils::{self, hdc_log::*};

use crate::common::jdwp::Jdwp;
use crate::config;
use crate::config::TaskMessage;
#[cfg(feature = "emulator")]
use crate::daemon_lib::bridge;
use crate::transfer;
#[cfg(not(feature = "emulator"))]
use crate::transfer::base::Reader;
#[cfg(not(feature = "emulator"))]
use crate::transfer::uart::UartReader;
#[cfg(not(feature = "emulator"))]
use crate::transfer::uart_wrapper;
use crate::transfer::buffer::DiedSession;

use crate::daemon_lib::sys_para::*;
use std::ffi::CString;
#[cfg(not(feature = "emulator"))]
use ylong_runtime::net::{TcpListener, TcpStream};
#[cfg(not(feature = "emulator"))]
use ylong_runtime::sync::mpsc;

extern "C" {
    #[cfg(not(feature = "emulator"))]
    fn NeedDropRootPrivileges() -> c_int;
}

#[cfg(not(feature = "emulator"))]
pub fn need_drop_root_privileges() {
    crate::info!("need_drop_root_privileges");
    unsafe {
        NeedDropRootPrivileges();
    }
}

pub async fn handle_message(res: io::Result<TaskMessage>, session_id: u32) -> io::Result<()> {
    match res {
        Ok(msg) => {
            utils::spawn(async move {
                if let Err(e) = task::dispatch_task(msg, session_id).await {
                    crate::error!("dispatch tcp task failed: {}", e.to_string());
                }
            });
        }
        Err(e) => {
            crate::debug!("clear pty map: {}", session_id);
            if e.kind() == ErrorKind::Other {
                crate::warn!("unpack task failed: {}", e.to_string());
                return Err(e);
            }
        }
    };
    Ok(())
}

pub async fn jdwp_daemon_start(lock_value: Arc<Jdwp>) {
    lock_value.init().await;
}

#[cfg(feature = "emulator")]
pub async fn bridge_daemon_start() -> io::Result<()> {
    crate::info!("bridge_daemon_start start...");
    let ptr = bridge::init_bridge() as u64;
    crate::info!("bridge_daemon_start ptr:{}", ptr);
    let pipe_read_fd = bridge::start_listen(ptr);
    crate::info!("bridge_daemon_start pipe_read_fd:{}", pipe_read_fd);
    if pipe_read_fd < 0 {
        crate::error!("daemon bridge listen fail.");
        return Err(std::io::Error::new(
            ErrorKind::Other,
            "daemon bridge listen fail.",
        ));
    }
    loop {
        crate::info!("bridge_daemon_start loop...");
        let client_fd_for_hdc_server = bridge::accept_server_socket_fd(ptr, pipe_read_fd);
        if client_fd_for_hdc_server < 0 {
            crate::error!("bridge_daemon_start accept client fd for hdc server fail...");
            break;
        }
        let client_fd = bridge::init_client_fd(ptr, client_fd_for_hdc_server);
        if client_fd < 0 {
            crate::error!("bridge_daemon_start init client fd fail...");
            break;
        }
        utils::spawn(bridge_handle_client(
            ptr,
            client_fd,
            client_fd_for_hdc_server,
        ));
    }
    bridge::stop(ptr);
    Ok(())
}

#[cfg(feature = "emulator")]
pub async fn bridge_handle_client(ptr: u64, fd: i32, client_fd: i32) -> io::Result<()> {
    crate::info!("bridge_handle_client start...");
    let rd = bridge::BridgeReader { ptr, fd };
    let wr = bridge::BridgeWriter { ptr, fd };
    let recv_msg = bridge::unpack_task_message(&rd).await?;
    let (session_id, send_msg) = auth::handshake_init(recv_msg).await?;
    let channel_id = send_msg.channel_id;
    bridge::BridgeMap::start(session_id, wr).await;
    transfer::put(session_id, send_msg).await;

    if auth::AuthStatusMap::get(session_id).await == auth::AuthStatus::Ok {
        transfer::put(
            session_id,
            TaskMessage {
                channel_id,
                command: config::HdcCommand::KernelChannelClose,
                payload: vec![0],
            },
        )
        .await;
    }

    loop {
        let ret = handle_message(transfer::tcp::unpack_task_message(&rd).await, session_id).await;
        if ret.is_err() {
            unsafe {
                libc::close(fd);
                libc::close(client_fd);
            }
            break;
        }
    }
    Ok(())
}

#[cfg(not(feature = "emulator"))]
pub async fn tcp_handle_client(stream: TcpStream) -> io::Result<()> {
    let (mut rd, wr) = stream.into_split();
    let msg = transfer::tcp::unpack_task_message(&mut rd).await?;
    let session_id = auth::get_session_id_from_msg(&msg).await?;
    crate::info!(
        "tcp_handle_client session_id {session_id}, channel_id {}",
        msg.channel_id
    );
    transfer::TcpMap::start(session_id, wr).await;
    let ret = handle_message(Ok(msg), session_id).await;
    if ret.is_err() {
        transfer::TcpMap::end(session_id).await;
        return ret;
    }

    loop {
        let result = handle_message(
            transfer::tcp::unpack_task_message(&mut rd).await,
            session_id,
        )
        .await;
        if result.is_err() {
            crate::warn!("tcp free_session, session_id:{}, result:{:?}", session_id, result);
            task_manager::free_session(session_id).await;
            return result;
        }
    }
}

#[cfg(not(feature = "emulator"))]
pub async fn tcp_daemon_start(port: u16) -> io::Result<()> {
    crate::info!("tcp_daemon_start port = {:#?}", port);
    let saddr = format!("0.0.0.0:{}", port);
    let listener = TcpListener::bind(saddr.clone()).await?;
    let random_port = listener.local_addr()?.port();
    crate::info!(
        "daemon binds on saddr = {:#?}, port = {:#?}",
        saddr,
        random_port
    );
    if !set_dev_item(config::ENV_HOST_PORT, &random_port.to_string()) {
        crate::error!("set tcp port: {} failed.", port);
    }
    loop {
        let (stream, addr) = listener.accept().await?;
        crate::info!("accepted client {addr}");
        utils::spawn(async {
            if let Err(e) = tcp_handle_client(stream).await {
                crate::error!("tcp_handle_client {e:?}");
            }
        });
    }
}

#[allow(unused)]
#[cfg(not(feature = "emulator"))]
pub async fn uart_daemon_start() -> io::Result<()> {
    loop {
        let fd = transfer::uart::uart_init()?;
        if let Err(e) = uart_handle_client(fd).await {
            crate::error!("uart_handle_client failed, {:?}", e);
        }
        transfer::uart::uart_close(fd);
    }
}

#[cfg(not(feature = "emulator"))]
pub async fn uart_handshake(
    handshake_message: TaskMessage,
    fd: i32,
    rd: &UartReader,
    package_index: u32,
) -> io::Result<u32> {
    let (session_id, send_msg) = auth::handshake_init(handshake_message).await?;
    let channel_id = send_msg.channel_id;

    let wr = transfer::uart::UartWriter { fd };
    transfer::start_uart(session_id, wr).await;
    transfer::start_session(session_id).await;

    let Some(head) = rd.head.clone() else {
        return Err(std::io::Error::new(
            ErrorKind::Other,
            "rd head clone failed",
        ));
    };
    uart_wrapper::on_read_head(head).await;
    transfer::wrap_put(session_id, send_msg, package_index, 0).await;

    if auth::AuthStatusMap::get(session_id).await == auth::AuthStatus::Ok {
        transfer::put(
            session_id,
            TaskMessage {
                channel_id,
                command: config::HdcCommand::KernelChannelClose,
                payload: vec![0],
            },
        )
        .await;
    }
    Ok(session_id)
}

#[cfg(not(feature = "emulator"))]
pub async fn uart_handle_client(fd: i32) -> io::Result<()> {
    let mut rd = transfer::uart::UartReader { fd, head: None };
    let (packet_size, package_index, _session_id) = rd.check_protocol_head()?;
    let (tx, mut rx) = mpsc::bounded_channel::<TaskMessage>(config::USB_QUEUE_LEN);
    utils::spawn(async move {
        let mut rd = transfer::uart::UartReader { fd, head: None };
        if let Err(e) =
            transfer::base::unpack_task_message_lock(&mut rd, packet_size, tx.clone()).await
        {
            crate::warn!("unpack task failed: {}, reopen fd...", e.to_string());
        }
    });
    let session_id;
    match rx.recv().await {
        Ok(handshake_message) => {
            let _ = rx.recv().await;
            crate::info!("uart handshake_message:{:?}", handshake_message);
            session_id = uart_handshake(handshake_message.clone(), fd, &rd, package_index).await?;
        }
        Err(e) => {
            crate::info!("uart handshake error, {e:?}");
            return Err(std::io::Error::new(
                ErrorKind::Other,
                format!("uart recv handshake error, {e:?}"),
            ));
        }
    }

    uart_wrapper::stop_other_session(session_id).await;
    let mut real_session_id = session_id;
    loop {
        let (packet_size, _package_index, _session_id) = rd.check_protocol_head()?;
        let Some(head) = rd.head.clone() else {
            return Err(std::io::Error::new(ErrorKind::Other, "rd head clone file"));
        };
        let package_index = head.package_index;
        let session_id = head.session_id;
        uart_wrapper::on_read_head(head).await;
        if real_session_id != session_id {
            crate::info!("real_session_id:{real_session_id}, session_id:{session_id}");
            uart_wrapper::stop_other_session(session_id).await;
        }
        if packet_size == 0 {
            continue;
        }

        let (tx, mut rx) = mpsc::bounded_channel::<TaskMessage>(config::USB_QUEUE_LEN);
        utils::spawn(async move {
            let mut rd = transfer::uart::UartReader { fd, head: None };
            if let Err(e) =
                transfer::base::unpack_task_message_lock(&mut rd, packet_size, tx.clone()).await
            {
                crate::warn!("uart read uart taskmessage error:{:?}", e);
            }
        });

        loop {
            match rx.recv().await {
                Ok(message) => {
                    if message.command == config::HdcCommand::UartFinish {
                        break;
                    }

                    if message.command == config::HdcCommand::KernelHandshake {
                        real_session_id =
                            uart_handshake(message.clone(), fd, &rd, package_index).await?;
                        crate::info!("real_session_id:{real_session_id:?}");
                        continue;
                    }
                    let command = message.command;
                    utils::spawn(async move {
                        if let Err(e) = task::dispatch_task(message, real_session_id).await {
                            log::error!("dispatch task({:?}) fail: {:?}", command, e);
                        }
                    });
                }
                Err(e) => {
                    let error_msg = format!("uart recv error: {e:?}");
                    crate::info!("{error_msg}");
                    return Err(std::io::Error::new(ErrorKind::Other, error_msg));
                }
            }
        }
    }
}

#[cfg(not(feature = "emulator"))]
pub async fn usb_daemon_start() -> io::Result<()> {
    loop {
        let ret = transfer::usb::usb_init();
        match ret {
            Ok((config_fd, bulkin_fd, bulkout_fd)) => {
                let _ = usb_handle_client(config_fd, bulkin_fd, bulkout_fd).await;
                transfer::usb::usb_close(config_fd, bulkin_fd, bulkout_fd);
            }
            Err(e) => {
                crate::error!("usb init failure and restart hdcd error is {:?}", e);
                std::process::exit(0);
            }
        }
    }
}

#[cfg(not(feature = "emulator"))]
pub async fn usb_handle_client(_config_fd: i32, bulkin_fd: i32, bulkout_fd: i32) -> io::Result<()> {
    let _rd = transfer::usb::UsbReader { fd: bulkin_fd };
    let mut rx = transfer::usb_start_recv(bulkin_fd, 0);
    let mut cur_session_id = 0;
    loop {
        match rx.recv().await {
            Ok((msg, _index, this_session_id)) => {
                if msg.command == config::HdcCommand::KernelHandshake {
                    if let Ok(session_id_in_msg) = auth::get_session_id_from_msg(&msg).await {
                        if session_id_in_msg != cur_session_id {
                            task_manager::free_session(cur_session_id).await;
                            crate::info!("free session(usb) over {:?} and new session is {}", cur_session_id, session_id_in_msg);
                            let wr = transfer::usb::UsbWriter { fd: bulkout_fd };
                            transfer::UsbMap::start(session_id_in_msg, wr).await;
                            cur_session_id = session_id_in_msg;
                        }
                    }
                }
                if DiedSession::get(this_session_id).await {
                    crate::error!("session is not connected, command:{:?}, this session:{this_session_id},
                        current session:{cur_session_id}", msg.command);
                    continue;
                }
                utils::spawn(async move {
                    if let Err(e) = task::dispatch_task(msg, cur_session_id).await {
                        crate::error!("dispatch task failed: {}", e.to_string());
                    }
                });
            }
            Err(e) => {
                crate::warn!("unpack task failed: {}", e.to_string());
                break;
            }
        }
    }
    task_manager::free_session(cur_session_id).await;
    Ok(())
}

#[cfg(not(feature = "emulator"))]
pub fn get_tcp_port() -> u16 {
    let (ret, host_port) = get_dev_item(config::ENV_HOST_PORT, "_");
    if !ret || host_port == "_" {
        crate::error!(
            "get host port failed, will use default port {}.",
            config::DAEMON_PORT
        );
        return config::DAEMON_PORT;
    }

    let str = host_port.trim();
    crate::info!("get_tcp_port from prop, value:{}", str);
    let mut end = str.len();
    for i in 0..str.len() {
        let c = str.as_bytes()[i];
        if !c.is_ascii_digit() {
            end = i;
            break;
        }
    }
    let str2 = str[0..end].to_string();
    let number = str2.parse::<u16>();
    if let Ok(num) = number {
        crate::info!("get host port:{} success", num);
        return num;
    }

    crate::error!(
        "convert host port failed, will use default port {}.",
        config::DAEMON_PORT
    );
    config::DAEMON_PORT
}