import { inject , injectable } from "inversify" ; import type { Card } from "./Card" ; import { CardTypeCategory } from "./type/CardTypeCategory" ; import { CardDatabase } from "./CardDatabase" ; import { TYPES } from "../types" ; import { Format } from "./format/Format" ; export enum SortingStrategy { /**
     * Sort cards like they would appear in a sorted deck
     */ DEFAULT = "Default" , NAME = "Name" , ATK = "ATK" , DEF = "DEF" , LEVEL = "Level" , VIEWS = "Views" , RELEASE_TCG = "Date: TCG" , RELEASE_OCG = "Date: OCG" } export enum SortingOrder { DESC = "Desc" , ASC = "Asc" } export interface SortingOptions { strategy : SortingStrategy ; order ? : SortingOrder ; } type Comparator < T > = ( a : T , b : T ) => number ; @ injectable ( ) export class SortingService { readonly #cardDatabase : CardDatabase ; constructor ( @ inject ( TYPES . CardDatabase ) cardDatabase ) { this . #cardDatabase = cardDatabase ; } param cards param options return sort ( cards , options ) { const comparator = this . #findComparator ( options . strategy , options . order ?? SortingOrder . DESC ) ; return cards . sort ( comparator ) ; } #findComparator ( strategy , order ) { if ( strategy === SortingStrategy . NAME ) { return this . #createNameComparator ( order ) ; } if ( strategy === SortingStrategy . ATK ) { return this . #createAtkComparator ( order ) ; } if ( strategy === SortingStrategy . DEF ) { return this . #createDefComparator ( order ) ; } if ( strategy === SortingStrategy . LEVEL ) { return this . #createLevelComparator ( order ) ; } if ( strategy === SortingStrategy . RELEASE_TCG ) { return this . #compareReleaseDate ( order , Format . TCG ) ; } if ( strategy === SortingStrategy . RELEASE_OCG ) { return this . #compareReleaseDate ( order , Format . OCG ) ; } if ( strategy === SortingStrategy . VIEWS ) { return this . #createViewsComparator ( order ) ; } else { return this . #createDefaultComparator ( order ) ; } } /**
     * Deck-sorting function loosely based on
     * {@see https://github.com/Fluorohydride/ygopro}'s ./gframe/client_card.cpp sorting methods
     */ #createDefaultComparator ( order ) { const levelComparator = this . #createLevelComparator ( order ) ; const sortGroupComparator = this . #createSortGroupComparator ( order ) ; const atkComparator = this . #createAtkComparator ( order ) ; const defComparator = this . #createDefComparator ( order ) ; const subTypeComparator = this . #createSubTypeComparator ( order ) ; const nameComparator = this . #createNameComparator ( order ) ; return ( a , b ) => { if ( a . type . sortGroup != b . type . sortGroup ) { return sortGroupComparator ( a , b ) ; } if ( a . type . category === CardTypeCategory . MONSTER ) { if ( a . level !== b . level ) { return levelComparator ( a , b ) ; } if ( a . atk !== b . atk ) { return atkComparator ( a , b ) ; } if ( a . def !== b . def ) { return defComparator ( a , b ) ; } } else { if ( a . subType != b . subType ) { return subTypeComparator ( a , b ) ; } } return nameComparator ( a , b ) ; } ; } #createAtkComparator ( order ) { return this . #createComparator ( ( card ) => card . atk ?? 0 , order ) ; } #createDefComparator ( order ) { return this . #createComparator ( ( card ) => card . def ?? 0 , order ) ; } #createLevelComparator ( order ) { return this . #createComparator ( ( card ) => card . level ?? 0 , order ) ; } #createViewsComparator ( order ) { return this . #createComparator ( ( card ) => card . views , order ) ; } #createSortGroupComparator ( order ) { const orderModifier = this . #getOrderModifier ( order ) * - 1 ; return ( a , b ) => ( b . type . sortGroup - a . type . sortGroup ) * orderModifier ; } #compareReleaseDate ( order , format ) { const fallbackRelease = order === SortingOrder . ASC ? Infinity : 0 ; return this . #createComparator ( ( card ) => card . release [ format ] ?? fallbackRelease , order ) ; } #createNameComparator ( order ) { return order === SortingOrder . DESC ? ( a , b ) => a . name . localeCompare ( b . name ) : ( a , b ) => b . name . localeCompare ( a . name ) ; } #createSubTypeComparator ( order ) { return ( a , b ) => { const subTypes = this . #cardDatabase . getSubTypes ( a . type . category ) ; return ( ( subTypes . indexOf ( a . subType ) - subTypes . indexOf ( b . subType ) ) * this . #getOrderModifier ( order ) ) ; } ; } #createComparator ( selector , order ) { return ( a , b ) => ( selector ( b ) - selector ( a ) ) * this . #getOrderModifier ( order ) ; } #getOrderModifier ( order ) { return order === SortingOrder . ASC ? - 1 : 1 ; } }