import _ from "lodash" ; import type { FrameEntryType , FramesType , GameStartType } from "../types" ; import { Frames } from "../types" ; export interface StatComputer < T > { setup ( settings ) : void ; processFrame ( newFrame , allFrames ) : void ; fetch ( ) : T ; } export interface StatOptions { processOnTheFly : boolean ; } const defaultOptions = { processOnTheFly : false , } ; export class Stats { private options : StatOptions ; private lastProcessedFrame : number | null = null ; private frames : FramesType = { } ; private players : number [ ] = [ ] ; private allComputers = new Array < StatComputer < unknown > > ( ) ; public constructor ( options ? ) { this . options = Object . assign ( { } , defaultOptions , options ) ; } /**
     * Should reset the frames to their default values.
     */ public setup ( settings ) { this . frames = { } ; this . players = settings . players . map ( ( v ) => v . playerIndex ) ; this . allComputers . forEach ( ( comp ) => comp . setup ( settings ) ) ; } public register ( ... computer ) { this . allComputers . push ( ... computer ) ; } public process ( ) { if ( this . players . length === 0 ) { return ; } let i = this . lastProcessedFrame !== null ? this . lastProcessedFrame + 1 : Frames . FIRST ; while ( this . frames [ i ] ) { const frame = this . frames [ i ] ; if ( ! isCompletedFrame ( this . players , frame ) ) { return ; } this . allComputers . forEach ( ( comp ) => comp . processFrame ( frame , this . frames ) ) ; this . lastProcessedFrame = i ; i ++ ; } } public addFrame ( frame ) { this . frames [ frame . frame ] = frame ; if ( this . options . processOnTheFly ) { this . process ( ) ; } } } function isCompletedFrame ( players , frame ) { for ( const player of players ) { const playerPostFrame = _ . get ( frame , [ "players" , player , "post" ] ) ; if ( ! playerPostFrame ) { return false ; } } return true ; }