/* Copyright 2024, Savanni D'Gerinel This file is part of Kifu. Kifu is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Kifu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Kifu. If not, see . */ use crate::{ database::Database, types::{AppState, Config, ConfigOption, GameState, LibraryPath, Player, Rank}, settings, }; use async_std::{ channel::{Receiver, Sender}, stream, task::spawn, }; use serde::{Deserialize, Serialize}; use std::{ future::Future, path::PathBuf, sync::{Arc, RwLock, RwLockReadGuard}, }; pub trait Observable { fn subscribe(&self) -> Receiver; } #[derive(Clone, Debug, Serialize, Deserialize)] pub enum CoreRequest { Settings(settings::SettingsRequest) /* ChangeSetting(ChangeSettingRequest), CreateGame(CreateGameRequest), Home, OpenConfiguration, PlayingField, PlayStone(PlayStoneRequest), StartGame, */ } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum ChangeSettingRequest { LibraryPath(String), } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct PlayStoneRequest { pub column: u8, pub row: u8, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct CreateGameRequest { pub black_player: PlayerInfoRequest, pub white_player: PlayerInfoRequest, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum PlayerInfoRequest { Hotseat(HotseatPlayerRequest), } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct HotseatPlayerRequest { pub name: String, pub rank: Option, } impl From for Player { fn from(p: HotseatPlayerRequest) -> Self { Self { name: p.name, rank: p.rank.and_then(|r| Rank::try_from(r.as_ref()).ok()), } } } #[derive(Clone, Debug, Serialize, Deserialize)] pub enum CoreResponse { Settings(settings::SettingsResponse) /* ConfigurationView(ConfigurationView), HomeView(HomeView), PlayingFieldView(PlayingFieldView), UpdatedConfigurationView(ConfigurationView), */ } impl From for CoreResponse { fn from(r: settings::SettingsResponse) -> Self { Self::Settings(r) } } #[derive(Clone, Debug)] pub enum CoreNotification { ConfigurationUpdated(Config), BoardUpdated, } #[derive(Clone, Debug)] pub struct Core { config: Arc>, // state: Arc>, library: Arc>>, subscribers: Arc>>>, } impl Core { pub fn new(config: Config) -> Self { println!("config: {:?}", config); Self { config: Arc::new(RwLock::new(config)), // state, library: Arc::new(RwLock::new(None)), subscribers: Arc::new(RwLock::new(vec![])), } } pub fn get_config(&self) -> Config { self.config.read().unwrap().clone() } /// Change the configuration of the Core. This function will update any relevant core /// functions, especially the contents of the library, and it will notify any subscribed objects /// that the configuration has changed. /// /// It will not handle persisting the new configuration, as the backing store for the /// configuration is not a decision for the core library. pub async fn set_config(&self, config: Config) { *self.config.write().unwrap() = config.clone(); let subscribers = self.subscribers.read().unwrap().clone(); for subscriber in subscribers { let subscriber = subscriber.clone(); let _ = subscriber.send(CoreNotification::ConfigurationUpdated(config.clone())).await; } } pub fn library<'a>(&'a self) -> RwLockReadGuard<'_, Option> { self.library.read().unwrap() } pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse { match request { CoreRequest::Settings(request) => settings::handle(&self, request).await.into(), } } /* pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse { match request { CoreRequest::ChangeSetting(request) => match request { ChangeSettingRequest::LibraryPath(path) => { // let mut config = self.config.write().unwrap(); // config.set(ConfigOption::DatabasePath(DatabasePath(PathBuf::from( // path, // )))); // CoreResponse::UpdatedConfigurationView(configuration(&config)) unimplemented!() } }, CoreRequest::CreateGame(create_request) => { /* let mut app_state = self.state.write().unwrap(); let white_player = { match create_request.white_player { PlayerInfoRequest::Hotseat(request) => Player::from(request), } }; let black_player = { match create_request.black_player { PlayerInfoRequest::Hotseat(request) => Player::from(request), } }; app_state.game = Some(GameState { white_player, black_player, ..GameState::default() }); let game_state = app_state.game.as_ref().unwrap(); CoreResponse::PlayingFieldView(playing_field(game_state)) */ unimplemented!() } CoreRequest::Home => { // CoreResponse::HomeView(home(self.state.read().unwrap().database.all_games())) unimplemented!() } CoreRequest::OpenConfiguration => { // CoreResponse::ConfigurationView(configuration(&self.config.read().unwrap())) unimplemented!() } CoreRequest::PlayingField => { // let app_state = self.state.read().unwrap(); // let game = app_state.game.as_ref().unwrap(); // CoreResponse::PlayingFieldView(playing_field(game)) unimplemented!() } CoreRequest::PlayStone(request) => { // let mut app_state = self.state.write().unwrap(); // app_state.place_stone(request); // let game = app_state.game.as_ref().unwrap(); // CoreResponse::PlayingFieldView(playing_field(game)) unimplemented!() } CoreRequest::StartGame => { unimplemented!() } } } */ // pub async fn run(&self) {} } impl Observable for Core { fn subscribe(&self) -> Receiver { let mut subscribers = self.subscribers.write().unwrap(); let (sender, receiver) = async_std::channel::unbounded(); subscribers.push(sender); receiver } }