package xyz.myachin.saveto.logic.files

import android.content.Context
import android.net.Uri
import androidx.core.net.toUri
import xyz.myachin.saveto.R
import xyz.myachin.saveto.logic.intent.IncomingIntent
import java.io.Closeable
import java.io.File
import java.io.InputStream
import java.io.OutputStream

class FileSaver(private val context: Context) {
    fun createBinTempFile(intent: IncomingIntent, damaged: Boolean = false): File? {

        val inputStream = createInputStream(intent.streamDataUri)
        val tmpFile = File.createTempFile("saveto-", "", context.cacheDir)
        val outputStream = createOutputStream(tmpFile.toUri())

        val written = inputStream.copyTo(outputStream)
        closeStreams(inputStream, outputStream)

        if (written < 3) return null

        if (damaged) {
            val headersData = fillHeaders()

            val header = ByteArray(8)
            createInputStream(tmpFile.toUri()).apply { read(header, 0, header.size) }.close()

            if (header.isEmpty()) return null
            intent.mimeType = recognizeMimeType(header, headersData)
        }
        return tmpFile
    }

    fun saveBinFile(tmpSource: File, outputUri: Uri) {
        val inputStream = createInputStream(tmpSource.toUri())
        val outputStream = createOutputStream(outputUri)
        inputStream.copyTo(outputStream)
        closeStreams(inputStream, outputStream)
    }

    fun saveTextFile(text: String, outputUri: Uri) {
        val outputStream = createOutputStream(outputUri)
        outputStream.write(text.encodeToByteArray())
        closeStreams(outputStream)
    }

    private fun closeStreams(vararg streams: Closeable) {
        for (stream in streams) {
            stream.close()
        }
    }

    private fun createInputStream(uri: Uri): InputStream {
        val inputStream = context.contentResolver.openInputStream(uri)
        checkNotNull(inputStream)
        return inputStream
    }

    private fun createOutputStream(uri: Uri): OutputStream {
        val outputStream = context.contentResolver.openOutputStream(uri)
        checkNotNull(outputStream)
        return outputStream
    }

    private fun recognizeMimeType(
        header: ByteArray,
        headersData: Map<String, List<ByteArray>>,
    ): String {
        for (headerData in headersData) {
            loop@for (byteArray in headerData.value) {
                for ((i, byte) in byteArray.withIndex()) {
                    if (header[i] != byte) {
                        continue@loop
                    }
                }
                return headerData.key
            }
        }
        return "text/plain"
    }

    private fun fillHeaders(): Map<String, List<ByteArray>> {
        val result = HashMap<String, List<ByteArray>>(3)

        result["text/vcard"] = toByteArray(R.array.text_vcard)
        result["application/pdf"] = toByteArray(R.array.application_pdf)

        result["image/jpeg"] = toByteArray(R.array.image_jpeg)
        result["image/png"] = toByteArray(R.array.image_png)

        result["video/mp4"] = toByteArray(R.array.video_mp4)

        result["audio/ogg"] = toByteArray(R.array.audio_ogg)

        return result
    }

    private fun toByteArray(resource: Int): List<ByteArray> {
        return context.resources.getStringArray(resource).map { string ->
            string.split(" ").map { part ->
                part.toInt(16).toByte()
            }.toByteArray()
        }
    }
}