//! The Busbi Crate is a CLI tool to generate BadUSB scripts for the [Flipper Zero](https://docs.flipper.net/zero/bad-usb).
//!
//! The BadUSB generated script will recreate files which you specified onto a another machine by running it with your
//! Flipper-Zero on any machine that takes keyboard input.   
//! The way it inputs the information into the new machine is through the powershell if on Winodws
//! and the terminal if on Linux or MacOS[^note]. 
//!
//! The intended use is for copying setup scripts[^note2] like shell (.sh) and powershell (.ps1) onto other
//! machines (and possibly running them), but other uses are possible. 
//!
//! The generated files use the [Flipper scripting language](https://developer.flipper.net/flipperzero/doxygen/badusb_file_format.html). 
//!
//! [^note]: Tested on linux but does assume a desktop environment which can open a terminal using
//! the 'Windows'/GUI key. MacOS not tested but should work.
//!
//! [^note2]: Technically it will work on any file that contains text, so if you want to copy over
//! large text files, this way it's possible. However note that since this is a Flipper script it
//! will actually write it all out, which may take time. 
//! ## Basic Usage: 
//!
//! ```
//!busbi my_script.sh my_badUSB.txt
//! ```
//! This will create a file called `my_badUSB.txt` which you can execute from a Flipper Zero. If you
//! do, the script will write a copy of the original file `my_script.sh` under: 
//! `$HOME/my_badUSB/my_script.sh`[^note3]
//!
//! [^note3]: If you don't know where $HOME is, it is the main folder under which you find stuff like your documents, pictures and desktop,
//! you can see the location by executing `echo $HOME` in your terminal on linux or by typing
//! `%userprofile%` on powershell in windows.
//!
//! The general pattern is:
//! ```
//! busbi [OPTIONS] [TARGET] [NEW_NAME]
//! ```
//! OPTIONS - Represents the options for flags like '-o' which you can specify for more flexible
//! use cases.
//!
//! TARGET - Is the file (or directory[^note4]) you want the script to copy over when run on
//! another machine. 
//!
//! NEW_NAME - Is the name that will be given to the script, this must end with '.txt'.
//!
//![^note4]: Use either '-d' or '-m' for targeting directories, see the help message or
//!documentation for usage instructions. 
//!
//! # Targeting Operating Systems
//!
//! Targeting a specific OS like Windows or Linux can be done with the `-o` flag
//!
//! Valid Values are 'Windows' and 'Unix' (NOT case sensitive).
//! DEFAULT: 'Windows'
//!
//! ## Example
//!
//! ```
//!busbi -o unix my_file.sh my_BadUSB.txt
//! ```
//! Will generete the `my_BadUSB.txt` file which when run with the flipper will copy the content of
//! the `my_file.sh` onto the target machine. 
//!
//!# Executing the copied file: 
//!
//!Executing a file/script will be done at the very of the Bad USB script, you do this with the `-x` flag
//!and then specifying the script to execute. Note the file you want to execute has to be specified
//!explicitely as the Bad USB script will try to execute whatever file name you give it. So it
//!doesn't have to be the file it copied over but could be other executatbles that are already on
//!the target machine. 
//!
//! If you are on a Windows machine and want to **copy** 'my_file.ps1' (a powershell script) onto another Windows machine and then **execute** it, do as follows:
//! ```
//! busbi -x my_file.ps1 my_file.ps1 my_BadUSB.txt 
//! ```
//! Note you don't need to specify the target OS being windows with `-o` because that is already
//! the default, however you can still add `-o windows` if you want.
//!
//! If instead you want to target a Linux machine with a shell script you made on your windows
//! machine then instead do:
//! ```
//! busbi -x my_file.sh -o unix my_file.sh my_BadUSB.txt 
//! ```
//! You must specify the file that needs to executed explicitely as `-x` will try to execute
//! whatever file you specify! Even if that is another pre-existing unrelated file.   
use std::{env, fs::{File, create_dir}};
use std::io::{self, BufWriter, Write};
use std::path::PathBuf;

mod boilerplates;
use boilerplates::{executable_boilerplate, start_boilerplate, end_boilerplate, make_file_boilerplate};
mod help_msg;
use help_msg::{HelpMessage};
mod arguments;
use arguments::{parse_args_advanced};
mod handlers;
use handlers::{file_handler, d_flag_handler};

fn main() -> io::Result<()> {
    let args : Vec<String> = env::args().collect();

    let (target_os, execute, x_value, d_flag, m_flag, source_file, destination, list_files, close_window) = match parse_args_advanced(&args) {
        Ok(x) => x,
        Err(e) => match e {
            HelpMessage::PrintingHelp => {println!("\x1b[32mHope that helped :)\x1b[0m"); return Ok(())},
            _ => {println!("Got an error. Check your usage. Got: {:?}", e); println!("Use '--help' or '-h' for usage instructions."); return Ok(())},
        }
    };

    if m_flag {
        match create_dir(PathBuf::from(destination)) {
            Ok(_x) => println!("Made directory {}.", destination),
            Err(e) => {println!("Failed with following error: {}", e); return Ok(())},
        }
        match file_handler(target_os, execute, list_files, destination, d_flag, m_flag, close_window) {
            Ok(_x) => println!("\x1b[32mSuccesfully created files and directories.\x1b[0m"), 
            Err(e) => {println!("Got an error: {:?}, Check usage.", e); return Ok(())},
        }
    } else if d_flag {
        let new_file = File::create(destination)?;
        //1MB capacity for the buffer, feel free to change this.  
        let mut write_buf = BufWriter::with_capacity(1000000, new_file);
        let start_boiler: String = start_boilerplate(target_os, true, destination);
        let _ = write_buf.write(start_boiler.as_bytes());

        let grande_string : String = match d_flag_handler(target_os, execute, list_files, destination) {
            Ok(x) => x,
            Err(e) => panic!("Got an error: {:?}", e),
        };
        let _ = write_buf.write(grande_string.as_bytes());
        if execute {
            let execute_boiler : String = match executable_boilerplate(target_os, &x_value, destination, d_flag, m_flag) {
                Ok(x) => x,
                Err(e) => panic!("Got an error: {:?}",e),
            };
            let _ = write_buf.write(execute_boiler.as_bytes());
        } else {
            let end_boiler: String = end_boilerplate(target_os, close_window);
            let _ = write_buf.write(end_boiler.as_bytes());
        }
        //This pushes the contents of the buffer to the file. 
        match write_buf.flush() {
            Ok(_x) => println!("\x1b[32mSuccessfully wrote file.\x1b[0m"),
            Err(e) => {println!("Got error: {}", e); return Ok(())},  
        };

    } else {
        let new_file = File::create(destination)?;
        let mut write_buf = BufWriter::with_capacity(1000000, new_file);
        let start_boiler: String = start_boilerplate(target_os, false, destination);
        let _ = write_buf.write(start_boiler.as_bytes());
        let file_content: String = make_file_boilerplate(target_os, &source_file, destination, false, false);
        let _ = write_buf.write(file_content.as_bytes());
        if execute {
            let execute_boiler : String = match executable_boilerplate(target_os, &x_value, destination, d_flag, m_flag) {
                Ok(x) => x, 
                Err(e) => panic!("Couldn't work with given x flag value, got error: {:?}", e),
            };
            let _ = write_buf.write(execute_boiler.as_bytes());
        } else {
            let end_boiler: String = end_boilerplate(target_os, close_window);
            let _ = write_buf.write(end_boiler.as_bytes());
        }
        match write_buf.flush() {
            Ok(_x) => println!("\x1b[32mSuccessfully wrote file.\x1b[0m"),
            Err(e) => {println!("Got error: {}", e);return Ok(())}, 
        };

    }
    Ok(())

}





