import { HttpClient } from '@angular/common/http' ; import { Injectable } from '@angular/core' ; import { combineLatest , forkJoin , Observable , of , from } from 'rxjs' ; import { map , take } from 'rxjs/operators' ; import { CONFIG } from '../../environments/environment' ; import { Game , GameOperations , GameOptions , GameStatus , PlayerQnA , Question , ReportQuestion , User } from '../../shared/model' ; import { DbService } from './../db-service' ; @ Injectable ( ) export class GameService { constructor ( private http , private dbService ) { } createNewGame ( gameOptions , user ) { const url = `${ CONFIG . functionsUrl }/game` ; const payload = { gameOptions : gameOptions , userId : user . userId } ; return this . http . post < string > ( url , payload ) ; } getActiveGames ( user ) { if ( user && user . userId ) { const queryParams1 = { condition : [ { name : 'playerId_0' , comparator : '==' , value : user . userId } , { name : 'gameOver' , comparator : '==' , value : false } , { name : 'GameStatus' , comparator : 'in' , value : [ GameStatus . STARTED , GameStatus . RESTARTED , GameStatus . WAITING_FOR_NEXT_Q , GameStatus . AVAILABLE_FOR_OPPONENT , GameStatus . WAITING_FOR_FRIEND_INVITATION_ACCEPTANCE , GameStatus . WAITING_FOR_RANDOM_PLAYER_INVITATION_ACCEPTANCE , GameStatus . JOINED_GAME ] } ] } ; const userGames = this . dbService . valueChanges ( 'games' , '' , queryParams1 ) ; const queryParams2 = { condition : [ { name : 'playerId_1' , comparator : '==' , value : user . userId } , { name : 'gameOver' , comparator : '==' , value : false } , { name : 'GameStatus' , comparator : 'in' , value : [ GameStatus . JOINED_GAME , GameStatus . WAITING_FOR_NEXT_Q ] } ] } ; const OtherGames = this . dbService . valueChanges ( 'games' , '' , queryParams2 ) ; return combineLatest ( [ userGames , OtherGames ] ) . pipe ( map ( games => [ ] . concat . apply ( [ ] , games ) ) , map ( gs => gs . map ( g => Game . getViewModel ( g ) ) ) , map ( games => { let myTurnGames = [ ] ; let notMyTurnGames = [ ] ; games . map ( gameObj => { if ( gameObj . nextTurnPlayerId === user . userId ) { myTurnGames . push ( gameObj ) ; } else { notMyTurnGames . push ( gameObj ) ; } } ) ; myTurnGames = myTurnGames . sort ( ( a , b ) => a . turnAt - b . turnAt ) ; notMyTurnGames = notMyTurnGames . sort ( ( a , b ) => b . turnAt - a . turnAt ) ; return myTurnGames . concat ( notMyTurnGames ) ; } ) ) ; } else { return of < Game [ ] > ( [ ] ) ; } } getGame ( gameId ) { return this . dbService . valueChanges ( 'games' , gameId ) . pipe ( map ( g => { g [ 'id' ] = ( g [ 'id' ] ) ? g [ 'id' ] : gameId ; return Game . getViewModel ( g ) ; } ) ) ; } getNextQuestion ( game ) { const url = `${ CONFIG . functionsUrl }/question/next/` ; return this . http . post < Question > ( url + game . gameId , { } ) ; } addPlayerQnAToGame ( gameId , playerQnA ) { const url = `${ CONFIG . functionsUrl }/game/${ gameId }` ; const payload = { playerQnA : playerQnA , operation : GameOperations . CALCULATE_SCORE } ; return this . http . put < any > ( url , payload ) ; } setGameOver ( gameId ) { return this . http . put ( `${ CONFIG . functionsUrl }/game/${ gameId }` , { operation : GameOperations . GAME_OVER } ) ; } updateGameRound ( gameId ) { return this . http . put ( `${ CONFIG . functionsUrl }/game/${ gameId }` , { operation : GameOperations . UPDATE_ROUND } ) ; } rejectGameInvitation ( gameId ) { return this . http . put ( `${ CONFIG . functionsUrl }/game/${ gameId }` , { operation : GameOperations . REJECT_GAME } ) ; } getGameResult ( user ) { if ( user && user . userId ) { const queryParams1 = { condition : [ { name : 'playerId_0' , comparator : '==' , value : user . userId } , { name : 'gameOver' , comparator : '==' , value : true } , ] , orderBy : [ { name : 'gameOverAt' , value : 'desc' } ] , limit : 20 } ; const query1 = this . dbService . valueChanges ( 'games' , '' , queryParams1 ) ; const queryParams2 = { condition : [ { name : 'playerId_1' , comparator : '==' , value : user . userId } , { name : 'gameOver' , comparator : '==' , value : true } , ] , orderBy : [ { name : 'gameOverAt' , value : 'desc' } ] , limit : 20 } ; const query2 = this . dbService . valueChanges ( 'games' , '' , queryParams2 ) ; return combineLatest ( query1 , query2 ) . pipe ( map ( ( data ) => data [ 1 ] . concat ( data [ 0 ] ) ) , map ( gs => gs . map ( g => Game . getViewModel ( g ) ) . sort ( ( a , b ) => b . gameOverAt - a . gameOverAt ) ) ) ; } else { return of < Game [ ] > ( [ ] ) ; } } getGameInvites ( user ) { if ( user && user . userId ) { const queryParams1 = { condition : [ { name : 'GameStatus' , comparator : '==' , value : GameStatus . WAITING_FOR_FRIEND_INVITATION_ACCEPTANCE } , { name : 'playerId_1' , comparator : '==' , value : user . userId } , { name : 'gameOver' , comparator : '==' , value : false } ] , orderBy : [ { name : 'turnAt' , value : 'desc' } ] , limit : 20 , } ; const query1 = this . dbService . valueChanges ( 'games' , '' , queryParams1 ) ; const queryParams2 = { condition : [ { name : 'GameStatus' , comparator : '==' , value : GameStatus . WAITING_FOR_RANDOM_PLAYER_INVITATION_ACCEPTANCE } , { name : 'playerId_1' , comparator : '==' , value : user . userId } , { name : 'gameOver' , comparator : '==' , value : false } ] , orderBy : [ { name : 'turnAt' , value : 'desc' } ] , limit : 20 , } ; const query2 = this . dbService . valueChanges ( 'games' , '' , queryParams2 ) ; return combineLatest ( query1 , query2 ) . pipe ( map ( ( data ) => data [ 0 ] . concat ( data [ 1 ] ) ) , map ( gs => gs . map ( g => Game . getViewModel ( g ) ) . sort ( ( a , b ) => b . turnAt - a . turnAt ) ) ) ; } else { return of < Game [ ] > ( [ ] ) ; } } checkUserQuestion ( playerQnA ) { return this . http . post < Question > ( `${ CONFIG . functionsUrl }/question/${ playerQnA . questionId }` , { playerQnA : playerQnA } ) ; } getUsersAnsweredQuestion ( userId , game ) { const observables = [ ] ; for ( const playerQnA of game . playerQnAs ) { if ( playerQnA . playerId === userId ) { observables . push ( this . checkUserQuestion ( playerQnA ) ) ; } } return forkJoin ( observables ) ; } saveReportQuestion ( report , game ) { const dbReport = Object . assign ( { } , report ) ; const queryParams = { condition : [ { name : 'created_uid' , comparator : '==' , value : report . created_uid } , { name : 'gameId' , comparator : '==' , value : report . gameId } ] } ; return this . dbService . valueChanges ( 'report_questions' , '' , queryParams ) . pipe ( take ( 1 ) , map ( question => { if ( question . length > 0 ) { const reportQuestion = new ReportQuestion ( ) ; reportQuestion . questions = question [ 0 ] [ 'questions' ] ; const key = Object . keys ( report . questions ) [ 0 ] ; reportQuestion . questions [ key ] = report . questions [ key ] ; return of ( this . dbService . updateDoc ( 'report_questions' , dbReport . gameId , { questions : reportQuestion . questions } ) ) ; } else { return of ( this . dbService . setDoc ( 'report_questions' , dbReport . gameId , dbReport ) ) ; } } ) ) ; } updateGame ( report , game ) { let playerQnA = new PlayerQnA ( ) ; playerQnA = game . playerQnAs . filter ( info => info . questionId === Object . keys ( report . questions ) [ 0 ] ) [ 0 ] ; playerQnA . isReported = true ; const url = `${ CONFIG . functionsUrl }/game/${ game . gameId }` ; const payload = { playerQnA : playerQnA , operation : GameOperations . REPORT_STATUS } ; return this . http . put < any > ( url , payload ) ; } userReaction ( questionId , userId , status ) { const collection = `questions/${ questionId }/reactions` ; return from ( this . dbService . getDoc ( collection , userId ) . get ( ) ) . pipe ( map ( ( res ) => { const reaction = res . data ( ) ; if ( reaction ) { if ( status !== reaction . status ) { return from ( this . dbService . setDoc ( collection , userId , { status : status } , { updatedOn : true } ) ) ; } else { return from ( this . dbService . deleteDoc ( collection , userId ) ) ; } } else { return from ( this . dbService . setDoc ( collection , userId , { status : status } , { createdOn : true , updatedOn : true } ) ) ; } } ) ) ; } getUserReaction ( questionId , userId ) { return this . dbService . valueChanges ( `questions/${ questionId }/reactions` , userId ) ; } getQuestion ( questionId ) { return this . dbService . valueChanges ( `questions` , questionId ) ; } updateQuestionStat ( questionId , type ) { const url = `${ CONFIG . functionsUrl }/question/question-stat-update/` ; return this . http . post < Question > ( url , { questionId : questionId , type : type === 'CREATED' ? type : 'UPDATED' , update : type === 'CORRECT' ? true : false } ) ; } }