/*
 * 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.
 */
//! task
#![allow(missing_docs)]

use crate::daemon_lib::{auth, daemon_unity};

use crate::daemon_lib::shell::*;

use crate::daemon_lib::daemon_app::{self, AppTaskMap, DaemonAppTask};
use crate::daemon_lib::sys_para::*;
use crate::utils::hdc_log::*;
use crate::common::forward::{self, ForwardTaskMap, HdcForward};
use crate::common::hdcfile::{self, FileTaskMap, HdcFile};
use crate::common::jdwp;
use crate::config::*;
use crate::transfer;

use std::io::{self, Error, ErrorKind};

async fn daemon_shell_task(task_message: TaskMessage, session_id: u32) -> io::Result<()> {
    match task_message.command {
        HdcCommand::ShellInit => {
            let pty_task = PtyTask::new(
                session_id,
                task_message.channel_id,
                None,
                HdcCommand::KernelEchoRaw,
            );
            PtyMap::put(session_id, task_message.channel_id, pty_task).await;
        }
        HdcCommand::UnityExecute => {
            match String::from_utf8(task_message.payload) {
                Ok(cmd_str) => {
                    let shell_execute_task: ShellExecuteTask = ShellExecuteTask::new(
                        session_id,
                        task_message.channel_id,
                        cmd_str,
                        HdcCommand::KernelEchoRaw,
                    );
                    
                    ShellExecuteMap::put(session_id, task_message.channel_id, shell_execute_task).await;
                },
                Err(_) => {
                    crate::common::hdctransfer::echo_client(
                        session_id,
                        task_message.channel_id,
                        "only support utf-8 chars",
                        MessageLevel::Fail,
                    )
                    .await;
                    let message = TaskMessage {
                        channel_id: task_message.channel_id,
                        command: HdcCommand::KernelChannelClose,
                        payload: [1].to_vec(),
                    };
                    let _ = daemon_channel_close(message, session_id).await;
                    return Err(Error::new(ErrorKind::Other, "Get an FromUtf8Error"));
                }
            }
        }
        _ => {
            let channel_id = task_message.channel_id;
            if let Some(pty_task) = PtyMap::get(session_id, channel_id).await {
                let _ = &pty_task.tx.send(task_message.payload.clone()).await;
                if task_message.payload[..].contains(&0x4_u8) {
                    PtyMap::del(session_id, channel_id).await;
                }
                return Ok(());
            } else {
                return Err(Error::new(ErrorKind::Other, "invalid channel id"));
            }
        }
    }
    Ok(())
}

async fn remove_task(session_id: u32, channel_id: u32) {
    jdwp::stop_task(session_id, channel_id).await;
    AppTaskMap::remove(session_id, channel_id).await;
    FileTaskMap::remove(session_id, channel_id).await;
    forward::free_channel_task(session_id, channel_id).await;
    // shell & hilog task
    if let Some(pty_task) = PtyMap::get(session_id, channel_id).await {
        let _ = &pty_task.tx.send(vec![0x04_u8]).await;
        PtyMap::del(session_id, channel_id).await;
    }
    ShellExecuteMap::del(session_id, channel_id).await;
}

async fn daemon_channel_close(task_message: TaskMessage, session_id: u32) -> io::Result<()> {
    // task stop:
    crate::debug!(
        "daemon_channel_close session_id {session_id}, channel_id {}",
        task_message.channel_id
    );
    
    remove_task(session_id, task_message.channel_id).await;

    if task_message.payload[0] > 0 {
        let message = TaskMessage {
            channel_id: task_message.channel_id,
            command: HdcCommand::KernelChannelClose,
            payload: vec![task_message.payload[0] - 1],
        };
        transfer::put(session_id, message).await;
    }
    Ok(())
}

async fn daemon_file_task(task_message: TaskMessage, session_id: u32) -> io::Result<()> {
    match task_message.command {
        HdcCommand::AppCheck | HdcCommand::AppUninstall => {
            if !AppTaskMap::exsit(session_id, task_message.channel_id).await {
                let task = DaemonAppTask::new(session_id, task_message.channel_id);
                AppTaskMap::put(session_id, task_message.channel_id, task).await;
            }
            daemon_app::command_dispatch(
                session_id,
                task_message.channel_id,
                task_message.command,
                &task_message.payload,
                task_message.payload.len() as u16,
            )
            .await;
            return Ok(());
        }
        HdcCommand::AppBegin | HdcCommand::AppData => {
            daemon_app::command_dispatch(
                session_id,
                task_message.channel_id,
                task_message.command,
                &task_message.payload,
                task_message.payload.len() as u16,
            )
            .await;
            return Ok(());
        }
        HdcCommand::FileMode | HdcCommand::FileCheck | HdcCommand::FileInit => {
            if !FileTaskMap::exsit(session_id, task_message.channel_id).await {
                let mut task = HdcFile::new(session_id, task_message.channel_id);
                task.transfer.server_or_daemon = false;
                FileTaskMap::put(session_id, task_message.channel_id, task).await;
            }

            hdcfile::command_dispatch(
                session_id,
                task_message.channel_id,
                task_message.command,
                &task_message.payload,
                task_message.payload.len() as u16,
            )
            .await;
            return Ok(());
        }
        HdcCommand::ForwardInit | HdcCommand::ForwardCheck => {
            let mut task = HdcForward::new(session_id, task_message.channel_id, false);
            task.transfer.server_or_daemon = false;
            ForwardTaskMap::update(session_id, task_message.channel_id, task).await;
            forward::command_dispatch(
                session_id,
                task_message.channel_id,
                task_message.command,
                &task_message.payload,
                task_message.payload.len() as u16,
            )
            .await;
            return Ok(());
        }
        HdcCommand::ForwardCheckResult
        | HdcCommand::ForwardActiveSlave
        | HdcCommand::ForwardActiveMaster
        | HdcCommand::ForwardData
        | HdcCommand::ForwardFreeContext => {
            forward::command_dispatch(
                session_id,
                task_message.channel_id,
                task_message.command,
                &task_message.payload,
                task_message.payload.len() as u16,
            )
            .await;
            return Ok(());
        }
        HdcCommand::FileBegin
        | HdcCommand::FileData
        | HdcCommand::FileFinish
        | HdcCommand::DirMode => {
            hdcfile::command_dispatch(
                session_id,
                task_message.channel_id,
                task_message.command,
                &task_message.payload,
                task_message.payload.len() as u16,
            )
            .await;
            return Ok(());
        }
        HdcCommand::UnityRunmode
        | HdcCommand::UnityReboot
        | HdcCommand::UnityRemount
        | HdcCommand::UnityRootrun
        | HdcCommand::JdwpList
        | HdcCommand::JdwpTrack => {
            daemon_unity::command_dispatch(
                session_id,
                task_message.channel_id,
                task_message.command,
                &task_message.payload,
                task_message.payload.len() as u16,
            )
            .await;
            return Ok(());
        }
        _ => {
            crate::error!(
                "other tasks, cmd {:?}. session_id {session_id}, channel_id {}",
                task_message.command,
                task_message.channel_id
            );
        }
    }

    Ok(())
}

async fn daemon_hilog_task(task_message: TaskMessage, session_id: u32) -> io::Result<()> {
    let cmd = if task_message.payload.len() == 1 && task_message.payload[0] == 104 {
        // payload is 'h'
        "hilog -h"
    } else {
        // blank or unknown payload, ignore
        "hilog"
    }
    .to_string();
    let pty_task = PtyTask::new(
        session_id,
        task_message.channel_id,
        Some(cmd),
        HdcCommand::KernelEchoRaw,
    );
    PtyMap::put(session_id, task_message.channel_id, pty_task).await;
    Ok(())
}

async fn daemon_bug_report_task(task_message: TaskMessage, session_id: u32) -> io::Result<()> {
    let pty_task = PtyTask::new(
        session_id,
        task_message.channel_id,
        Some("hidumper".to_string()),
        HdcCommand::UnityBugreportData,
    );
    PtyMap::put(session_id, task_message.channel_id, pty_task).await;
    Ok(())
}

fn get_control_permission(param: &str) -> bool {
    let (_, control_value) = get_dev_item(param, "_");
    if control_value.trim().to_lowercase() == "false" {
        return false;
    }
    true
}

fn check_control(command: HdcCommand) -> bool {
    let mut control_param = "";
    match command {
        HdcCommand::UnityRunmode
        | HdcCommand::UnityReboot
        | HdcCommand::UnityRemount
        | HdcCommand::UnityRootrun
        | HdcCommand::ShellInit
        | HdcCommand::ShellData
        | HdcCommand::UnityExecute
        | HdcCommand::UnityHilog
        | HdcCommand::UnityBugreportInit
        | HdcCommand::JdwpList
        | HdcCommand::JdwpTrack => {
            control_param = ENV_SHELL_CONTROL;
        }
        HdcCommand::FileInit
        | HdcCommand::FileCheck
        | HdcCommand::FileData
        | HdcCommand::FileBegin
        | HdcCommand::FileFinish
        | HdcCommand::AppCheck
        | HdcCommand::AppData
        | HdcCommand::AppUninstall => {
            control_param = ENV_FILE_CONTROL;
        }
        HdcCommand::ForwardInit
        | HdcCommand::ForwardCheck
        | HdcCommand::ForwardCheckResult
        | HdcCommand::ForwardActiveSlave
        | HdcCommand::ForwardActiveMaster
        | HdcCommand::ForwardData
        | HdcCommand::ForwardFreeContext => {
            control_param = ENV_FPORT_CONTROL;
        }
        _ => {}
    }
    // (_, run_debug) = crate::utils::get_dev_item(param);
    if !control_param.is_empty() && !get_control_permission(control_param) {
        return false;
    }
    true
}

pub async fn dispatch_task(task_message: TaskMessage, session_id: u32) -> io::Result<()> {
    let cmd = task_message.command;
    let special_cmd =
        (cmd == HdcCommand::KernelHandshake) || (cmd == HdcCommand::KernelChannelClose);
    let auth_ok = auth::AuthStatusMap::get(session_id).await == auth::AuthStatus::Ok;

    if !auth_ok && !special_cmd {
        crate::error!("auth status is nok, cannt accept cmd: {}", cmd as u32);
        crate::common::hdctransfer::echo_client(
            session_id,
            task_message.channel_id,
            auth::get_auth_msg(session_id).await.as_str(),
            MessageLevel::Fail,
        )
        .await;
        transfer::put(
            session_id,
            auth::make_channel_close_message(task_message.channel_id).await,
        )
        .await;
        return Err(Error::new(
            ErrorKind::Other,
            format!("auth status is nok, cannt accept cmd: {}", cmd as u32),
        ));
    }
    if !check_control(task_message.command) {
        crate::common::hdctransfer::echo_client(
            session_id,
            task_message.channel_id,
            "debugging is not allowed",
            MessageLevel::Fail,
        )
        .await;
        crate::common::hdctransfer::transfer_task_finish(task_message.channel_id, session_id).await;
        crate::debug!(
            "check_permission param false: {}",
            task_message.command as u32
        );

        return Ok(());
    }
    match task_message.command {
        HdcCommand::KernelHandshake => auth::handshake_task(task_message, session_id).await,
        HdcCommand::UnityHilog => daemon_hilog_task(task_message, session_id).await,
        HdcCommand::UnityBugreportInit => daemon_bug_report_task(task_message, session_id).await,
        HdcCommand::ShellInit | HdcCommand::ShellData | HdcCommand::UnityExecute => {
            daemon_shell_task(task_message, session_id).await
        }
        HdcCommand::KernelChannelClose => daemon_channel_close(task_message, session_id).await,
        HdcCommand::FileInit
        | HdcCommand::FileCheck
        | HdcCommand::FileData
        | HdcCommand::FileBegin
        | HdcCommand::FileFinish
        | HdcCommand::FileMode
        | HdcCommand::DirMode
        | HdcCommand::AppCheck
        | HdcCommand::AppData
        | HdcCommand::AppUninstall
        | HdcCommand::ForwardInit
        | HdcCommand::ForwardCheck
        | HdcCommand::ForwardCheckResult
        | HdcCommand::ForwardActiveSlave
        | HdcCommand::ForwardActiveMaster
        | HdcCommand::ForwardData
        | HdcCommand::ForwardFreeContext
        | HdcCommand::UnityRunmode
        | HdcCommand::UnityReboot
        | HdcCommand::UnityRemount
        | HdcCommand::UnityRootrun
        | HdcCommand::JdwpList
        | HdcCommand::JdwpTrack => daemon_file_task(task_message, session_id).await,
        HdcCommand::KernelWakeupSlavetask => Ok(()),
        _ => Err(Error::new(
            ErrorKind::Other,
            format!("unknown command: {}", task_message.command as u32),
        )),
    }
}