#[cfg(feature = "gui")]
use eframe::egui;

// TODO look into internationalization in Rust, and maybe rewrite this.

/// Generate a LayoutJob for a label with specified string having the specified part underlined.
///
/// Used for keyboard shortcut hints.
#[cfg(feature = "gui")]
fn layout_with_underlined_part(label: &str, underlined: &str) -> egui::text::LayoutJob {
    let mut job = egui::text::LayoutJob::default();
    let parts = label.split_once(underlined);
    let (before, after) = parts.expect("underlined must be present in label");
    if before.len() > 0 {
        job.append(
            before,
            0.0,
            egui::TextFormat { ..Default::default() },
        );
    }
    job.append(
        underlined,
        0.0,
        egui::TextFormat { underline: egui::Stroke::new(1.0, egui::Color32::GRAY), ..Default::default() },
    );
    if after.len() > 0 {
        job.append(
            after,
            0.0,
            egui::TextFormat { ..Default::default() },
        );
    }
    job
}

// Strings for the process selection panel.
#[cfg(feature = "gui")]
pub const TTIP_PROCLIST_SHOW: &str = "Show process list.";
#[cfg(feature = "gui")]
pub const TTIP_PROCLIST_HIDE: &str = "Hide process list.";
#[cfg(feature = "gui")]
pub fn filter() -> egui::text::LayoutJob {
    layout_with_underlined_part("Filter:", "F")
}
pub fn processes(count: usize) -> String {
    format!("{} process{}", count, if count != 1 {"es"} else {""})
}

// Strings for the right panel
#[cfg(feature = "gui")]
pub const TTIP_HELP: &str = "show HELP info";
#[cfg(feature = "gui")]
pub const TTIP_CPU:  &str = "show CPU info";

// Strings for the statusbar
pub const STATUS_PROCESSES_DIED_EARLY: &str = "All started processes stopped before thread affinities could be set";
pub const STATUS_THREAD_AFFINITIES_CANCELED: &str = "Thread order/core selection canceled";
pub fn status_failed_to_set_thread_affinities(pid: &sysinfo::Pid, name: &String, error: &anyhow::Error ) -> String {
    format!("ERROR: failed to set thread affinties for process {}/{} (permissions?): {}", pid, name, error)
}
pub const STATUS_THREAD_AFFINITIES_SET: &str = "Success setting thread affinities for";
pub fn status_failed_to_get_affinity(pid: &sysinfo::Pid) -> String {
    format!("ERROR: failed to get CPU affinity for process {} or its thread", pid)
}
pub const STATUS_GROUP_SELECT_FAILED: &str = "ERROR: group select failed: could not get the set of CPUs in a group";
pub const STATUS_PROC_AFFINITY_SUCCESS: &str = "Success setting process affinity for";
pub const STATUS_FAILED_SET_AFFINITY: &str = "ERROR: failed to set process or thread affinity (permissions? dead process?)";
pub const STATUS_PROCESS_DIED: &str = "Selected process died";

// Help information strings
pub const HELP_RUN: &str = 
r"# **RUN**

Type command to **RUN:**, and
click **run** to run it.

By default, the command's process
affinities will be set. 
Optionally, type **subprocess** to
instead set affinities of any 
(fuzzy) matching process launched 
by the command.

Unpack **threads** and specify 
thread **count** to set affinities
of threads launched by the 
process, and set time since start
to **apply thread affinities** at.

Up to **count** threads will have
affinities set as specified in
**threads**. **thread order** will
set affinities of any further
threads.

Affinities are only applied at
specified time; threads stopped
before or started after will 
be unaffected.
";
#[cfg(feature = "gui")]
pub const HELP_RUN_SHORTCUTS: &str = 
r"| Action          | Shortcut    |
| --------------- | ----------- |
| Edit process    | Ctrl-r      |
| Edit subprocess | Ctrl-p      |
| Thread count    | Ctrl-t      |
| Run the command | Ctrl-Enter  |
| Clear threads   | Ctrl-BSpace |
| Thr. apply time | Ctrl-h      |
| disable SMT     | Ctrl-n      |
| TO: HW threads  | Ctrl-1      |
| TO: cores       | Ctrl-2      |
| TO: L3          | Ctrl-3      |
| TO: None        | Ctrl-0      |
| TO: ignore main | Ctrl--      |
";
#[cfg(feature = "tui")]
pub const HELP_RUN_SHORTCUTS: &str = 
r"| Action          | Shortcut    |
| --------------- | ----------- |
| Process to run  | <:>,<e>     |
| Subproc. to set | <!>,<p>     |
| Thr. apply time | <r>         |
| Run the process | <Ctrl-Enter>|
| Thread count    | t           |
";
pub const HELP_VIEW: &str = 
r"# **VIEW**

Click a core, core group or set
a **thread order** to **EDIT** 
affinities.

Unpack **threads** to view
affinities of each thread.
";
#[cfg(feature = "gui")]
pub const HELP_VIEW_SHORTCUTS: &str = 
r"| Action          | Shortcut    |
| --------------- | ----------- |
| RUN mode        | F3          |
| EDIT mode       | F5          |
| disable SMT     | Ctrl-n      |
| disable SMT     | Ctrl-n      |
| TO: HW threads  | Ctrl-1      |
| TO: cores       | Ctrl-2      |
| TO: L3          | Ctrl-3      |
| TO: None        | Ctrl-0      |
| TO: ignore main | Ctrl--      |
";
#[cfg(feature = "tui")]
pub const HELP_VIEW_SHORTCUTS: &str = "";
pub const HELP_EDIT: &str = 
r"# **EDIT**

Click cores or core groups to edit
affinities.

Unpack **threads** to edit 
affinities of each thread.

Click **apply** to set selected
affinities.
";
#[cfg(feature = "gui")]
pub const HELP_EDIT_SHORTCUTS: &str = 
r"| Action          | Shortcut    |
| --------------- | ----------- |
| RUN mode        | F3          |
| VIEW mode       | F4          |
| Apply affinity  | Ctrl-Enter  |
| Clear threads   | Ctrl-BSpace |
| disable SMT     | Ctrl-n      |
| TO: HW threads  | Ctrl-1      |
| TO: cores       | Ctrl-2      |
| TO: L3          | Ctrl-3      |
| TO: None        | Ctrl-0      |
| TO: ignore main | Ctrl--      |
";
#[cfg(feature = "tui")]
pub const HELP_EDIT_SHORTCUTS: &str = "";
pub const HELP_MAIN: &str =
r"## Modes

* **RUN** a command with affinities
* **VIEW** affinities of a process
* **EDIT** affinities of a process

Select a process to **VIEW** or 
**EDIT** on the left panel
";
#[cfg(feature = "gui")]
pub const HELP_MAIN_SHORTCUTS_GUI: &str = 
r"

| Action          | Shortcut |
| --------------- | -------- |
| Process filter  | <Ctrl-f> |
| Left pane       | <F10>    |
| Help            | <F1>     |
| CPU info        | <F2>     |
";
pub const HELP_MAIN_SHORTCUTS_TUI: &str = 
r"

| Action          | Shortcut |
| --------------- | -------- |
| Quit            | <Ctrl-c> |
| Process filter  | </>,<?>  |
| Left pane       | <F10>,<.>|
| Help            | <F1>     |
| CPU info        | <F2>     |
| Run process     | <F3>     |
| View affinity   | <F4>     |
| Edit affinity   | <F5>     |
| Process select  | <↓>/<↑>  |
| Process panel   | '>'/'<'  |
| Cores: navig.   | 🆆 🅰 🆂 🅳  |
| Cores: in       | <Enter>  |
| Cores: out      | <Esc>    |
| Cores: select   | <Space>  |
| Thread navig.   | 🅷 🅹 🅺 🅻  |
| Proc<->thread   | <Tab>    |
| TO: next order  | '+'      |
| TO: ignore main | '-'      |
| disable SMT     | 'n'      |
";
pub const HELP_MAIN_RUN: &str = ", or type command to **RUN**";

// strings for CPU info
#[cfg(feature = "gui")]
pub const CPU_INFO: &str = "CPU Information";

// Strings for the top bar/mode buttons:
#[cfg(feature = "gui")]
pub fn run() -> egui::text::LayoutJob {
    layout_with_underlined_part("RUN:", "R")
}
#[cfg(feature = "gui")]
pub const TTIP_RUN: &str = "Run a process with specified affinity.";
#[cfg(feature = "gui")]
pub fn subprocess() -> egui::text::LayoutJob {
    layout_with_underlined_part("subprocess", "p")
}
#[cfg(feature = "gui")]
pub const TTIP_SUBPROCESS: &str = "Instead of applying affinity to launched process,\n apply it to subprocess/es with names matching this.";
#[cfg(feature = "gui")]
pub const TTIP_VIEW: &str = "View CPU affinity of a process and its threads.";
#[cfg(feature = "gui")]
pub const TTIP_EDIT: &str = "Edit CPU affinity of a running process.";
#[cfg(feature = "gui")]
pub const TTIP_RUN_SWITCH: &str = "go to run mode";
#[cfg(feature = "gui")]
pub const TTIP_VIEW_SWITCH: &str = "go to view mode";
#[cfg(feature = "gui")]
pub const TTIP_EDIT_SWITCH: &str = "go to edit mode";

// Strings for the row right below the topology view.
#[cfg(feature = "gui")]
pub fn no_smt() -> egui::text::LayoutJob {
    layout_with_underlined_part(NO_SMT, "n")
}
pub const NO_SMT: &str = "no HT/SMT";
#[cfg(feature = "gui")]
pub const TTIP_NO_SMT: &str = "Select only one thread per physical core.";
#[cfg(feature = "gui")]
pub fn apply_thread_affinities() -> egui::text::LayoutJob {
    layout_with_underlined_part(APPLY_THREAD_AFFINITIES, "h")
}
pub const APPLY_THREAD_AFFINITIES: &str = "apply thread affinities @ ";
#[cfg(feature = "gui")]
pub const TTIP_APPLY_THREAD_AFFINITIES: &str = 
r"Delay from process start to apply thread affinities at.
Affinities will not be set for any threads started *after* this time";

// Strings for the bottom-most row.
#[cfg(feature = "gui")]
pub const TTIP_APPLY_RUN: &str = "Run specified command";
#[cfg(feature = "gui")]
pub const TTIP_APPLY: &str = "Set process and its threads to use selected cores.";

// Strings for the thread view pane.
pub const THREADS: &str = "threads";
pub const CLEAR: &str = "clear";
#[cfg(feature = "gui")]
pub const TTIP_THREADS: &str = "View and control which threads can use which cores.\nBy default, each thread uses all cores allowed by its process.";
pub const THREADS_COUNT: &str = "count";
#[cfg(feature = "gui")]
pub const TTIP_THREADS_COUNT_RUN: &str = 
r"Number of threads to manually set affinity for.
If the process runs *fewer* threads, excess entries
will be ignored. If it spawns *more* threads, their
affinity will be set by thread order, if any, or
default to process affinity. Affinities will
be assigned in thread launch order.";
#[cfg(feature = "gui")]
pub const TTIP_THREADS_CLEAR: &str = "Clears selected cores for all threads.";
#[cfg(feature = "gui")]
pub const TTIP_THREADS_COUNT: &str = "number of threads currently running in this process";

// Strings for thread orders.
pub const THREAD_ORDER: &str = "thread order";
#[cfg(feature = "gui")]
pub const TTIP_ORDER_PU: &str = "Bind each thread to a separate hardware thread ('hyper-thread').\nMultiple threads on a core may fight for time/resources";
#[cfg(feature = "gui")]
pub const TTIP_ORDER_CORES: &str = "Bind each thread to a unique physical core.\nReduces thread-contention within the process.";
#[cfg(feature = "gui")]
pub const TTIP_ORDER_L3: &str = "Bind each thread to a core group with a separate L3 cache.\nMaximizes cache per thread, increases thread synchronization overhead";
#[cfg(feature = "gui")]
pub const TTIP_ORDER_NONE: &str = "Apply user-specified affinity, don't specify affinity for any more threads";
#[cfg(feature = "gui")]
pub const TTIP_ORDER: &str = 
r"Thread orders assign different cores to each thread,
e.g. to improve benchmark consistency or reduce resource conflicts.
If more threads than cores exit, cores are assigned in a cyclic fashion.";
pub const IGNORE_MAIN: &str = "ignore main";
#[cfg(feature = "gui")]
pub const TTIP_IGNORE_MAIN: &str = "Exclude main thread from thread order.\nUse when there is one 'main' and N 'worker' threads.";

// other strings
#[cfg(feature = "gui")]
pub const OK: &str = "Ok";
pub const ERROR: &str = "ERROR";
