package org.jeudego.pairgoth.test

import com.republicate.kson.Json
import org.jeudego.pairgoth.model.Game
import org.jeudego.pairgoth.model.ID
import org.jeudego.pairgoth.model.fromJson
import org.jeudego.pairgoth.pairing.solver.BaseSolver
import org.jeudego.pairgoth.store.MemoryStore
import org.jeudego.pairgoth.store.lastPlayerId
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import java.io.File
import java.io.FileWriter
import java.io.PrintWriter
import java.nio.charset.StandardCharsets
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.test.assertNotNull
import kotlin.test.assertTrue

//@Disabled("pairings differ")
class PairingTests: TestBase() {

    @BeforeEach
    fun reset() {
        MemoryStore.reset()
    }

    fun compare_weights(file1: File, file2: File, skipSeeding: Boolean = false):Boolean {
        BaseSolver.weightsLogger!!.flush()
        // Maps to store name pairs and costs
        val map1 = HashMap<Pair<String, String>, List<Double>>()
        val map2 = HashMap<Pair<String, String>, List<Double>>()
        var count: Int = 1

        for (file in listOf(file1, file2)) {

            // Read lines
            val lines = file.readLines()

            // Store headers
            val header1 = lines[0]
            val header2 = lines[1]

            logger.info("Reading weights file "+file)

            // Loop through sections
            for (i in 2..lines.size-1 step 12) {
                // Get name pair
                val name1 = lines[i].split("=")[1]
                val name2 = lines[i+1].split("=")[1]

                // Nested loop over costs
                val costs = mutableListOf<Double>()
                for (j in i + 2..i + 11) {
                    val parts = lines[j].split("=")
                    costs.add(parts[1].toDouble())
                }

                val tmp_pair = if (name1 > name2) Pair(name1,name2) else Pair(name2,name1)
                // Add to map
                if (count == 1) {
                    map1[tmp_pair] = costs
                } else {
                    map2[tmp_pair] = costs
                }
            }
            count += 1

        }

        var identical = true
        for ((key, value) in map1) {
            // Check if key exists in both
            if (map2.containsKey(key)) {
                // Compare values
                //logger.info("Comparing $key")
                val isValid = if (!skipSeeding) {
                    abs(value!![9] - map2[key]!![9])>10 && identical==true
                } else {
                    abs((value!![9]-value!![6]-value!![5]) - (map2[key]!![9]-map2[key]!![6]-map2[key]!![5]))>10 && identical==true
                }
                if (isValid) {
                    // Key exists but values differ - print key
                    logger.info("Difference found at $key")
                    logger.info("baseDuplicateGameCost =   "+value!![0].toString()+"   "+map2[key]!![0].toString())
                    logger.info("baseRandomCost        =   "+value!![1].toString()+"   "+map2[key]!![1].toString())
                    logger.info("baseBWBalanceCost     =   "+value!![2].toString()+"   "+map2[key]!![2].toString())
                    logger.info("mainCategoryCost      =   "+value!![3].toString()+"   "+map2[key]!![3].toString())
                    logger.info("mainScoreDiffCost     =   "+value!![4].toString()+"   "+map2[key]!![4].toString())
                    logger.info("mainDUDDCost          =   "+value!![5].toString()+"   "+map2[key]!![5].toString())
                    logger.info("mainSeedCost          =   "+value!![6].toString()+"   "+map2[key]!![6].toString())
                    logger.info("secHandiCost          =   "+value!![7].toString()+"   "+map2[key]!![7].toString())
                    logger.info("secGeoCost            =   "+value!![8].toString()+"   "+map2[key]!![8].toString())
                    logger.info("totalCost             =   "+value!![9].toString()+"   "+map2[key]!![9].toString())
                    identical = false
                }
            }
        }

        //return map1==map2
        return identical
    }

    fun compare_games(games:Json.Array, opengotha:Json.Array, skipColor: Boolean = false): Boolean{
        if (games.size != opengotha.size) {
            val tmp = Game.fromJson(games.getJson(games.size-1)!!.asObject())
            if ((tmp.white != 0) and (tmp.black != 0)) {return false}
        }
        val gamesPair = mutableSetOf<Pair<ID,ID>>()
        val openGothaPair = mutableSetOf<Pair<ID,ID>>()
        for (i in 0 until opengotha.size) {
            val tmp = Game.fromJson(games.getJson(i)!!.asObject().let {
                Json.MutableObject(it).set("t", 0) // hack to fill the table to make fromJson() happy
            })
            val tmpOG = Game.fromJson(opengotha.getJson(i)!!.asObject().let {
                Json.MutableObject(it).set("t", 0) // hack to fill the table to make fromJson() happy
            })
            if (skipColor) {
                gamesPair.add(Pair(min(tmp.white, tmp.black), max(tmp.white, tmp.black)))
                openGothaPair.add(Pair(min(tmpOG.white, tmpOG.black), max(tmpOG.white, tmpOG.black)))
            } else {
                gamesPair.add(Pair(tmp.white, tmp.black))
                openGothaPair.add(Pair(tmpOG.white, tmpOG.black))
            }
        }
        if (gamesPair!=openGothaPair) {
            logger.info("Pairings do not match "+gamesPair.asSequence().minus(openGothaPair).map {it}.toList().toString())
        }
        return gamesPair==openGothaPair
    }

    fun compare_string(string1:String, string2:String): String{
        for (i in 0..string1.length) {
            // Check if key exists in both
            if (string1[i] != string2[i]) {
                return "at position "+i.toString()+" "+string1.substring(i-10,i+2)+" != "+string2.substring(i-10,i+2)
            }
        }
        return "strings are identical"
    }

    @Test
    fun `008 simple swiss tournament`() {
        // read tournament with pairing
        var file = getTestFile("opengotha/pairings/simpleswiss_7R.xml")
        logger.info("read from file $file")
        val resource = file.readText(StandardCharsets.UTF_8)
        var resp = TestAPI.post("/api/tour", resource)
        val id = resp.asObject().getInt("id")
        val tournament = TestAPI.get("/api/tour/$id").asObject()
        logger.info(tournament.toString().slice(0..50) + "...")
        val players = TestAPI.get("/api/tour/$id/part").asArray()
        logger.info(players.toString().slice(0..50) + "...")

        val pairingsOG = mutableListOf<String>()
        for (round in 1..tournament.getInt("rounds")!!) {
            val games = TestAPI.get("/api/tour/$id/res/$round").asArray()
            logger.info("games for round $round: {}", games.toString().slice(0..50) + "...")
            pairingsOG.add(games.toString())
        }

        for (round in tournament.getInt("rounds")!! downTo 1) {
            TestAPI.delete("/api/tour/$id/pair/$round", Json.Array("all"))
        }

        /*
        At least 2 solutions have the same sum of weights, OpenGotha pairing is
        """[{"id":698,"t":1,"w":335,"b":324,"h":0,"r":"b","dd":0},{"id":699,"t":2,"w":316,"b":311,"h":0,"r":"b","dd":0},{"id":700,"t":3,"w":337,"b":325,"h":0,"r":"b","dd":0},{"id":701,"t":4,"w":313,"b":326,"h":0,"r":"b","dd":0},{"id":702,"t":5,"w":321,"b":309,"h":0,"r":"b","dd":0},{"id":703,"t":6,"w":319,"b":317,"h":0,"r":"b","dd":0},{"id":704,"t":7,"w":340,"b":312,"h":0,"r":"b","dd":0},{"id":705,"t":8,"w":332,"b":318,"h":0,"r":"b","dd":0},{"id":706,"t":9,"w":336,"b":327,"h":0,"r":"b","dd":0},{"id":707,"t":10,"w":333,"b":322,"h":0,"r":"b","dd":0},{"id":708,"t":11,"w":315,"b":328,"h":0,"r":"b","dd":0},{"id":709,"t":12,"w":323,"b":334,"h":0,"r":"b","dd":0},{"id":710,"t":13,"w":329,"b":331,"h":0,"r":"b","dd":0},{"id":711,"t":14,"w":339,"b":310,"h":0,"r":"b","dd":0},{"id":712,"t":15,"w":338,"b":320,"h":0,"r":"b","dd":0},{"id":713,"t":16,"w":330,"b":314,"h":0,"r":"b","dd":0}]"""
        Pairgoth pairing is
        */
        pairingsOG[6] = Json.parse(
            """[{"id":810,"t":1,"w":335,"b":324,"h":0,"r":"?","dd":0},{"id":811,"t":2,"w":337,"b":325,"h":0,"r":"?","dd":1},{"id":812,"t":3,"w":316,"b":319,"h":0,"r":"?","dd":1},{"id":813,"t":4,"w":313,"b":326,"h":0,"r":"?","dd":0},{"id":814,"t":5,"w":321,"b":309,"h":0,"r":"?","dd":0},{"id":815,"t":6,"w":311,"b":317,"h":0,"r":"?","dd":1},{"id":816,"t":7,"w":340,"b":312,"h":0,"r":"?","dd":0},{"id":817,"t":8,"w":332,"b":318,"h":0,"r":"?","dd":0},{"id":818,"t":9,"w":336,"b":327,"h":0,"r":"?","dd":0},{"id":819,"t":10,"w":333,"b":322,"h":0,"r":"?","dd":0},{"id":820,"t":11,"w":315,"b":328,"h":0,"r":"?","dd":1},{"id":821,"t":12,"w":323,"b":334,"h":0,"r":"?","dd":0},{"id":822,"t":13,"w":329,"b":331,"h":0,"r":"?","dd":0},{"id":823,"t":14,"w":339,"b":310,"h":0,"r":"?","dd":0},{"id":824,"t":15,"w":338,"b":320,"h":0,"r":"?","dd":0},{"id":825,"t":16,"w":330,"b":314,"h":0,"r":"?","dd":0}]"""
        )!!.asArray().mapTo(Json.MutableArray()) {
            // adjust ids
            Json.MutableObject(it as Json.Object).also { game ->
                game["w"] = game.getInt("w")!! - 340 + lastPlayerId
                game["b"] = game.getInt("b")!! - 340 + lastPlayerId
            }
        }.toString()

        var games: Json.Array
        var firstGameID: Int

        for (round in 1..7) {
            BaseSolver.weightsLogger = PrintWriter(FileWriter(getOutputFile("weights.txt")))
            games = TestAPI.post("/api/tour/$id/pair/$round", Json.Array("all")).asArray()
            logger.info("games for round $round: {}", games.toString().slice(0..50) + "...")
            assertTrue(compare_weights(getOutputFile("weights.txt"), getTestFile("opengotha/simpleswiss_weights_R$round.txt")), "Not matching opengotha weights for round $round")
            assertTrue(compare_games(games, Json.parse(pairingsOG[round - 1])!!.asArray()),"pairings for round $round differ")
            logger.info("Pairings for round $round match OpenGotha")

            firstGameID = (games.getJson(0)!!.asObject()["id"] as Long?)!!.toInt()
            for (gameID in firstGameID..firstGameID + 15) {
                resp = TestAPI.put("/api/tour/$id/res/$round", Json.parse("""{"id":$gameID,"result":"b"}""")).asObject()
                assertTrue(resp.getBoolean("success") == true, "expecting success")
            }
            logger.info("Results succesfully entered for round $round")
        }

    }

    @Test
    fun `009 not so simple swiss tournament`() {
        // read tournament with pairing
        var file = getTestFile("opengotha/pairings/notsosimpleswiss_10R.xml")
        logger.info("read from file $file")
        val resource = file.readText(StandardCharsets.UTF_8)
        var resp = TestAPI.post("/api/tour", resource)
        val id = resp.asObject().getInt("id")
        val tournament = TestAPI.get("/api/tour/$id").asObject()
        logger.info(tournament.toString().slice(0..50) + "...")
        val players = TestAPI.get("/api/tour/$id/part").asArray()
        logger.info(players.toString().slice(0..50) + "...")

        val pairingsOG = mutableListOf<String>()
        for (round in 1..tournament.getInt("rounds")!!) {
            val games = TestAPI.get("/api/tour/$id/res/$round").asArray()
            logger.info("games for round $round: {}", games.toString().slice(0..50) + "...")
            pairingsOG.add(games.toString())
        }

        for (round in tournament.getInt("rounds")!! downTo 1) {
            TestAPI.delete("/api/tour/$id/pair/$round", Json.Array("all"))
        }

        var games: Json.Array
        var firstGameID: Int
        val playersList = mutableListOf<Long>()

        for (i in 0..34){
            playersList.add(players.getJson(i)!!.asObject()["id"] as Long)
        }

        for (round in 1..10) {
            //games = TestAPI.post("/api/tour/$id/pair/$round", Json.Array(playersList.filter{it != byePlayerList[round-1]})).asArray()
            BaseSolver.weightsLogger = PrintWriter(FileWriter(getOutputFile("weights.txt")))

            //games = TestAPI.post("/api/tour/$id/pair/$round", Json.Array(playersList.filter{it != byePlayerList[round-1]})).asArray()
            games = TestAPI.post("/api/tour/$id/pair/$round", Json.Array("all")).asArray()
            logger.info("games for round $round: {}", games.toString().slice(0..50) + "...")

            assertTrue(compare_weights(getOutputFile("weights.txt"), getTestFile("opengotha/notsosimpleswiss_weights_R$round.txt")), "Not matching opengotha weights for round $round")
            assertTrue(compare_games(games, Json.parse(pairingsOG[round - 1])!!.asArray()),"pairings for round $round differ")
            logger.info("Pairings for round $round match OpenGotha")

            firstGameID = (games.getJson(0)!!.asObject()["id"] as Long?)!!.toInt()
            for (gameID in firstGameID..firstGameID + 16) {
                resp = TestAPI.put("/api/tour/$id/res/$round", Json.parse("""{"id":$gameID,"result":"b"}""")).asObject()
                assertTrue(resp.getBoolean("success") == true, "expecting success")
            }
            logger.info("Results succesfully entered for round $round")
        }

    }

    @Test
    fun `testSimpleMM`() {
        /*
        // read tournament with pairing
        var fileOG = getTestFile("opengotha/pairings/simplemm.xml")

        logger.info("read from file $fileOG")
        val resourceOG = fileOG.readText(StandardCharsets.UTF_8)
        val respOG = TestAPI.post("/api/tour", resourceOG)
        val idOG = respOG.asObject().getInt("id")
        val tournamentOG = TestAPI.get("/api/tour/$idOG").asObject()
        //logger.info(tournamentOG.toString().slice(0..50) + "...")
        val playersOG = TestAPI.get("/api/tour/$idOG/part").asArray()
        //logger.info(players.toString().slice(0..50) + "...")
        logger.info(playersOG.toString())

        val pairingsOG = mutableListOf<Json.Array>()
        for (round in 1..tournamentOG.getInt("rounds")!!) {
            val games = TestAPI.get("/api/tour/$idOG/res/$round").asArray()
            logger.info("games for round $round: {}", games.toString())
            pairingsOG.add(games)
        }
        */

        //assert(false)


        val pairingsR1 = """[{"id":1,"w":3,"b":5,"h":0,"r":"w","dd":0},{"id":2,"w":12,"b":10,"h":0,"r":"b","dd":0},{"id":3,"w":9,"b":14,"h":0,"r":"b","dd":0},{"id":4,"w":11,"b":6,"h":0,"r":"b","dd":0},{"id":5,"w":13,"b":15,"h":0,"r":"b","dd":0},{"id":6,"w":2,"b":16,"h":1,"r":"w","dd":0},{"id":7,"w":8,"b":4,"h":5,"r":"b","dd":0},{"id":8,"w":7,"b":1,"h":2,"r":"w","dd":0}]"""
        val pairingsR2 = """[{"id":9,"w":14,"b":3,"h":0,"r":"b","dd":0},{"id":10,"w":10,"b":5,"h":0,"r":"b","dd":0},{"id":11,"w":6,"b":9,"h":0,"r":"b","dd":0},{"id":12,"w":15,"b":12,"h":0,"r":"w","dd":0},{"id":13,"w":2,"b":11,"h":0,"r":"w","dd":0},{"id":14,"w":8,"b":13,"h":0,"r":"b","dd":0},{"id":15,"w":7,"b":4,"h":0,"r":"b","dd":0},{"id":16,"w":16,"b":1,"h":7,"r":"b","dd":0}]"""
        val pairingsR3 = """[{"id":17,"w":5,"b":14,"h":0,"r":"b","dd":0},{"id":18,"w":10,"b":9,"h":0,"r":"w","dd":0},{"id":19,"w":15,"b":3,"h":0,"r":"w","dd":0},{"id":20,"w":12,"b":2,"h":0,"r":"b","dd":0},{"id":21,"w":6,"b":13,"h":0,"r":"b","dd":0},{"id":22,"w":11,"b":8,"h":0,"r":"w","dd":0},{"id":23,"w":16,"b":7,"h":3,"r":"w","dd":0},{"id":24,"w":4,"b":1,"h":3,"r":"b","dd":0}]"""
        val pairingsR4 = """[{"id":25,"w":3,"b":10,"h":0,"r":"w","dd":0},{"id":26,"w":14,"b":15,"h":0,"r":"b","dd":0},{"id":27,"w":5,"b":2,"h":0,"r":"w","dd":0},{"id":28,"w":12,"b":6,"h":0,"r":"w","dd":0},{"id":29,"w":9,"b":11,"h":0,"r":"w","dd":0},{"id":30,"w":16,"b":4,"h":3,"r":"b","dd":0},{"id":31,"w":13,"b":7,"h":5,"r":"w","dd":0},{"id":32,"w":8,"b":1,"h":6,"r":"w","dd":0}]"""
        // Opengotha R5
        // val pairingsR5 = """[{"id":33,"w":15,"b":5,"h":0,"r":"w","dd":0},{"id":34,"w":14,"b":10,"h":0,"r":"b","dd":0},{"id":35,"w":9,"b":3,"h":0,"r":"w","dd":0},{"id":36,"w":13,"b":2,"h":0,"r":"w","dd":0},{"id":37,"w":16,"b":12,"h":0,"r":"b","dd":0},{"id":38,"w":11,"b":4,"h":3,"r":"b","dd":0},{"id":39,"w":8,"b":7,"h":5,"r":"w","dd":0},{"id":40,"w":6,"b":1,"h":7,"r":"b","dd":0}]"""
        // Add a valid permutation at the end: 11-1(7) & 6-4(3) instead of 11-4(7) 6-1(3)
        val pairingsR5 = """[{"id":33,"w":15,"b":5,"h":0,"r":"w","dd":0},{"id":34,"w":14,"b":10,"h":0,"r":"b","dd":0},{"id":35,"w":9,"b":3,"h":0,"r":"w","dd":0},{"id":36,"w":13,"b":2,"h":0,"r":"w","dd":0},{"id":37,"w":16,"b":12,"h":0,"r":"b","dd":0},{"id":38,"w":11,"b":1,"h":3,"r":"b","dd":0},{"id":39,"w":8,"b":7,"h":5,"r":"w","dd":0},{"id":40,"w":6,"b":4,"h":7,"r":"b","dd":0}]"""
        val pairings = mutableListOf<String>()
        pairings.add(pairingsR1)
        pairings.add(pairingsR2)
        pairings.add(pairingsR3)
        pairings.add(pairingsR4)
        pairings.add(pairingsR5)

        // read tournament without pairings
        var file = getTestFile("opengotha/pairings/simplemm_nopairings.xml")
        logger.info("read from file $file")
        val resource = file.readText(StandardCharsets.UTF_8)
        var resp = TestAPI.post("/api/tour", resource)
        val id = resp.asObject().getInt("id")
        assertNotNull(id)
        val tournament = TestAPI.get("/api/tour/$id").asObject()
        logger.info(tournament.toString().slice(0..50) + "...")
        val players = TestAPI.get("/api/tour/$id/part").asArray()
        logger.info(players.toString().slice(0..50) + "...")

        var games: Json.Array
        var firstGameID: Int
        var forcedGames: Json.Array
        var game: Json

        for (round in 1..5) {
            BaseSolver.weightsLogger = PrintWriter(FileWriter(getOutputFile("weights.txt")))
            // games must be created and then modified by PUT
            games = TestAPI.post("/api/tour/$id/pair/$round", Json.Array("all")).asArray()
            logger.info(games.toString())
            val skipSeeding = round <= 2
            assertTrue(compare_weights(getOutputFile("weights.txt"), getTestFile("opengotha/simplemm/simplemm_weights_R$round.txt"), skipSeeding), "Not matching opengotha weights for round $round")
            logger.info("Weights for round $round match OpenGotha")

            /*
            // fix players ids (TODO - reset memstore between each test for simplicity)
            val maxId = games.flatMap { listOf((it as Json.Object).getInt("b")!!, (it as Json.Object).getInt("w")!!) }.max()
            val fixedGames = games.mapTo(Json.MutableArray()) {
                val game = it as Json.Object
                Json.MutableObject(game)
                    .set("b", game.getInt("b")!! - maxId + 16)
                    .set("w", game.getInt("w")!! - maxId + 16)
            }
             */

            assertTrue(compare_games(games, Json.parse(pairings[round - 1])!!.asArray(), skipColor=true),"pairings for round $round differ")
            logger.info("Pairing for round $round match OpenGotha")

            TestAPI.delete("/api/tour/$id/pair/$round", Json.Array("all"))

            forcedGames = Json.parse(pairings[round-1])!!.asArray()
            //forcedGames = pairingsOG[round-1]

            var fixedGames = mutableListOf<Json.Object>()
            for (j in 0..forcedGames.size-1) {
                val game = forcedGames.getJson(j)!!.asObject()
                val ret = TestAPI.post("/api/tour/$id/pair/$round", Json.Array(game.getInt("w")!!, game.getInt("b")!!)).asArray()
                fixedGames.addAll(ret.map { it as Json.Object })
            }

            // Enter results
            val results = forcedGames.map { game -> game.toString().split("r\":\"")[1][0] }
            for (j in 0 .. forcedGames.size-1) {
                resp = TestAPI.put("/api/tour/$id/res/$round", Json.parse("""{"id":${fixedGames[j].getInt("id")!!},"result":"${results[j]}"}""")).asObject()
                assertTrue(resp.getBoolean("success") == true, "expecting success")
            }

            logger.info("Results succesfully entered for round $round")
        }
    }

    @Test
    fun `testNotSimpleMM`() {
        /*
        // read tournament with pairing
        var fileOG = getTestFile("opengotha/pairings/notsimplemm.xml")

        logger.info("read from file $fileOG")
        val resourceOG = fileOG.readText(StandardCharsets.UTF_8)
        val respOG = TestAPI.post("/api/tour", resourceOG)
        val idOG = respOG.asObject().getInt("id")
        val tournamentOG = TestAPI.get("/api/tour/$idOG").asObject()
        //logger.info(tournamentOG.toString().slice(0..50) + "...")
        val playersOG = TestAPI.get("/api/tour/$idOG/part").asArray()
        //logger.info(players.toString().slice(0..50) + "...")
        logger.info(playersOG.toString())

        val pairingsOG = mutableListOf<Json.Array>()
        for (round in 1..tournamentOG.getInt("rounds")!!) {
            val games = TestAPI.get("/api/tour/$idOG/res/$round").asArray()
            logger.info("games for round $round: {}", games.toString())
            pairingsOG.add(games)
        }
        */

        //assert(false)


        val pairingsR1 = """[{"id":1,"t":1,"w":8,"b":1,"h":0,"r":"b","dd":0},{"id":2,"t":2,"w":3,"b":6,"h":0,"r":"b","dd":0},{"id":3,"t":3,"w":5,"b":2,"h":5,"r":"w","dd":0},{"id":4,"t":4,"w":9,"b":7,"h":1,"r":"b","dd":0}]"""
        val pairingsR2 = """[{"id":5,"t":1,"w":6,"b":5,"h":0,"r":"w","dd":0},{"id":6,"t":2,"w":3,"b":8,"h":0,"r":"w","dd":0},{"id":7,"t":3,"w":9,"b":2,"h":0,"r":"w","dd":0},{"id":8,"t":4,"w":7,"b":4,"h":3,"r":"w","dd":0}]"""
        val pairingsR3 = """[{"id":9,"t":1,"w":5,"b":3,"h":0,"r":"w","dd":0},{"id":10,"t":2,"w":1,"b":6,"h":0,"r":"w","dd":0},{"id":11,"t":3,"w":9,"b":4,"h":5,"r":"b","dd":0}]"""
        val pairingsR4 = """[{"id":12,"t":1,"w":5,"b":1,"h":0,"r":"w","dd":0},{"id":13,"t":2,"w":6,"b":9,"h":3,"r":"w","dd":0},{"id":14,"t":3,"w":3,"b":7,"h":3,"r":"b","dd":0},{"id":15,"t":4,"w":2,"b":4,"h":2,"r":"w","dd":0}]"""
        val pairingsR5 = """[{"id":16,"t":1,"w":6,"b":8,"h":0,"r":"w","dd":0},{"id":17,"t":2,"w":5,"b":7,"h":2,"r":"w","dd":0},{"id":18,"t":3,"w":1,"b":9,"h":3,"r":"w","dd":0},{"id":19,"t":4,"w":3,"b":4,"h":8,"r":"b","dd":0}]"""
        val pairings = mutableListOf<String>()
        pairings.add(pairingsR1)
        pairings.add(pairingsR2)
        pairings.add(pairingsR3)
        pairings.add(pairingsR4)
        pairings.add(pairingsR5)

        // read tournament without pairings
        var file = getTestFile("opengotha/pairings/notsimplemm_nopairings.xml")
        logger.info("read from file $file")
        val resource = file.readText(StandardCharsets.UTF_8)
        var resp = TestAPI.post("/api/tour", resource)
        val id = resp.asObject().getInt("id")
        assertNotNull(id)
        val tournament = TestAPI.get("/api/tour/$id").asObject()
        logger.info(tournament.toString().slice(0..50) + "...")
        val players = TestAPI.get("/api/tour/$id/part").asArray()
        logger.info(players.toString().slice(0..50) + "...")

        var games: Json.Array
        var firstGameID: Int
        var forcedGames: Json.Array
        var game: Json

        for (round in 1..5) {
            BaseSolver.weightsLogger = PrintWriter(FileWriter(getOutputFile("weights.txt")))
            // games must be created and then modified by PUT
            games = TestAPI.post("/api/tour/$id/pair/$round", Json.Array("all")).asArray()
            logger.info(games.toString())
            logger.info(pairings[round-1])
            assertTrue(compare_weights(getOutputFile("weights.txt"), getTestFile("opengotha/notsimplemm/notsimplemm_weights_R$round.txt"), true), "Not matching opengotha weights for round $round")
            logger.info("Weights for round $round match OpenGotha")

            if (round == 3) {
                // do not go further, pairings diverge here even with same weights
                break;
            }

            assertTrue(compare_games(games, Json.parse(pairings[round - 1])!!.asArray(), skipColor=true),"pairings for round $round differ")
            logger.info("Pairing for round $round match OpenGotha")

            TestAPI.delete("/api/tour/$id/pair/$round", Json.Array("all"))

            forcedGames = Json.parse(pairings[round-1])!!.asArray()
            //forcedGames = pairingsOG[round-1]

            var fixedGames = mutableListOf<Json.Object>()
            for (j in 0..forcedGames.size-1) {
                val game = forcedGames.getJson(j)!!.asObject()
                val ret = TestAPI.post("/api/tour/$id/pair/$round", Json.Array(game.getInt("w")!!, game.getInt("b")!!)).asArray()
                fixedGames.addAll(ret.map { it as Json.Object })
            }

            // Enter results
            val results = forcedGames.map { game -> game.toString().split("r\":\"")[1][0] }
            for (j in 0 .. forcedGames.size-1) {
                resp = TestAPI.put("/api/tour/$id/res/$round", Json.parse("""{"id":${fixedGames[j].getInt("id")!!},"result":"${results[j]}"}""")).asObject()
                assertTrue(resp.getBoolean("success") == true, "expecting success")
            }

            logger.info("Results succesfully entered for round $round")
        }
    }

}