package com.example.modifier

import android.util.Log
import androidx.core.content.ContextCompat
import com.example.modifier.utils.RcsHackTool
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.apache.commons.io.FileUtils
import org.apache.commons.io.IOUtils
import java.io.File
import java.io.InterruptedIOException
import java.util.Base64

class Frida {

    companion object {
        var p: Process? = null
        var script: File? = null
        private val TAG = "FridaScript"

        suspend fun start() {
            if (p != null) {
                p!!.destroy()
                p = null
            }
            if (script != null) {
                script!!.delete()
                script = null
            }

            val context = Utils.getContext()
            val dataDir = ContextCompat.getDataDir(context)
            Utils.copyAssetFolder(context.assets, "bin", File(dataDir, "bin").path)
            val binPath = File(dataDir, "bin/frida-inject-16.3.3-android-arm64").path
            val (pid, _) = shellRun("pidof com.android.phone")
            if (!Regex("[0-9]+").matches(pid)) {
                return
            }

            val scriptContent = IOUtils.toString(context.assets.open("scripts/phone.js"), "UTF-8")
            withContext(Dispatchers.IO) {
                script = File.createTempFile("script", ".js")
                FileUtils.writeStringToFile(script, scriptContent, "UTF-8")
                Log.i(TAG, "start: $binPath -p $pid  -s $script")

                p = Runtime.getRuntime().exec("su -M")
                p!!.outputStream.bufferedWriter().use {
                    it.write("chmod +x $binPath")
                    it.newLine()
                    it.flush()
                    it.write("$binPath -p $pid  -s $script")
                    it.newLine()
                    it.flush()
                }
                coroutineScope {
                    launch {
                        try {
                            p!!.inputStream
                                .bufferedReader()
                                .useLines { lines ->
                                    lines.forEach {
                                        Log.i(TAG, it)
                                    }
                                }
                        } catch (e: Exception) {
                            e.printStackTrace()
                        }
                    }
                    launch {
                        try {
                            p!!.errorStream
                                .bufferedReader()
                                .useLines { lines ->
                                    lines.forEach {
                                        Log.e(TAG, it)
                                    }
                                }
                        } catch (e: Exception) {
                            e.printStackTrace()
                        }
                    }
                }
            }
        }

        fun stop() {
            if (p != null) {
                p!!.inputStream.close()
                p!!.errorStream.close()
                p!!.destroy()
                p = null
            }
            if (script != null) {
                script!!.delete()
                script = null
            }
        }
    }
}

suspend fun sendSmsFrida(sender: String, msg: String) {
    val context = Utils.getContext()
    try {
        val dataDir = ContextCompat.getDataDir(context)
        Utils.copyAssetFolder(context.assets, "bin", File(dataDir, "bin").path)
        val binPath = File(dataDir, "bin/frida-inject-16.3.3-android-arm64").path
        val pduBase64 =
            String(Base64.getEncoder().encode(RcsHackTool.createFakeSms(sender, msg)))
        Log.i("Modifier", "pduBase64: $pduBase64")
        val script = IOUtils.toString(context.assets.open("scripts/sms.js"), "UTF-8")
            .replace("{pduBase64}", pduBase64)

        val tmpFile: File
        withContext(Dispatchers.IO) {
            tmpFile = File.createTempFile("script", ".js")
            FileUtils.writeStringToFile(tmpFile, script, "UTF-8")
        }

        val pid = Utils.runAsRoot("pidof com.android.phone").trim()
        if (!Regex("[0-9]+").matches(pid)) {
            return
        }

        Log.i("Modifier", "sendSms:  $binPath -p $pid  -s $tmpFile")

        val p = withContext(Dispatchers.IO) {
            Runtime.getRuntime().exec("su -M")
        }
        p.outputStream.bufferedWriter().use {
            it.write("chmod +x $binPath")
            it.newLine()
            it.flush()
            it.write("$binPath -p $pid  -s $tmpFile")
            it.newLine()
            it.flush()
        }
        coroutineScope {
            launch {
                p.errorStream.bufferedReader().useLines { lines ->
                    lines.forEach {
                        Log.e("Modifier", it)
                    }
                }
            }
            launch {
                p.inputStream
                    .bufferedReader()
                    .useLines { lines ->
                        lines.forEach {
                            Log.i("Modifier", it)
                            if (it == "OK") {
                                p.inputStream.close()
                                p.errorStream.close()
                                p.destroy()
                            }
                        }
                    }
            }
        }
        withContext(Dispatchers.IO) {
            p.waitFor()
        }

    } catch (e: Exception) {
        e.printStackTrace()
    }
}