import { inject , injectable } from "inversify" ; import { INTERNAL_TYPES , TYPES } from "../types" ; import { CardDataLoaderService } from "./CardDataLoaderService" ; import type { Card } from "./Card" ; import type { CardSet } from "./set/CardSet" ; import type { CardDatabase } from "./CardDatabase" ; import { FindCardBy } from "./CardDatabase" ; import type { CardType } from "./type/CardType" ; import { CardTypeCategory } from "./type/CardTypeCategory" ; import type { UnlinkedCard } from "./UnlinkedCard" ; import { deepFreeze } from "lightdash" ; import { CardLinkingService } from "./CardLinkingService" ; import { getLogger } from "../logger" ; @ injectable ( ) export class MemoryCardDatabase implements CardDatabase { private static readonly logger = getLogger ( MemoryCardDatabase ) ; readonly #cardDataLoaderService : CardDataLoaderService ; readonly #cardLinkingService : CardLinkingService ; #loadingSets : Promise < void > | null ; #loadingArchetypes : Promise < void > | null ; #loadingCardValues : Promise < void > | null ; #loadingAllCards : Promise < void > | null ; readonly #cardsByPasscode : Map < string , Card > ; readonly #cardsByName : Map < string , Card > ; readonly #sets : CardSet [ ] ; readonly #archetypes : string [ ] ; readonly #types : Map < CardTypeCategory , CardType [ ] > ; readonly #subTypes : Map < CardTypeCategory , string [ ] > ; readonly #attributes : string [ ] ; readonly #linkMarkers : string [ ] ; readonly #levels : number [ ] ; constructor ( @ inject ( TYPES . CardDataLoaderService ) cardDataLoaderService , @ inject ( INTERNAL_TYPES . CardLinkingService ) cardLinkingService ) { this . #cardDataLoaderService = cardDataLoaderService ; this . #cardLinkingService = cardLinkingService ; this . #loadingSets = null ; this . #loadingArchetypes = null ; this . #loadingCardValues = null ; this . #loadingAllCards = null ; this . #cardsByPasscode = new Map < string , Card > ( ) ; this . #cardsByName = new Map < string , Card > ( ) ; this . #sets = [ ] ; this . #archetypes = [ ] ; this . #types = new Map < CardTypeCategory , CardType [ ] > ( Object . values ( CardTypeCategory ) . map ( ( typeCategory ) => [ typeCategory , [ ] , ] ) ) ; this . #subTypes = new Map < CardTypeCategory , string [ ] > ( Object . values ( CardTypeCategory ) . map ( ( typeCategory ) => [ typeCategory , [ ] , ] ) ) ; this . #attributes = [ ] ; this . #linkMarkers = [ ] ; this . #levels = [ ] ; } async prepareAll ( ) { await Promise . all ( [ this . #loadSets ( ) , this . #loadCardValues ( ) , this . #loadArchetypes ( ) , ] ) ; await this . #loadAllCards ( ) ; } async prepareCard ( cardKey , findCardBy ) { await Promise . all ( [ this . #loadSets ( ) , this . #loadCardValues ( ) ] ) ; return await this . #loadCard ( cardKey , findCardBy ) ; } hasCard ( cardKey , findCardBy ) { return this . #getCardMap ( findCardBy ) . has ( cardKey ) ; } getCard ( cardKey , findCardBy ) { return this . #getCardMap ( findCardBy ) . get ( cardKey ) ?? null ; } getCards ( ) { return Array . from ( this . #cardsByPasscode . values ( ) ) ; } getSets ( ) { return this . #sets ; } getArchetypes ( ) { return this . #archetypes ; } getTypes ( typeCategory ) { return this . #types . get ( typeCategory ) ! ; } getSubTypes ( typeCategory ) { return this . #subTypes . get ( typeCategory ) ! ; } getAttributes ( ) { return this . #attributes ; } getLevels ( ) { return this . #levels ; } getLinkMarkers ( ) { return this . #linkMarkers ; } return async #loadCard ( cardKey , findCardBy ) { let card ; if ( this . hasCard ( cardKey , findCardBy ) ) { card = this . getCard ( cardKey , findCardBy ) ! ; } else { card = await this . #cardDataLoaderService . getCard ( cardKey , findCardBy ) ; if ( card != null ) { this . #registerCards ( [ card ] ) ; } } if ( card == null ) { return null ; } return findCardBy == FindCardBy . NAME ? card . name : card . passcode ; } async #loadAllCards ( ) { if ( this . #loadingAllCards == null ) { this . #loadingAllCards = this . #cardDataLoaderService . getAllCards ( ) . then ( ( cards ) => this . #registerCards ( cards ) ) ; } return this . #loadingAllCards ; } #loadArchetypes ( ) { if ( this . #loadingArchetypes == null ) { this . #loadingArchetypes = this . #cardDataLoaderService . getArchetypes ( ) . then ( ( archetypes ) => { this . #archetypes . push ( ... archetypes ) ; deepFreeze ( this . #archetypes ) ; MemoryCardDatabase . logger . debug ( "Registered archetypes." , this . #archetypes ) ; } ) ; } return this . #loadingArchetypes ; } #loadSets ( ) { if ( this . #loadingSets == null ) { this . #loadingSets = this . #cardDataLoaderService . getAllCardSets ( ) . then ( ( cardSets ) => { this . #sets . push ( ... cardSets ) ; deepFreeze ( this . #sets ) ; MemoryCardDatabase . logger . debug ( "Registered sets." , this . #sets ) ; } ) ; } return this . #loadingSets ; } async #loadCardValues ( ) { if ( this . #loadingCardValues == null ) { this . #loadingCardValues = this . #cardDataLoaderService . getCardValues ( ) . then ( ( cardValues ) => { for ( const typeCategory of Object . values ( CardTypeCategory ) ) { const cardTypes = this . #types . get ( typeCategory ) ! ; cardTypes . push ( ... cardValues [ typeCategory ] . types ) ; deepFreeze ( cardTypes ) ; const cardSubTypes = this . #subTypes . get ( typeCategory ) ! ; cardSubTypes . push ( ... cardValues [ typeCategory ] . subTypes ) ; deepFreeze ( cardSubTypes ) ; } MemoryCardDatabase . logger . debug ( "Registered types and sub-types." , this . #types , this . #subTypes ) ; this . #attributes . push ( ... cardValues [ CardTypeCategory . MONSTER ] . attributes ) ; deepFreeze ( this . #attributes ) ; this . #levels . push ( ... cardValues [ CardTypeCategory . MONSTER ] . levels ) ; deepFreeze ( this . #levels ) ; this . #linkMarkers . push ( ... cardValues [ CardTypeCategory . MONSTER ] . linkMarkers ) ; deepFreeze ( this . #linkMarkers ) ; MemoryCardDatabase . logger . debug ( "Registered monster values." , this . #attributes , this . #levels , this . #linkMarkers ) ; } ) ; } return this . #loadingCardValues ; } #registerCards ( unlinkedCards ) { const setMap = new Map < string , CardSet > ( this . #sets . map ( ( set ) => [ set . name , set ] ) ) ; const types = Array . from ( this . #types . values ( ) ) . flat ( ) ; const typeMap = new Map < string , CardType > ( types . map ( ( type ) => [ type . name , type ] ) ) ; for ( const unlinkedCard of unlinkedCards ) { if ( this . #cardsByPasscode . has ( unlinkedCard . passcode ) ) { continue ; } const card = this . #cardLinkingService . linkCard ( unlinkedCard , setMap , typeMap ) ; deepFreeze ( card ) ; this . #cardsByPasscode . set ( card . passcode , card ) ; this . #cardsByName . set ( card . name , card ) ; MemoryCardDatabase . logger . trace ( `Registered card '${ card . passcode }'.` ) ; } } #getCardMap ( findCardBy ) { return findCardBy == FindCardBy . PASSCODE ? this . #cardsByPasscode : this . #cardsByName ; } }