package code.name.monkey.retromusic.activities.tageditor

import android.app.Activity
import android.content.Context
import android.graphics.Bitmap
import android.media.MediaScannerConnection
import android.os.Build
import android.util.Log
import android.widget.Toast
import androidx.annotation.RequiresApi
import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener
import code.name.monkey.retromusic.model.AudioTagInfo
import code.name.monkey.retromusic.util.MusicUtil.createAlbumArtFile
import code.name.monkey.retromusic.util.MusicUtil.deleteAlbumArt
import code.name.monkey.retromusic.util.MusicUtil.insertAlbumArt
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jaudiotagger.audio.AudioFileIO
import org.jaudiotagger.audio.exceptions.CannotReadException
import org.jaudiotagger.audio.exceptions.CannotWriteException
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException
import org.jaudiotagger.tag.TagException
import org.jaudiotagger.tag.images.AndroidArtwork
import org.jaudiotagger.tag.images.Artwork
import java.io.File
import java.io.FileOutputStream
import java.io.IOException

class TagWriter {

    companion object {

        suspend fun scan(context: Context, toBeScanned: List<String?>?) {
            if (toBeScanned == null || toBeScanned.isEmpty()) {
                Log.i("scan", "scan: Empty")
                Toast.makeText(context, "Scan file from folder", Toast.LENGTH_SHORT).show()
                return
            }
            MediaScannerConnection.scanFile(
                context,
                toBeScanned.toTypedArray(),
                null,
                withContext(Dispatchers.Main) {
                    if (context is Activity) UpdateToastMediaScannerCompletionListener(
                        context, toBeScanned
                    ) else null
                }
            )
        }

        suspend fun writeTagsToFiles(context: Context, info: AudioTagInfo) {
            withContext(Dispatchers.IO) {
                kotlin.runCatching {
                    var artwork: Artwork? = null
                    var albumArtFile: File? = null
                    if (info.artworkInfo?.artwork != null) {
                        try {
                            albumArtFile = createAlbumArtFile(context).canonicalFile
                            info.artworkInfo.artwork.compress(
                                Bitmap.CompressFormat.JPEG,
                                100,
                                FileOutputStream(albumArtFile)
                            )
                            artwork = AndroidArtwork.createArtworkFromFile(albumArtFile)
                        } catch (e: IOException) {
                            e.printStackTrace()
                        }
                    }
                    var wroteArtwork = false
                    var deletedArtwork = false
                    for (filePath in info.filePaths!!) {
                        try {
                            val audioFile = AudioFileIO.read(File(filePath))
                            val tag = audioFile.tagOrCreateAndSetDefault
                            if (info.fieldKeyValueMap != null) {
                                for ((key, value) in info.fieldKeyValueMap) {
                                    try {
                                        tag.setField(key, value)
                                    } catch (e: Exception) {
                                        e.printStackTrace()
                                    }
                                }
                            }
                            if (info.artworkInfo != null) {
                                if (info.artworkInfo.artwork == null) {
                                    tag.deleteArtworkField()
                                    deletedArtwork = true
                                } else if (artwork != null) {
                                    tag.deleteArtworkField()
                                    tag.setField(artwork)
                                    wroteArtwork = true
                                }
                            }
                            audioFile.commit()
                        } catch (e: CannotReadException) {
                            e.printStackTrace()
                        } catch (e: IOException) {
                            e.printStackTrace()
                        } catch (e: CannotWriteException) {
                            e.printStackTrace()
                        } catch (e: TagException) {
                            e.printStackTrace()
                        } catch (e: ReadOnlyFileException) {
                            e.printStackTrace()
                        } catch (e: InvalidAudioFrameException) {
                            e.printStackTrace()
                        }
                    }
                    if (wroteArtwork) {
                        insertAlbumArt(context, info.artworkInfo!!.albumId, albumArtFile!!.path)
                    } else if (deletedArtwork) {
                        deleteAlbumArt(context, info.artworkInfo!!.albumId)
                    }
                    scan(context, info.filePaths)
                }.onFailure {
                    it.printStackTrace()
                }
            }
        }


        @RequiresApi(Build.VERSION_CODES.R)
        suspend fun writeTagsToFilesR(context: Context, info: AudioTagInfo): List<File> =
            withContext(Dispatchers.IO) {
                val cacheFiles = mutableListOf<File>()
                kotlin.runCatching {
                    var artwork: Artwork? = null
                    var albumArtFile: File? = null
                    if (info.artworkInfo?.artwork != null) {
                        try {
                            albumArtFile = createAlbumArtFile(context).canonicalFile
                            info.artworkInfo.artwork.compress(
                                Bitmap.CompressFormat.JPEG,
                                100,
                                FileOutputStream(albumArtFile)
                            )
                            artwork = AndroidArtwork.createArtworkFromFile(albumArtFile)
                        } catch (e: IOException) {
                            e.printStackTrace()
                        }
                    }
                    var wroteArtwork = false
                    var deletedArtwork = false
                    for (filePath in info.filePaths!!) {
                        try {
                            val originFile = File(filePath)
                            val cacheFile = File(context.cacheDir, originFile.name)
                            cacheFiles.add(cacheFile)
                            originFile.inputStream().use { input ->
                                cacheFile.outputStream().use { output ->
                                    input.copyTo(output)
                                }
                            }
                            val audioFile = AudioFileIO.read(cacheFile)
                            val tag = audioFile.tagOrCreateAndSetDefault
                            if (info.fieldKeyValueMap != null) {
                                for ((key, value) in info.fieldKeyValueMap) {
                                    try {
                                        tag.setField(key, value)
                                    } catch (e: Exception) {
                                        e.printStackTrace()
                                    }
                                }
                            }
                            if (info.artworkInfo != null) {
                                if (info.artworkInfo.artwork == null) {
                                    tag.deleteArtworkField()
                                    deletedArtwork = true
                                } else if (artwork != null) {
                                    tag.deleteArtworkField()
                                    tag.setField(artwork)
                                    wroteArtwork = true
                                }
                            }
                            audioFile.commit()
                        } catch (e: CannotReadException) {
                            e.printStackTrace()
                        } catch (e: IOException) {
                            e.printStackTrace()
                        } catch (e: CannotWriteException) {
                            e.printStackTrace()
                        } catch (e: TagException) {
                            e.printStackTrace()
                        } catch (e: ReadOnlyFileException) {
                            e.printStackTrace()
                        } catch (e: InvalidAudioFrameException) {
                            e.printStackTrace()
                        }
                    }
                    if (wroteArtwork) {
                        insertAlbumArt(context, info.artworkInfo!!.albumId, albumArtFile!!.path)
                    } else if (deletedArtwork) {
                        deleteAlbumArt(context, info.artworkInfo!!.albumId)
                    }
                }.onFailure {
                    it.printStackTrace()
                }
                cacheFiles
            }
    }
}