guess-It-SDP/guessIt

View on GitHub
app/src/main/java/com/github/freeman/bootcamp/games/guessit/GameManagerService.kt

Summary

Maintainability
D
1 day
Test Coverage
F
0%
package com.github.freeman.bootcamp.games.guessit

import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
import com.github.freeman.bootcamp.R
import com.github.freeman.bootcamp.games.guessit.guessing.GuessingActivity
import com.github.freeman.bootcamp.utilities.firebase.FirebaseUtilities
import com.github.freeman.bootcamp.utilities.firebase.FirebaseUtilities.getGameDBRef
import com.google.firebase.auth.ktx.auth
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.ValueEventListener
import com.google.firebase.database.ktx.getValue
import com.google.firebase.ktx.Firebase
import java.util.*
import kotlin.concurrent.schedule

/***
 * This Service is launched from the WaitingRoomActivity when a game is started,
 * either because this app is the game host and the Start button was clicked,
 * or because this app was in a lobby and the game state changed to "new turn".
 * This service manages the transitions between the different activities and updates
 * the database when needed throughout the span of the game.
 */
class GameManagerService : Service() {

    companion object {
        var roundNb = 0
        var turnNb = 0
        var nbPlayers = 0
        var nbRounds = 0
        var isHost = false
        var topics = ArrayList<String>()
        var firstStart = true
        var playersOrder = listOf<String>()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (firstStart) {
            Log.d("GameManagerD", "Game Manager Started")
            val gameID = intent!!.getStringExtra(getString(R.string.gameId_extra)).toString()
            val gameDBRef = getGameDBRef(this, gameID)
            // Get the number of rounds and number of players
            FirebaseUtilities.databaseGet(gameDBRef.child(getString(R.string.param_nb_rounds_path)))
                .thenAccept { getNbRounds ->
                    nbRounds = getNbRounds.toInt()
                    FirebaseUtilities.databaseGet(gameDBRef.child(getString(R.string.param_nb_players_path)))
                        .thenAccept { getNbPlayers ->
                            nbPlayers = getNbPlayers.toInt()
                            // Get local player ID and host ID to check if this app is the host app
                            val localPlayerID = Firebase.auth.currentUser?.uid.toString()
                            FirebaseUtilities.databaseGet(gameDBRef.child(getString(R.string.param_host_id_path)))
                                .thenAccept { hostID ->
                                    isHost = localPlayerID == hostID.toString()

                                    // Get the list of topics
                                    for (i in 0 until GameOptionsActivity.NB_TOPICS) {
                                        topics.add(intent.getStringExtra("topic$i").toString())
                                    }

                                    // Listen to the current game state to start corresponding activities
                                    // Activities are responsible for closing themselves based on current the game state
                                    val currentStateRef = gameDBRef.child(getString(R.string.current_state_path))
                                    currentStateRef.addValueEventListener(object : ValueEventListener {
                                        override fun onDataChange(snapshot: DataSnapshot) {
                                            if (snapshot.exists()) {
                                                when (snapshot.getValue<String>()!!) {
                                                    getString(R.string.state_initialize) -> {
                                                        if (isHost) {
                                                            initializeGame(gameDBRef)
                                                        }
                                                    }
                                                    getString(R.string.state_setartist) -> {
                                                        if (isHost) {
                                                            setNewArtist(gameDBRef)
                                                        }
                                                    }
                                                    getString(R.string.state_newturn) -> {
                                                        startNewTurn(localPlayerID, gameID, gameDBRef)
                                                    }
                                                    getString(R.string.state_scorerecap) -> {
                                                        scoreRecap(gameID)
                                                        prepareNewTurn(gameDBRef)
                                                    }
                                                    getString(R.string.state_gameover) -> {
                                                        gameOver(gameID)
                                                    }
                                                    getString(R.string.state_playturn) -> {
                                                        setTimerInProgress(gameDBRef)
                                                    }
                                                }
                                            }
                                        }
                                        override fun onCancelled(error: DatabaseError) {
                                            // do nothing
                                        }
                                    })

                                    // Change game state to score recap or game over if the timer is over
                                    val currentTimerRef = gameDBRef.child(getString(R.string.current_timer_path))
                                    currentTimerRef.addValueEventListener(object : ValueEventListener {
                                        override fun onDataChange(snapshot: DataSnapshot) {
                                            if (snapshot.exists()) {
                                                val timerState = snapshot.getValue<String>()!!
                                                if (timerState == getString(R.string.timer_over)) {
                                                    endTurn(currentStateRef, currentTimerRef)
                                                }
                                            }
                                        }
                                        override fun onCancelled(error: DatabaseError) {
                                            // do nothing
                                        }
                                    })

                                    // Change game state to score recap or game over if everyone guessed the word
                                    val correctGuessesRef = gameDBRef.child(getString(R.string.current_correct_guesses_path))
                                    correctGuessesRef.addValueEventListener(object : ValueEventListener {
                                        override fun onDataChange(snapshot: DataSnapshot) {
                                            if (snapshot.exists()) {
                                                val correctGuesses = snapshot.getValue<Long>()!!.toInt()
                                                if (correctGuesses == nbPlayers - 1) {
                                                    endTurn(currentStateRef, currentTimerRef)
                                                }
                                            }
                                        }
                                        override fun onCancelled(error: DatabaseError) {
                                            // do nothing
                                        }
                                    })
                                }
                        }
                }
            firstStart = false
        }
        return START_NOT_STICKY
    }

    // Override required to extend Service
    override fun onBind(intent: Intent?): IBinder? {
        return null
    }

    private fun setTimerInProgress(gameDBRef: DatabaseReference) {
        gameDBRef.child(getString(R.string.current_timer_path)).setValue(getString(R.string.timer_inprogress))
    }

    private fun initializeGame(gameDBRef: DatabaseReference) {
        // Create players order that will be used for the rest of the game
        // First get all the player IDs
        FirebaseUtilities.databaseGetMap(gameDBRef.child(getString(R.string.players_path)))
            .thenAccept {
                // Randomly shuffle the player IDs
                @Suppress("UNCHECKED_CAST")
                playersOrder = it.keys.toList() as List<String>
                playersOrder = playersOrder.shuffled()
                Log.d("GameManagerD", "Players order: $playersOrder")
                // Change game state to start the game
                gameDBRef.child(getString(R.string.current_state_path)).setValue(getString(R.string.state_setartist))
                Log.d("GameManagerD", "New turn state set (initialization)")
            }
    }

    // Starts the correct activity between the Topic Selection Activity and the Guessing Activity
    private fun startNewTurn(localPlayerID : String, gameID : String, gameDBRef : DatabaseReference) {
        // Get current artist
        FirebaseUtilities.databaseGet(gameDBRef.child(getString(R.string.current_artist_path)))
            .thenAccept {
                val currentArtistID = it
                Log.d("GameManagerD", "Current Artist: $it")
                val intent : Intent
                if (localPlayerID == currentArtistID) {
                    // Launch the topic selection activity
                    intent = Intent(this, TopicSelectionActivity::class.java)
                    intent.apply {
                        for (i in topics.indices) {
                            putExtra("topic$i", topics[i])
                        }
                    }
                    Log.d("GameManagerD", "Topic selection launched")
                } else {
                    // Launch the guessing activity
                    intent = Intent(this, GuessingActivity::class.java)
                    Log.d("GameManagerD", "Guessing Activity launched")
                }
                intent.putExtra(getString(R.string.gameId_extra), gameID)
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                startActivity(intent)
            }
    }

    private fun scoreRecap(gameID: String) {
        val intent = Intent(this, ScoreActivity2::class.java)
        intent.putExtra(getString(R.string.gameId_extra), gameID)
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        startActivity(intent)
    }

    private fun endTurn(currentStateRef: DatabaseReference, currentTimerRef: DatabaseReference) {
        Log.d("GameManagerD", "Round number : $roundNb, Turn number : $turnNb")
        currentTimerRef.setValue(getString(R.string.timer_unused))
        if (roundNb == nbRounds - 1 && turnNb == nbPlayers - 1) {
            Timer().schedule(2000) {
                currentStateRef.setValue(getString(R.string.state_gameover))
            }
        } else {
            Timer().schedule(2000) {
                currentStateRef.setValue(getString(R.string.state_scorerecap))
            }
        }
    }

    private fun prepareNewTurn(gameDBRef: DatabaseReference) {
        if (turnNb == nbPlayers - 1) {
            turnNb = 0
            roundNb += 1
        } else {
            turnNb += 1
        }
        if (isHost) {
            // Set the number of correct guesses to 0
            gameDBRef.child(getString(R.string.current_correct_guesses_path)).setValue(0)
            // Delete all the guesses
            gameDBRef.child(getString(R.string.guesses_path)).removeValue()
            // Change game state to start new turn
            // (wait 10 seconds so that players have time to see their scores)
            Timer().schedule(20000) {
                gameDBRef.child(getString(R.string.current_state_path)).setValue(getString(R.string.state_setartist))
                Log.d("GameManagerD", "New turn state set")
            }
        }
    }

    private fun setNewArtist(gameDBRef : DatabaseReference) {
        Log.d("GameManagerD", "Setting new artist")
        Log.d("GameManagerD", "PO: $playersOrder")
        gameDBRef.child(getString(R.string.current_artist_path)).setValue(playersOrder[turnNb]).addOnSuccessListener {
            gameDBRef.child(getString(R.string.current_state_path)).setValue(getString(R.string.state_newturn))
        }
    }

    private fun gameOver(gameID: String) {
        if (isHost) {
            VideoCreator.createRecap(this, gameID)
        }
        val intent = Intent(this, FinalActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        intent.putExtra(getString(R.string.gameId_extra), gameID)
        startActivity(intent)
//        stopSelf()
    }
}