// Copyright (C) 2024 The Android Open Source Project
//
// 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.

//! sfdo: Make surface flinger do things
use android_gui::{aidl::android::gui::ISurfaceComposer::ISurfaceComposer, binder};
use clap::{Parser, Subcommand};
use std::fmt::Debug;

const SERVICE_IDENTIFIER: &str = "SurfaceFlingerAIDL";

fn print_result<T, E>(function_name: &str, res: Result<T, E>)
where
    E: Debug,
{
    match res {
        Ok(_) => println!("{}: Operation successful!", function_name),
        Err(err) => println!("{}: Operation failed: {:?}", function_name, err),
    }
}

fn parse_toggle(toggle_value: &str) -> Option<bool> {
    let positive = ["1", "true", "y", "yes", "on", "enabled", "show"];
    let negative = ["0", "false", "n", "no", "off", "disabled", "hide"];

    let word = toggle_value.to_lowercase(); // Case-insensitive comparison

    if positive.contains(&word.as_str()) {
        Some(true)
    } else if negative.contains(&word.as_str()) {
        Some(false)
    } else {
        None
    }
}

#[derive(Parser)]
#[command(version = "0.1", about = "Execute SurfaceFlinger internal commands.")]
#[command(propagate_version = true)]
struct Cli {
    #[command(subcommand)]
    command: Option<Commands>,
}

#[derive(Subcommand, Debug)]
enum Commands {
    #[command(about = "[optional(--delay)] Perform a debug flash.")]
    DebugFlash {
        #[arg(short, long, default_value_t = 0)]
        delay: i32,
    },

    #[command(
        about = "state = [enabled | disabled] When enabled, it disables Hardware Overlays, \
                      and routes all window composition to the GPU. This can help check if \
                      there is a bug in HW Composer."
    )]
    ForceClientComposition { state: Option<String> },

    #[command(about = "state = [hide | show], displays the framerate in the top left corner.")]
    FrameRateIndicator { state: Option<String> },

    #[command(about = "Force composite ahead of next VSYNC.")]
    ScheduleComposite,

    #[command(about = "Force commit ahead of next VSYNC.")]
    ScheduleCommit,
}

/// sfdo command line tool
///
/// sfdo allows you to call different functions from the SurfaceComposer using
/// the adb shell.
fn main() {
    binder::ProcessState::start_thread_pool();
    let composer_service = match binder::get_interface::<dyn ISurfaceComposer>(SERVICE_IDENTIFIER) {
        Ok(service) => service,
        Err(err) => {
            eprintln!("Unable to connect to ISurfaceComposer: {}", err);
            return;
        }
    };

    let cli = Cli::parse();

    match &cli.command {
        Some(Commands::FrameRateIndicator { state }) => {
            if let Some(op_state) = state {
                let toggle = parse_toggle(op_state);
                match toggle {
                    Some(true) => {
                        let res = composer_service.enableRefreshRateOverlay(true);
                        print_result("enableRefreshRateOverlay", res);
                    }
                    Some(false) => {
                        let res = composer_service.enableRefreshRateOverlay(false);
                        print_result("enableRefreshRateOverlay", res);
                    }
                    None => {
                        eprintln!("Invalid state: {}, choices are [hide | show]", op_state);
                    }
                }
            } else {
                eprintln!("No state, choices are [hide | show]");
            }
        }
        Some(Commands::DebugFlash { delay }) => {
            let res = composer_service.setDebugFlash(*delay);
            print_result("setDebugFlash", res);
        }
        Some(Commands::ScheduleComposite) => {
            let res = composer_service.scheduleComposite();
            print_result("scheduleComposite", res);
        }
        Some(Commands::ScheduleCommit) => {
            let res = composer_service.scheduleCommit();
            print_result("scheduleCommit", res);
        }
        Some(Commands::ForceClientComposition { state }) => {
            if let Some(op_state) = state {
                let toggle = parse_toggle(op_state);
                match toggle {
                    Some(true) => {
                        let res = composer_service.forceClientComposition(true);
                        print_result("forceClientComposition", res);
                    }
                    Some(false) => {
                        let res = composer_service.forceClientComposition(false);
                        print_result("forceClientComposition", res);
                    }
                    None => {
                        eprintln!("Invalid state: {}, choices are [enabled | disabled]", op_state);
                    }
                }
            } else {
                eprintln!("No state, choices are [enabled | disabled]");
            }
        }
        None => {
            println!("Execute SurfaceFlinger internal commands.");
            println!("run `adb shell sfdo help` for more to view the commands.");
            println!("run `adb shell sfdo [COMMAND] --help` for more info on the command.");
        }
    }
}