package oppen.filters.ui

/*

    Lätt - fast image filtering app for Android
    Copyright (C) 2020  Öppenlab

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

 */

import android.content.Context
import android.content.res.Resources
import android.graphics.Bitmap
import android.net.Uri
import android.util.Log
import com.bumptech.glide.Glide
import oppen.filters.io.ImageIO
import oppen.filters.io.Levels
import oppen.filters.io.PhotoProcessor

class FilterRepository(
    private val context: Context,
    private val imageIO: ImageIO) {

    private val photoProcessor = PhotoProcessor(context)
    private var originalSourceUri: Uri? = null
    private var previewSourceUri: Uri? = null
    private var thumbnailSourceUri: Uri? = null

    private var previewBitmap: Bitmap? = null

    private var lut: Pair<String, Int>? = null

    /**
     * Main repository entry-point, called every time user selects a photo via an intent - reset any/all state
     * @param uri - Storage Access Framework Uri
     * @param screenWidth - guide width downscaling image for UI work
     * @param onPreview - callback with previewUri for main ui image, and smaller thumbnailUri for filter adapter
     */
    fun imageSelected(uri: Uri, screenWidth: Int, onPreview: (previewUri: Uri, thumbnailUri: Uri) -> Unit){
        previewBitmap = null
        Levels.reset()

        originalSourceUri = uri

        if(originalSourceUri == null){
            //todo - some error callback for ui feedback - or any error correction at all...
            Log.e("FilterRepository", "Null sourceUri")
            return
        }

        Glide.get(context).clearMemory()//This needs to be done here before we're in threaded world

        //First build the main preview image with screenWidth
        imageIO.buildAndStoreLivePreview(originalSourceUri!!, screenWidth){ previewUri ->

            Glide.get(context).clearDiskCache()//This needs to be done here - within a thread

            previewSourceUri = previewUri

            //Then generate the thumbnail source - a smaller image for the filter previews
            imageIO.buildAndStoreLiveThumbnail(previewSourceUri!!, 150){ thumbnailUri ->
                thumbnailSourceUri = thumbnailUri

                //Tell the ui to display the live preview, and pass the thumbnail Uri for the adapter
                onPreview.invoke(previewSourceUri!!, thumbnailSourceUri!!)
            }
        }
    }

    fun filterSelected(lut: Pair<String, Int>, onPreview: (bitmap: Bitmap?) -> Unit) {
        this.lut = lut
        if(previewBitmap == null){
            imageIO.get(previewSourceUri!!){ bitmap ->
                previewBitmap = bitmap?.copy(bitmap.config, false)
                imageIO.recycleLast()
                if(previewBitmap != null){
                    photoProcessor.filter(previewBitmap!!, lut.second, PhotoProcessor.ALL_FILTERS){ output ->
                        onPreview.invoke(output)
                    }
                }
            }
        }else{
            photoProcessor.filter(previewBitmap!!, lut.second, PhotoProcessor.ALL_FILTERS){ output ->
                onPreview.invoke(output)
            }
        }
    }

    fun getSuggestedFilename(): String? = FilenameHelper.getSuggestedFilename(context, originalSourceUri, lut?.first ?: "Unknown Filter")

    /**
     * This carries out the main full resolution image rendering and save to disk so will be the
     * longest running and most memory intensive operation in the app.
     *
     * @param exportUri - Uri set via Storage Access Framework new file intent
     * @param onSaved - callback with Uri (should be same as exportUri argument - needed?)
     *
     */
    fun export(exportUri: Uri, onSaved: (uri: Uri) -> Unit) {
        imageIO.get(originalSourceUri!!){ fullResSourceBitmap ->
            if(fullResSourceBitmap != null){
                photoProcessor.filter(fullResSourceBitmap, lut?.second ?: 0, PhotoProcessor.ALL_FILTERS){ output ->
                    imageIO.recycleLast()
                    imageIO.publicExport(output, exportUri){ savedUri ->
                        onSaved(savedUri)
                        cacheExportPreview(savedUri)
                    }
                }
            }
        }
    }

    fun rebuild(onPreview: (bitmap: Bitmap) -> Unit){
        if(previewBitmap == null){
            //Get it from store
            imageIO.get(previewSourceUri!!){ bitmap ->
                previewBitmap = bitmap?.copy(bitmap.config, false)
                imageIO.recycleLast()
                if(previewBitmap != null){
                    photoProcessor.filter(previewBitmap!!, lut?.second, PhotoProcessor.ALL_FILTERS){ output ->
                        onPreview.invoke(output!!)
                    }
                }
            }
        }else{
            photoProcessor.filter(previewBitmap!!, lut?.second, PhotoProcessor.ALL_FILTERS){ output ->
                onPreview.invoke(output!!)
            }
        }
    }

    private fun cacheExportPreview(uri: Uri){
        imageIO.getAndResize(uri, (Resources.getSystem().displayMetrics.widthPixels/1.5).toInt()){
            imageIO.put(it, "cache_image.png"){cacheUri ->
                imageIO.recycleLast()
            }
        }

    }
}