package com.racingapp.pitlog.data import org.json.JSONArray import org.json.JSONObject data class SyncResponse( val rev: Long, val accepted: Set, val profile: Car?, val nights: List, val runs: List, val sheets: List, ) fun encodeSync( rev: Long, profile: Car, nights: List, runs: List, sheets: List, ): String = JSONObject() .put("rev", rev) .put("profile", profileJson(profile)) .put("nights", JSONArray().apply { nights.forEach { put(nightJson(it)) } }) .put("runs", JSONArray().apply { runs.forEach { put(runJson(it)) } }) .put("sheets", JSONArray().apply { sheets.forEach { put(sheetJson(it)) } }) .toString() fun decodeSync(body: String): SyncResponse { val json = JSONObject(body) val accepted = buildSet { val array = json.optJSONArray("accepted") ?: JSONArray() for (index in 0 until array.length()) add(array.optString(index)) } return SyncResponse( rev = json.optLong("rev"), accepted = accepted, profile = if (json.isNull("profile")) null else profileFrom(json.getJSONObject("profile")), nights = json.optJSONArray("nights").toList { nightFrom(it) }, runs = json.optJSONArray("runs").toList { runFrom(it) }, sheets = json.optJSONArray("sheets").toList { sheetFrom(it) }, ) } private fun profileJson(profile: Car) = JSONObject() .put("carName", profile.carName) .put("raceClass", profile.raceClass) .put("discipline", profile.discipline) .put("updatedAt", profile.updatedAt) private fun profileFrom(json: JSONObject) = Car( carName = json.optString("carName"), raceClass = json.optString("raceClass"), discipline = json.optString("discipline"), updatedAt = json.optLong("updatedAt"), ) private fun nightJson(night: RaceNight) = JSONObject() .put("syncId", night.syncId) .put("updatedAt", night.updatedAt) .put("deleted", night.deleted) .put("dateEpochDay", night.dateEpochDay) .put("trackName", night.trackName) .put("carName", night.carName) .put("raceClass", night.raceClass) .put("discipline", night.discipline) .putNullable("airTempF", night.airTempF) .put("notes", night.notes) .put("createdAt", night.createdAt) .putNullable("crossPercent", night.crossPercent) .putNullable("springLf", night.springLf) .putNullable("springRf", night.springRf) .putNullable("springLr", night.springLr) .putNullable("springRr", night.springRr) .putNullable("camberLf", night.camberLf) .putNullable("camberRf", night.camberRf) .putNullable("casterLf", night.casterLf) .putNullable("casterRf", night.casterRf) .putNullable("swayBarHole", night.swayBarHole) .putNullable("blockLr", night.blockLr) .putNullable("blockRr", night.blockRr) private fun nightFrom(json: JSONObject) = RaceNight( dateEpochDay = json.optLong("dateEpochDay"), trackName = json.optString("trackName"), carName = json.optString("carName"), raceClass = json.optString("raceClass"), discipline = json.optString("discipline"), airTempF = json.optionalInt("airTempF"), notes = json.optString("notes"), createdAt = json.optLong("createdAt"), crossPercent = json.optionalDouble("crossPercent"), springLf = json.optionalInt("springLf"), springRf = json.optionalInt("springRf"), springLr = json.optionalInt("springLr"), springRr = json.optionalInt("springRr"), camberLf = json.optionalDouble("camberLf"), camberRf = json.optionalDouble("camberRf"), casterLf = json.optionalDouble("casterLf"), casterRf = json.optionalDouble("casterRf"), swayBarHole = json.optionalInt("swayBarHole"), blockLr = json.optionalDouble("blockLr"), blockRr = json.optionalDouble("blockRr"), syncId = json.optString("syncId"), updatedAt = json.optLong("updatedAt"), deleted = json.optBoolean("deleted"), pending = false, ) private fun runJson(run: RunLog) = JSONObject() .put("syncId", run.syncId) .put("nightSyncId", run.nightSyncId) .put("updatedAt", run.updatedAt) .put("deleted", run.deleted) .put("session", run.session) .put("tireSet", run.tireSet) .putNullable("laps", run.laps) .putNullable("finish", run.finish) .putNullable("staggerInches", run.staggerInches) .putNullable("coldLf", run.coldLf) .putNullable("coldRf", run.coldRf) .putNullable("coldLr", run.coldLr) .putNullable("coldRr", run.coldRr) .putNullable("hotLf", run.hotLf) .putNullable("hotRf", run.hotRf) .putNullable("hotLr", run.hotLr) .putNullable("hotRr", run.hotRr) .putNullable("lfInside", run.lfInside) .putNullable("lfMiddle", run.lfMiddle) .putNullable("lfOutside", run.lfOutside) .putNullable("rfInside", run.rfInside) .putNullable("rfMiddle", run.rfMiddle) .putNullable("rfOutside", run.rfOutside) .putNullable("lrInside", run.lrInside) .putNullable("lrMiddle", run.lrMiddle) .putNullable("lrOutside", run.lrOutside) .putNullable("rrInside", run.rrInside) .putNullable("rrMiddle", run.rrMiddle) .putNullable("rrOutside", run.rrOutside) .put("handling", run.handling) .put("handlingWhere", run.handlingWhere) .put("notes", run.notes) .put("recordedAt", run.recordedAt) private fun runFrom(json: JSONObject) = RunLog( nightId = 0, session = json.optString("session"), tireSet = json.optString("tireSet"), laps = json.optionalInt("laps"), finish = json.optionalInt("finish"), staggerInches = json.optionalDouble("staggerInches"), coldLf = json.optionalDouble("coldLf"), coldRf = json.optionalDouble("coldRf"), coldLr = json.optionalDouble("coldLr"), coldRr = json.optionalDouble("coldRr"), hotLf = json.optionalDouble("hotLf"), hotRf = json.optionalDouble("hotRf"), hotLr = json.optionalDouble("hotLr"), hotRr = json.optionalDouble("hotRr"), lfInside = json.optionalInt("lfInside"), lfMiddle = json.optionalInt("lfMiddle"), lfOutside = json.optionalInt("lfOutside"), rfInside = json.optionalInt("rfInside"), rfMiddle = json.optionalInt("rfMiddle"), rfOutside = json.optionalInt("rfOutside"), lrInside = json.optionalInt("lrInside"), lrMiddle = json.optionalInt("lrMiddle"), lrOutside = json.optionalInt("lrOutside"), rrInside = json.optionalInt("rrInside"), rrMiddle = json.optionalInt("rrMiddle"), rrOutside = json.optionalInt("rrOutside"), handling = json.optString("handling"), handlingWhere = json.optString("handlingWhere"), notes = json.optString("notes"), recordedAt = json.optLong("recordedAt"), syncId = json.optString("syncId"), nightSyncId = json.optString("nightSyncId"), updatedAt = json.optLong("updatedAt"), deleted = json.optBoolean("deleted"), pending = false, ) private fun sheetJson(sheet: SetupSheet) = JSONObject() .put("syncId", sheet.syncId) .put("updatedAt", sheet.updatedAt) .put("deleted", sheet.deleted) .put("recordedAt", sheet.recordedAt) .put("note", sheet.note) .putNullable("crossPercent", sheet.crossPercent) .putNullable("springLf", sheet.springLf) .putNullable("springRf", sheet.springRf) .putNullable("springLr", sheet.springLr) .putNullable("springRr", sheet.springRr) .putNullable("camberLf", sheet.camberLf) .putNullable("camberRf", sheet.camberRf) .putNullable("casterLf", sheet.casterLf) .putNullable("casterRf", sheet.casterRf) .putNullable("swayBarHole", sheet.swayBarHole) .putNullable("blockLr", sheet.blockLr) .putNullable("blockRr", sheet.blockRr) .putNullable("toeFront", sheet.toeFront) .putNullable("toeRear", sheet.toeRear) private fun sheetFrom(json: JSONObject) = SetupSheet( recordedAt = json.optLong("recordedAt"), note = json.optString("note"), crossPercent = json.optionalDouble("crossPercent"), springLf = json.optionalInt("springLf"), springRf = json.optionalInt("springRf"), springLr = json.optionalInt("springLr"), springRr = json.optionalInt("springRr"), camberLf = json.optionalDouble("camberLf"), camberRf = json.optionalDouble("camberRf"), casterLf = json.optionalDouble("casterLf"), casterRf = json.optionalDouble("casterRf"), swayBarHole = json.optionalInt("swayBarHole"), blockLr = json.optionalDouble("blockLr"), blockRr = json.optionalDouble("blockRr"), toeFront = json.optionalDouble("toeFront"), toeRear = json.optionalDouble("toeRear"), syncId = json.optString("syncId"), updatedAt = json.optLong("updatedAt"), deleted = json.optBoolean("deleted"), pending = false, ) private fun JSONObject.putNullable(key: String, value: Any?): JSONObject = put(key, value ?: JSONObject.NULL) private fun JSONObject.optionalInt(key: String): Int? = if (isNull(key)) null else optInt(key) private fun JSONObject.optionalDouble(key: String): Double? = if (isNull(key)) null else optDouble(key) private fun JSONArray?.toList(read: (JSONObject) -> T): List { if (this == null) return emptyList() return buildList { for (index in 0 until length()) add(read(getJSONObject(index))) } }