package com.bomostory.sceneeditmodule

import android.graphics.Bitmap
import android.util.Log
import com.bomostory.sceneeditmodule.MovieEditActivity.Companion.FPS
import com.bomostory.sceneeditmodule.basicdata.Music
import com.bomostory.sceneeditmodule.basicdata.Project
import com.bomostory.sceneeditmodule.basicdata.Scene
import com.example.exportmedia.MediaHelper
import com.example.exportmedia.data.AudioSource
import com.example.exportmedia.vedio.MovieMaker
import io.reactivex.Completable
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
import java.io.File
import java.util.AbstractMap
import java.util.concurrent.Semaphore
import kotlin.collections.ArrayList
import kotlin.collections.LinkedHashMap
import kotlin.collections.set

class SuperMovieMaker {

    fun make(
            outputFile: File,
            project: Project,
            musics: List<Music>,
            scaleWidth: Int,
            scaleHeight: Int,
            mediaHelper: MediaHelper): Observable<String> {
        return Observable.create<String> { emitter ->
            val movieBuilder = MovieMaker.Builder(mediaHelper)
            movieBuilder.fps = FPS
            movieBuilder.audioSources = generateAudioSource(musics)

            val inputSceneStatus = LinkedHashMap<Int, Int>()
            val frameDataList = generateFrameDataList(project)
            var bitmapIndex = 0
            val permitCount = frameDataList.size
            val semaphore = Semaphore(permitCount)
            var completeCount = 0
            emitter.onNext("$completeCount/${frameDataList.size}")

            var beginTime = System.currentTimeMillis()
            frameDataList.forEach { frameData ->
                val i = bitmapIndex++
                inputSceneStatus[i] = frameData.repeat
                semaphore.acquire()
                Completable.create {
                    project?.story?.apply {
                        val scene = scenes[frameData.sceneIndex]
                        val bitmap = SceneDrawer.drawScene(scene, frameData.x, scaleWidth, scaleHeight)
                        movieBuilder.addImage(i, bitmap!!).blockingSubscribe()
                    }
                    it.onComplete()
                }
                        .subscribeOn(Schedulers.computation())
                        .subscribe {
                            completeCount++
                            emitter.onNext("$completeCount/${frameDataList.size}")
                            semaphore.release()
                        }
            }
            semaphore.acquire(permitCount)
            var endTime = System.currentTimeMillis()
            Log.d(this@SuperMovieMaker::class.java.simpleName, "draw + save duration = ${endTime - beginTime}")
            movieBuilder.inputFilePath = movieBuilder.addInputFile(inputSceneStatus).blockingFirst()
            emitter.onNext("Encoding video")
            movieBuilder.build().output(outputFile).blockingSubscribe()
            emitter.onComplete()
        }
    }

    private fun generateAudioSource(musics: List<Music>): ArrayList<AudioSource> {
        val audioSources = ArrayList<AudioSource>()
        for (music in musics) {
            val audioSource = AudioSource()
            audioSource.duration = music.duration
            audioSource.offsetSec = music.offsetTime.toFloat() / 1000
            audioSource.path = music.path
            audioSources.add(audioSource)
        }
        return audioSources
    }

    fun generateFrameDataList(project: Project): List<FrameData> {
        val frameDataList = ArrayList<FrameData>()
        var index = 0
        project.story?.scenes?.forEach { scene ->
            val sceneIndex = index++
            scene.record?.apply {
                var x = 0
                var trackPosition = -1

                for (t in 0..period step ((1f / FPS) * 1000).toLong()) {
                    for (track in tracks) {
                        if (t >= track.time && tracks.indexOf(track) > trackPosition) {
                            x = track.positionX
                            trackPosition = tracks.indexOf(track)
                        }
                    }

                    if (frameDataList.isEmpty()) {
                        frameDataList.add(FrameData(sceneIndex, x, 1))
                    } else {
                        val frameData = frameDataList.last()
                        if (frameData.x == x) {
                            frameData.repeat++
                        } else {
                            frameDataList.add(FrameData(sceneIndex, x, 1))
                        }
                    }
                }
            }
        }
        return frameDataList
    }

    private fun generateMovieFilms(
            scene: Scene,
            scaleWidth: Int,
            scaleHeight: Int): Observable<AbstractMap.SimpleEntry<Int, Bitmap>> {
        var preBitmap: Bitmap?
        return Observable.create<AbstractMap.SimpleEntry<Int, Bitmap>> {
            scene?.apply {
                record?.apply {
                    var bitmap = SceneDrawer.drawScene(scene, 0, scaleWidth, scaleHeight)
                    var bitmapIndex = 0
                    var trackPosition = -1

                    for (t in 0..period step ((1f / FPS) * 1000).toLong()) {
                        for (track in tracks) {
                            if (t >= track.time && tracks.indexOf(track) > trackPosition) {
                                preBitmap = bitmap
                                preBitmap?.recycle()

                                bitmap = SceneDrawer.drawScene(scene, track.positionX, scaleWidth, scaleHeight)
                                trackPosition = tracks.indexOf(track)
                            }
                        }

                        bitmap?.apply {
                            it.onNext(AbstractMap.SimpleEntry(bitmapIndex++, this))
                        }
                    }
                    it.onComplete()
                }
            }
        }
    }


    data class FrameData(val sceneIndex: Int, val x: Int, var repeat: Int)
}