import { AfterContentInit , ChangeDetectionStrategy , ChangeDetectorRef , Component , ViewRef } from '@angular/core' ; import { ScenarioId } from '@firestone-hs/reference-data' ; import { Observable } from 'rxjs' ; import { distinctUntilChanged , filter , map , takeUntil , tap } from 'rxjs/operators' ; import { GameStat } from '../../../models/mainwindow/stats/game-stat' ; import { MercenariesHeroLevelFilterType , MercenariesPvpMmrFilterType , MercenariesRoleFilterType , MercenariesStarterFilterType , } from '../../../models/mercenaries/mercenaries-filter-types' ; import { CardsFacadeService } from '../../../services/cards-facade.service' ; import { MercenariesGlobalStats , MercenariesHeroStat , MercenariesReferenceData , } from '../../../services/mercenaries/mercenaries-state-builder.service' ; import { AppUiStoreFacadeService } from '../../../services/ui-store/app-ui-store-facade.service' ; import { cdLog } from '../../../services/ui-store/app-ui-store.service' ; import { filterMercenariesHeroStats } from '../../../services/ui-store/mercenaries-ui-helper' ; import { arraysEqual , groupByFunction , sumOnArray } from '../../../services/utils' ; import { AbstractSubscriptionComponent } from '../../abstract-subscription.component' ; import { MercenaryInfo } from './mercenary-info' ; const THRESHOLD = 50 ; @ Component ( { selector : 'mercenaries-meta-hero-stats' , styleUrls : [ `../../../../css/global/components-global.scss` , `../../../../css/component/mercenaries/desktop/mercenaries-meta-hero-stats.component.scss` , ] , template : `
		<div class="mercenaries-stats" scrollable>
			<mercenaries-meta-hero-stat
				*ngFor="let stat of stats$ | async; trackBy: trackByFn"
				[stat]="stat"
			></mercenaries-meta-hero-stat>
		</div>
	` , changeDetection : ChangeDetectionStrategy . OnPush , } ) export class MercenariesMetaHeroStatsComponent extends AbstractSubscriptionComponent implements AfterContentInit { stats$ : Observable < readonly MercenaryInfo [ ] > ; constructor ( private readonly allCards , protected readonly store , protected readonly cdr ) { super ( store , cdr ) ; } ngAfterContentInit ( ) { this . stats$ = this . store . listen$ ( ( [ main , nav ] ) => main . mercenaries . globalStats , ( [ main , nav ] ) => main . mercenaries . referenceData , ( [ main , nav ] ) => main . stats . gameStats , ( [ main , nav ] ) => nav . navigationMercenaries . heroSearchString , ( [ main , nav , prefs ] ) => prefs . mercenariesActiveRoleFilter , ( [ main , nav , prefs ] ) => prefs . mercenariesActivePvpMmrFilter , ( [ main , nav , prefs ] ) => prefs . mercenariesActiveStarterFilter , ( [ main , nav , prefs ] ) => prefs . mercenariesActiveHeroLevelFilter2 ) . pipe ( filter ( ( [ globalStats , referenceData , gameStats , heroSearchString , roleFilter , mmrFilter , starterFilter , levelFilter , ] ) => ! ! globalStats ?. pvp ?. heroStats ?. length ) , map ( ( [ globalStats , referenceData , gameStats , heroSearchString , roleFilter , mmrFilter , starterFilter , levelFilter , ] ) => [ globalStats , referenceData , gameStats . stats . filter ( ( stat ) => stat . scenarioId === ScenarioId . LETTUCE_PVP ) , heroSearchString , roleFilter , mmrFilter , starterFilter , levelFilter , ] as [ MercenariesGlobalStats , MercenariesReferenceData , readonly GameStat [ ] , string , MercenariesRoleFilterType , MercenariesPvpMmrFilterType , MercenariesStarterFilterType , MercenariesHeroLevelFilterType ] ) , distinctUntilChanged ( ( a , b ) => arraysEqual ( a , b ) ) , tap ( ( info ) => console . debug ( 'filtering' , info ) ) , map ( ( [ globalStats , referenceData , gameStats , heroSearchString , roleFilter , mmrFilter , starterFilter , levelFilter , ] ) => { return [ filterMercenariesHeroStats ( globalStats . pvp . heroStats , 'pvp' , roleFilter , null , mmrFilter , starterFilter , levelFilter , this . allCards , referenceData , heroSearchString ) , roleFilter , heroSearchString , referenceData , ] as [ readonly MercenariesHeroStat [ ] , MercenariesRoleFilterType , string , MercenariesReferenceData ] ; } ) , map ( ( [ heroStats , roleFilter , heroSearchString , referenceData ] ) => { console . debug ( 'heroStats' , heroStats ) ; const heroStatsByHero = groupByFunction ( ( stat ) => stat . heroCardId ) ( heroStats ) ; const totalMatches = sumOnArray ( heroStats , ( stat ) => stat . totalMatches ) ; return Object . keys ( heroStatsByHero ) . map ( ( heroCardId ) => { const heroStats = heroStatsByHero [ heroCardId ] ; const refHeroStat = heroStats [ 0 ] ; const globalTotalMatches = sumOnArray ( heroStats , ( stat ) => stat . totalMatches ) ; return { id : heroCardId , name : this . allCards . getCard ( heroCardId ) ?. name ?? heroCardId , role : refHeroStat . heroRole , globalTotalMatches : globalTotalMatches , globalWinrate : globalTotalMatches === 0 ? null : ( 100 * sumOnArray ( heroStats , ( stat ) => stat . totalWins ) ) / globalTotalMatches , globalPopularity : ( 100 * globalTotalMatches ) / totalMatches , } as MercenaryInfo ; } ) . filter ( ( stat ) => stat . globalTotalMatches > THRESHOLD ) . sort ( ( a , b ) => b . globalWinrate - a . globalWinrate ) ; } ) , tap ( ( filter ) => setTimeout ( ( ) => { if ( ! ( this . cdr as ViewRef ) ?. destroyed ) { this . cdr . detectChanges ( ) ; } } , 0 ) ) , tap ( ( info ) => cdLog ( 'emitting stats in ' , this . constructor . name , info ) ) , takeUntil ( this . destroyed$ ) ) ; } trackByFn ( index , item ) { return item . id ; } }