OCP 2.10.19

offline-chess-puzzles - GUI to solve puzzles from the lichess puzzle database
use iced::widget::{Button, Checkbox, Column, Container, PickList, Scrollable, Text, TextInput, column, row};
use iced::{Alignment, Element, Length, Task, Theme, alignment};
use iced_aw::TabLabel;
use rfd::AsyncFileDialog;

use crate::{Message, Tab, config, lang, lang::PickListWrapper, styles};

#[derive(Debug, Clone)]
pub enum SettingsMessage {
	CheckPlaySound(bool),
	CheckAutoLoad(bool),
	CheckFlipBoard(bool),
	CheckShowCoords(bool),
	SelectPieceTheme(styles::PieceTheme),
	SelectBoardTheme(styles::BoardTheme),
	SelectLanguage(PickListWrapper<lang::Language>),
	ChangePDFExportPgs(String),
	ChangePuzzleDbLocation(String),
	ChangeSearchResultLimit(String),
	ChangeEnginePath(String),
	SearchEnginePressed,
	ChangePressed,
}

pub struct SettingsTab {
	pub engine_path: String,
	pub window_width: f32,
	pub window_height: f32,
	pub maximized: bool,
	pub piece_theme: styles::PieceTheme,
	pub board_theme: styles::BoardTheme,
	pub lang: PickListWrapper<lang::Language>,
	pub export_pgs: String,
	theme: styles::BoardTheme,
	play_sound: bool,
	auto_load_next: bool,
	pub flip_board: bool,
	pub show_coordinates: bool,

	puzzle_db_location_value: String,
	search_results_limit_value: String,

	settings_status: String,
	pub saved_configs: config::OfflinePuzzlesConfig,
}

impl SettingsTab {
	pub fn new() -> Self {
		SettingsTab {
			engine_path: config::SETTINGS.engine_path.clone().unwrap_or_default(),
			window_width: config::SETTINGS.window_width,
			window_height: config::SETTINGS.window_width,
			maximized: config::SETTINGS.maximized,
			piece_theme: config::SETTINGS.piece_theme,
			board_theme: config::SETTINGS.board_theme,
			lang: PickListWrapper::new_lang(config::SETTINGS.lang, config::SETTINGS.lang),
			export_pgs: config::SETTINGS.export_pgs.to_string(),
			theme: styles::BoardTheme::Blue,
			play_sound: config::SETTINGS.play_sound,
			auto_load_next: config::SETTINGS.auto_load_next,
			flip_board: config::SETTINGS.flip_board,
			show_coordinates: config::SETTINGS.show_coordinates,
			puzzle_db_location_value: String::from(&config::SETTINGS.puzzle_db_location),
			search_results_limit_value: config::SETTINGS.search_results_limit.to_string(),
			settings_status: String::new(),
			saved_configs: config::load_config(),
		}
	}

	pub fn update(&mut self, message: SettingsMessage) -> Task<Message> {
		match message {
			SettingsMessage::SelectPieceTheme(value) => {
				self.piece_theme = value;
				Task::perform(
					SettingsTab::send_changes(
						self.play_sound,
						self.auto_load_next,
						self.flip_board,
						self.show_coordinates,
						self.piece_theme,
						self.board_theme,
						self.engine_path.clone(),
						self.lang.lang,
					),
					Message::ChangeSettings,
				)
			}
			SettingsMessage::SelectBoardTheme(value) => {
				self.board_theme = value;
				Task::perform(
					SettingsTab::send_changes(
						self.play_sound,
						self.auto_load_next,
						self.flip_board,
						self.show_coordinates,
						self.piece_theme,
						self.theme,
						self.engine_path.clone(),
						self.lang.lang,
					),
					Message::ChangeSettings,
				)
			}
			SettingsMessage::SelectLanguage(value) => {
				self.lang = value;
				self.lang.lang = self.lang.item;
				Task::perform(
					SettingsTab::send_changes(
						self.play_sound,
						self.auto_load_next,
						self.flip_board,
						self.show_coordinates,
						self.piece_theme,
						self.theme,
						self.engine_path.clone(),
						self.lang.lang,
					),
					Message::ChangeSettings,
				)
			}
			SettingsMessage::ChangePuzzleDbLocation(value) => {
				self.puzzle_db_location_value = value;
				Task::none()
			}
			SettingsMessage::ChangeEnginePath(value) => {
				self.engine_path = value;
				Task::perform(
					SettingsTab::send_changes(
						self.play_sound,
						self.auto_load_next,
						self.flip_board,
						self.show_coordinates,
						self.piece_theme,
						self.board_theme,
						self.engine_path.clone(),
						self.lang.lang,
					),
					Message::ChangeSettings,
				)
			}
			SettingsMessage::SearchEnginePressed => Task::perform(Self::open_engine_exe(), Message::EngineFileChosen),
			SettingsMessage::ChangeSearchResultLimit(value) => {
				if value.is_empty() {
					self.search_results_limit_value = String::from("0");
				} else if let Ok(new_val) = value.parse::<usize>() {
					self.search_results_limit_value = new_val.to_string();
					self.settings_status = String::from("");
				}
				Task::none()
			}
			SettingsMessage::CheckPlaySound(value) => {
				self.play_sound = value;
				Task::perform(
					SettingsTab::send_changes(
						self.play_sound,
						self.auto_load_next,
						self.flip_board,
						self.show_coordinates,
						self.piece_theme,
						self.board_theme,
						self.engine_path.clone(),
						self.lang.lang,
					),
					Message::ChangeSettings,
				)
			}
			SettingsMessage::CheckAutoLoad(value) => {
				self.auto_load_next = value;
				Task::perform(
					SettingsTab::send_changes(
						self.play_sound,
						self.auto_load_next,
						self.flip_board,
						self.show_coordinates,
						self.piece_theme,
						self.board_theme,
						self.engine_path.clone(),
						self.lang.lang,
					),
					Message::ChangeSettings,
				)
			}
			SettingsMessage::CheckFlipBoard(value) => {
				self.flip_board = value;
				Task::perform(
					SettingsTab::send_changes(
						self.play_sound,
						self.auto_load_next,
						self.flip_board,
						self.show_coordinates,
						self.piece_theme,
						self.board_theme,
						self.engine_path.clone(),
						self.lang.lang,
					),
					Message::ChangeSettings,
				)
			}
			SettingsMessage::CheckShowCoords(value) => {
				self.show_coordinates = value;
				Task::none()
			}
			SettingsMessage::ChangePDFExportPgs(value) => {
				if value.parse::<i32>().is_ok() {
					self.export_pgs = value;
				} else if value.is_empty() {
					self.export_pgs = String::from("0");
				}
				Task::none()
			}
			SettingsMessage::ChangePressed => {
				let engine_path = if self.engine_path.is_empty() { None } else { Some(self.engine_path.clone()) };
				let config = config::OfflinePuzzlesConfig {
					engine_path,
					engine_limit: self.saved_configs.engine_limit.clone(),
					window_width: self.window_width,
					window_height: self.window_height,
					maximized: self.maximized,
					puzzle_db_location: String::from(&self.puzzle_db_location_value),
					piece_theme: self.piece_theme,
					search_results_limit: self.search_results_limit_value.parse().unwrap(),
					play_sound: self.play_sound,
					auto_load_next: self.auto_load_next,
					flip_board: self.flip_board,
					show_coordinates: self.show_coordinates,
					board_theme: self.board_theme,
					lang: self.lang.lang,
					export_pgs: self.export_pgs.parse().unwrap(),
					last_min_rating: self.saved_configs.last_min_rating,
					last_max_rating: self.saved_configs.last_max_rating,
					last_min_popularity: self.saved_configs.last_min_popularity,
					last_theme: self.saved_configs.last_theme,
					last_opening: self.saved_configs.last_opening,
					last_variation: self.saved_configs.last_variation.clone(),
					last_opening_side: self.saved_configs.last_opening_side,
				};
				let file = std::fs::File::create("settings.json");
				match file {
					Ok(file) => {
						if serde_json::to_writer_pretty(file, &config).is_ok() {
							self.settings_status = lang::tr(&self.lang.lang, "settings_saved");
						} else {
							self.settings_status = lang::tr(&self.lang.lang, "error_saving");
						}
					}
					Err(_) => self.settings_status = lang::tr(&self.lang.lang, "error_reading_config"),
				}
				Task::none()
			}
		}
	}

	async fn open_engine_exe() -> Option<String> {
		let engine_exe = AsyncFileDialog::new().pick_file().await;
		engine_exe.map(|engine_path| engine_path.path().display().to_string())
	}

	pub fn save_window_size(&self) {
		let mut config = config::load_config();
		config.window_width = self.window_width;
		config.window_height = self.window_height;
		config.maximized = self.maximized;
		let file = std::fs::File::create("settings.json");
		match file {
			Ok(file) => {
				if serde_json::to_writer_pretty(file, &config).is_err() {
					eprintln!("Error saving config file.");
				}
			}
			Err(_) => eprintln!("Error opening settings file"),
		}
	}

	pub async fn send_changes(
		play_sound: bool, auto_load: bool, flip: bool, coords: bool, pieces: styles::PieceTheme, theme: styles::BoardTheme, engine: String,
		lang: lang::Language,
	) -> Option<config::OfflinePuzzlesConfig> {
		let engine = if engine.is_empty() { None } else { Some(engine) };
		let mut config = config::load_config();
		config.board_theme = theme;
		config.piece_theme = pieces;
		config.lang = lang;
		config.play_sound = play_sound;
		config.auto_load_next = auto_load;
		config.flip_board = flip;
		config.show_coordinates = coords;
		config.engine_path = engine;
		Some(config)
	}
}

impl Tab for SettingsTab {
	type Message = Message;

	fn title(&self) -> String {
		lang::tr(&self.lang.lang, "settings")
	}

	fn tab_label(&self) -> TabLabel {
		TabLabel::Text(self.title())
	}

	fn content(&self) -> Element<'_, Message> {
		let col_settings = column![
			row![
				Text::new(lang::tr(&self.lang.lang, "piece_theme")),
				PickList::new(&styles::PieceTheme::ALL[..], Some(self.piece_theme), SettingsMessage::SelectPieceTheme)
					.style(styles::pick_list_style)
					.menu_style(styles::menu_style)
			]
			.spacing(5)
			.align_y(Alignment::Center),
			row![
				Text::new(lang::tr(&self.lang.lang, "board_theme")),
				PickList::new(&styles::BoardTheme::ALL[..], Some(self.board_theme), SettingsMessage::SelectBoardTheme)
					.style(styles::pick_list_style)
					.menu_style(styles::menu_style)
			]
			.spacing(5)
			.align_y(Alignment::Center),
			row![
				Text::new(lang::tr(&self.lang.lang, "language")),
				PickList::new(PickListWrapper::get_langs(self.lang.lang), Some(self.lang.clone()), SettingsMessage::SelectLanguage)
					.style(styles::pick_list_style)
					.menu_style(styles::menu_style)
			]
			.spacing(5)
			.align_y(Alignment::Center),
			row![
				Text::new(lang::tr(&self.lang.lang, "play_sound")),
				Checkbox::new(self.play_sound).on_toggle(SettingsMessage::CheckPlaySound).size(20),
			]
			.spacing(5)
			.align_y(Alignment::Center),
			row![
				Text::new(lang::tr(&self.lang.lang, "auto_load")),
				Checkbox::new(self.auto_load_next).on_toggle(SettingsMessage::CheckAutoLoad).size(20),
			]
			.spacing(5)
			.align_y(Alignment::Center),
			row![
				Text::new(lang::tr(&self.lang.lang, "flip_board")),
				Checkbox::new(self.flip_board).on_toggle(SettingsMessage::CheckFlipBoard).size(20),
			]
			.spacing(5)
			.align_y(Alignment::Center),
			row![
				Text::new(lang::tr(&self.lang.lang, "show_coords")),
				Checkbox::new(self.show_coordinates).on_toggle(SettingsMessage::CheckShowCoords).size(20),
			]
			.spacing(5)
			.align_y(Alignment::Center),
			row![
				Text::new(lang::tr(&self.lang.lang, "pdf_number_of_pages")),
				TextInput::new(&self.export_pgs, &self.export_pgs,).on_input(SettingsMessage::ChangePDFExportPgs).width(60),
			]
			.spacing(5)
			.align_y(Alignment::Center),
			row![
				Text::new(lang::tr(&self.lang.lang, "get_first_puzzles1")),
				TextInput::new(&self.search_results_limit_value, &self.search_results_limit_value,)
					.on_input(SettingsMessage::ChangeSearchResultLimit)
					.width(80),
				Text::new(lang::tr(&self.lang.lang, "get_first_puzzles2"))
			]
			.spacing(5)
			.align_y(Alignment::Center),
			Text::new(lang::tr(&self.lang.lang, "engine_path")),
			row![
				TextInput::new(&self.engine_path, &self.engine_path,).on_input(SettingsMessage::ChangeEnginePath).width(200),
				Button::new(Text::new("Select")).on_press(SettingsMessage::SearchEnginePressed),
			],
			Button::new(Text::new(lang::tr(&self.lang.lang, "save"))).padding(5).on_press(SettingsMessage::ChangePressed),
			Text::new(&self.settings_status).align_y(alignment::Vertical::Bottom),
		]
		.spacing(10)
		.align_x(Alignment::Center);
		let content: Element<SettingsMessage, Theme, iced::Renderer> =
			Container::new(Scrollable::new(Column::new().padding([0, 30]).spacing(10).push(col_settings)))
				.align_x(alignment::Horizontal::Center)
				.height(Length::Fill)
				.width(Length::Fill)
				.into();

		content.map(Message::Settings)
	}
}