/* Copyright 2024, Savanni D'Gerinel This file is part of On the Grid. On the Grid 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. On the Grid 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 On the Grid. If not, see . */ use crate::{ database::Database, library, settings, types::{Config, LibraryPath}, }; use async_std::channel::{Receiver, Sender}; use serde::{Deserialize, Serialize}; use std::sync::{Arc, RwLock, RwLockReadGuard}; pub trait Observable { fn subscribe(&self) -> Receiver; } #[derive(Clone, Debug, Serialize, Deserialize)] pub enum CoreRequest { Library(library::LibraryRequest), 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 { Library(library::LibraryResponse), Settings(settings::SettingsResponse), } impl From for CoreResponse { fn from(r: library::LibraryResponse) -> Self { Self::Library(r) } } 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); let library = match config.get::() { Some(ref path) if path.to_path_buf().exists() => { Some(Database::open_path(path.to_path_buf()).unwrap()) } _ => None, }; Self { config: Arc::new(RwLock::new(config)), // state, library: Arc::new(RwLock::new(library)), 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 db = library::read_library(self.config.read().unwrap().get::()).await; let library_path = self.config.read().unwrap().get::(); if let Some(ref path) = library_path { self.load_library(path); } self.notify(CoreNotification::ConfigurationUpdated(config.clone())) .await; } fn load_library(&self, path: &LibraryPath) { let db = Database::open_path(path.to_path_buf()).unwrap(); *self.library.write().unwrap() = Some(db); } pub fn library(&self) -> RwLockReadGuard<'_, Option> { self.library.read().unwrap() } pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse { match request { CoreRequest::Library(request) => library::handle(self, request).await.into(), CoreRequest::Settings(request) => settings::handle(self, request).await.into(), } } pub async fn notify(&self, notification: CoreNotification) { let subscribers = self.subscribers.read().unwrap().clone(); for subscriber in subscribers { let subscriber = subscriber.clone(); let _ = subscriber.send(notification.clone()).await; } } /* 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 } }