import { Store , Action , Dispatch } from 'redux' ; import { WolfState , Ability , Slot , ConvoState , OutputMessageType , SlotId , SlotData , GetSlotDataFunctions , GetStateFunctions } from '../types' ; import { getAbilitiesCompleteOnCurrentTurn , getPromptedSlotStack , getSlotBySlotId , getSlotDataByAbilityName } from '../selectors' ; import { addMessage as addMessageAction , setSlotPrompted , setAbilityStatus , resetSlotStatusByAbilityName } from '../actions' ; import { findInSlotIdItemBySlotId } from '../helpers' ; const logState = require ( 'debug' ) ( 'wolf:s4:enterState' ) ; const log = require ( 'debug' ) ( 'wolf:s4' ) ; export interface ExecuteResult { runOnComplete : ( ) => Promise < OnCompletePromiseResult < string > [ ] > ; addMessage : ( msgData : OnCompletePromiseResult < string > ) => void ; } interface OnCompletePromiseResult < T > { result : T ; abilityName : string ; } const makeSubmittedDataFromSlotData = ( slotData ) => { return slotData . reduce ( ( prev , current ) => ( { ... prev , [ current . slotName ] : current . value } ) , { } ) ; } ; param store param convoState param abilities export default function execute ( store , convoState , abilities ) { const { dispatch , getState } = store ; logState ( getState ( ) ) ; const addMessage = ( msg ) => dispatch ( addMessageAction ( { message : msg . result , abilityName : msg . abilityName , type : OutputMessageType . abilityCompleteMessage } ) ) ; let onCompleteReturnResult = null ; const abilityCompleteResult = getAbilitiesCompleteOnCurrentTurn ( getState ( ) ) ; if ( abilityCompleteResult . length > 0 ) { const valueOrPromiseArr = runAbilityOnComplete ( getState , convoState , abilities , abilityCompleteResult ) ; const allPromises = valueOrPromiseArr . map ( ( _ ) => { const { result : valueOrPromise , abilityName } = _ ; if ( typeof valueOrPromise === 'string' ) { return Promise . resolve ( { result : valueOrPromise , abilityName } ) ; } if ( ! valueOrPromise ) { return Promise . resolve ( { result : undefined , abilityName } ) ; } return valueOrPromise . then ( ( result ) => ( { result , abilityName } ) ) ; } ) ; abilityCompleteResult . forEach ( ( abilityName ) => { dispatch ( setAbilityStatus ( abilityName , true ) ) ; dispatch ( resetSlotStatusByAbilityName ( abilityName ) ) ; } ) ; onCompleteReturnResult = { runOnComplete : ( ) => Promise . all ( allPromises ) . then ( result => result . filter ( ( _ ) => _ . result ) ) as Promise < OnCompletePromiseResult < string > [ ] > , addMessage } ; } const promptedSlotStack = getPromptedSlotStack ( getState ( ) ) ; if ( promptedSlotStack [ 0 ] ) { const slot = promptedSlotStack [ 0 ] ; if ( slot . prompted ) { return ; } const slotToPrompt = getSlotBySlotId ( abilities , { slotName : slot . slotName , abilityName : slot . abilityName } ) ; if ( slotToPrompt ) { const runSlotQueryResult = runSlotQuery ( convoState , store , slotToPrompt , slot . abilityName ) ; runSlotQueryResult . forEach ( dispatch ) ; if ( onCompleteReturnResult ) { return onCompleteReturnResult ; } return ; } } if ( onCompleteReturnResult ) { return onCompleteReturnResult ; } return ; } /**
 * Execute ability.onComplete()
 */ function runAbilityOnComplete ( getState , convoState , abilities , abilitiesToComplete ) { return abilitiesToComplete . map ( ( abilityToComplete ) => { const ability = abilities . find ( ( ability ) => ability . name === abilityToComplete ) ; log ( `according to the wolfstate, ability ${ abilityToComplete }'s onComplete needs to run` ) ; if ( ! ability ) { log ( `  however, ${ abilityToComplete } is not found in the abilities definition` ) ; return { result : Promise . resolve ( ) , abilityName : abilityToComplete } ; } const abilitySlotData = getSlotDataByAbilityName ( getState ( ) , ability . name ) ; const submittedData = makeSubmittedDataFromSlotData ( abilitySlotData ) ; const getStateFuncs = { getAbilityList : ( ) => abilities } ; return { result : ability . onComplete ( convoState , submittedData , getStateFuncs ) , abilityName : ability . name } ; } ) ; } /**
 * makeGetSlotFillFunctions
 *
 * create an object that has the correct getters for slot information
 */ function makeGetSlotDataFunctions ( { getState } , abilityName ) { const wolfState = getState ( ) ; const { slotStatus , slotData } = wolfState ; return { getSlotStatus : < SlotStatus > ( slotName ) => findInSlotIdItemBySlotId ( slotStatus , { abilityName , slotName } ) , getSlotValue : < SlotData > ( slotName ) => findInSlotIdItemBySlotId ( slotData , { abilityName , slotName } ) } ; } /**
 * Execute slot.query()
 */ function runSlotQuery ( convoState , store , slot , abilityName ) { const getSlotDataFunctions = makeGetSlotDataFunctions ( store , abilityName ) ; const queryString = slot . query ( convoState , getSlotDataFunctions ) ; const message = { message : queryString , type : OutputMessageType . queryMessage , slotName : slot . name , abilityName } ; const slotId = { slotName : slot . name , abilityName } ; return [ addMessageAction ( message ) , setSlotPrompted ( slotId , true ) ] ; }