import { DialogueInvalidError } from './dialogue_invalid_error' ; import { Response } from './response' ; import { Translator } from '../translator' ; import { Message } from './message' ; export enum TurnType { Human , Bot , Branch , Wait } export class Turn { turnType : TurnType ; responses : Response [ ] ; query : string ; humanBranches : Turn [ ] [ ] = [ ] ; botBranches : Turn [ ] [ ] = [ ] ; numRunnersRequired : number ; numRunnersEntered = 0 ; waitTime : number ; constructor ( turnData , next ? ) { let data ; if ( turnData . Human ) { this . turnType = TurnType . Human ; data = turnData . Human ; } else if ( turnData . Bot ) { this . turnType = TurnType . Bot ; data = turnData . Bot ; } else if ( turnData [ '1' ] ) { this . turnType = TurnType . Branch ; data = turnData ; } else if ( turnData . Wait ) { this . turnType = TurnType . Wait ; if ( typeof turnData . Wait !== 'number' ) { throw new DialogueInvalidError ( `Expected number for Wait value, got ${ turnData . Wait }` ) ; } data = turnData . Wait ; } else { throw new DialogueInvalidError ( `No Human, Bot, or Branching keys on ${ JSON . stringify ( turnData ) }` ) ; } if ( this . turnType === TurnType . Human || this . turnType === TurnType . Bot ) { if ( typeof data === 'number' ) { data = data . toString ( ) ; } if ( typeof data === 'string' ) { data = [ data ] ; } data = data . reduce ( ( translatedData , phrase ) => { return translatedData . concat ( Translator . translate ( phrase ) ) ; } , [ ] ) ; } if ( this . turnType === TurnType . Human && data . length > 1 ) { data = data . reduce ( ( branchData , response , i ) => { branchData [ i + 1 ] = [ { Human : response } ] ; return branchData ; } , { } ) ; this . turnType = TurnType . Branch ; } switch ( this . turnType ) { case TurnType . Human : this . query = data [ 0 ] ; break ; case TurnType . Bot : this . responses = data . map ( responseData => new Response ( responseData ) ) ; break ; case TurnType . Branch : const numBranches = Object . keys ( data ) . length ; const branches = [ ... Array ( numBranches ) ] . map ( ( _ , i ) => data [ i + 1 ] ) . filter ( x => x ) . map ( branch => Turn . createTurns ( branch ) ) ; if ( branches . length !== numBranches ) { throw new DialogueInvalidError ( `Branch numbers do not go from 1 to ${ numBranches }: ${ JSON . stringify ( data ) }` ) ; } this . humanBranches = branches . filter ( branch => branch [ 0 ] . turnType === TurnType . Human ) ; this . botBranches = branches . filter ( branch => branch [ 0 ] . turnType === TurnType . Bot ) ; break ; case TurnType . Wait : this . waitTime = turnData . Wait ; break ; } this . numRunnersRequired = Math . max ( this . humanBranches . concat ( this . botBranches ) . map ( branch => branch [ 0 ] . numRunnersRequired ) . reduce ( ( totalRequired , numRequired ) => { return totalRequired + numRequired ; } , 0 ) , next ? next . numRunnersRequired : 1 ) ; } static createTurns ( turnsData ) { const turnsArrayData = ( turnsData instanceof Array ) ? turnsData : [ turnsData ] ; return turnsArrayData . slice ( ) . reverse ( ) . reduce ( ( turns , turnData ) => { turns . push ( new Turn ( turnData , turns [ turns . length - 1 ] ) ) ; return turns ; } , [ ] ) . reverse ( ) ; } /** Tests if this phrase matches this turn.
     *  Returns:
     *    - true if it is a simple text match
     *    - Turn[] when it matches a branch with runner capacity
     *      (if numRunners has not been exceeded)
     *    - [] when it matches a branch but there is no capacity
     *    - false if there is no match **/ matches ( message ) { if ( this . turnType === TurnType . Bot ) { return this . responses . some ( ( response ) => { return response . matches ( message ) ; } ) ; } else if ( this . turnType === TurnType . Branch ) { const matchingBranches = this . botBranches . filter ( ( branch ) => { const match = branch [ 0 ] . matches ( message ) ; return ( match === true || ( match instanceof Array && match . length > 0 ) ) ; } ) ; const validBranches = matchingBranches . filter ( ( branch ) => { return branch [ 0 ] . numRunnersEntered < branch [ 0 ] . numRunnersRequired ; } ) ; if ( matchingBranches . length === 0 ) { return false ; } else if ( validBranches . length === 0 ) { return [ ] ; } else { return validBranches [ 0 ] ; } } else { throw new Error ( 'matches() should not be called for Human branches' ) ; } } nextHuman ( ) { if ( ! ( this . turnType === TurnType . Branch ) ) { throw new Error ( 'can only get nextHuman for Branch turns' ) ; } return this . humanBranches . find ( ( branch ) => { return branch [ 0 ] . numRunnersEntered < branch [ 0 ] . numRunnersRequired ; } ) ; } toMatchArray ( ) { switch ( this . turnType ) { case TurnType . Bot : return this . responses . map ( response => response . original ) ; case TurnType . Branch : return this . botBranches . map ( branch => branch [ 0 ] . toMatchArray ( ) [ 0 ] ) ; default : throw new Error ( 'cannot have match array for Human' ) ; } } }