# Fragment.kt

    /*
     * The MIT License
     *
     * Original work sponsored and donated by The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     * Copyright (C) 2021 The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy of
     * this software and associated documentation files (the "Software"), to deal in
     * the Software without restriction, including without limitation the rights to
     * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
     * of the Software, and to permit persons to whom the Software is furnished to do
     * so, subject to the following conditions:
     *
     *
     * The above copyright notice and this permission notice shall be included in all
     * copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     * SOFTWARE.
     */

    package dk.nsp.mingraviditet.util.extensions

    import android.content.Context
    import androidx.annotation.IdRes
    import androidx.annotation.MainThread
    import androidx.fragment.app.Fragment
    import androidx.fragment.app.viewModels
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.ViewModelProvider
    import androidx.lifecycle.ViewModelStoreOwner
    import androidx.navigation.NavController
    import androidx.navigation.fragment.findNavController
    import dk.nsp.shared.tryAndLog

    /**
     * Returns a ViewModel generated via [viewModels], but wrapped in try/catch. Returns null if an error was thrown or
     * if the [factoryProducer] returns null instead of a valid [ViewModelProvider.Factory].
     * This is mainly due to [viewModels] way of throwing non-annotated errors
     * @receiver Fragment
     * @param ownerProducer Function0<ViewModelStoreOwner> The owner scope for the ViewModel
     * @param factoryProducer Function0<Factory?>? Optional producer, if supplied and if it returns a null factory, the returned ViewModel will be null
     * @return Lazy<VM?> The ViewModel created or null if an error is thrown or the supplied factory provider returns null
     */
    @MainThread
    inline fun <reified VM : ViewModel?> Fragment.safeViewModels(
        noinline ownerProducer: () -> ViewModelStoreOwner = { this },
        noinline factoryProducer: (() -> ViewModelProvider.Factory?)? = null
    ): Lazy<VM?> {
        return lazy {
            tryAndLog<VM?> {
                if (factoryProducer != null) {
                    // Check if the given factoryProducer returns a valid factory
                    val factory = factoryProducer.invoke()
                        ?: throw IllegalArgumentException("Supplied producer for ViewModelProvider.Factory returned an invalid producer")
                    return@tryAndLog ViewModelProvider(
                        ownerProducer.invoke(),
                        factory
                    ).get(VM::class.java)
                } else {
                    return@tryAndLog ViewModelProvider(ownerProducer.invoke()).get(VM::class.java)
                }
            }
        }
    }

    @MainThread
    inline fun <reified VM : ViewModel?> Fragment.safeViewModelsContext(
        noinline ownerProducer: () -> ViewModelStoreOwner = { this },
        noinline factoryProducer: ((Context) -> ViewModelProvider.Factory?)? = null
    ): Lazy<VM?> {
        return lazy {
            tryAndLog<VM?> {
                if (factoryProducer != null) {
                    val context = context ?: return@lazy null
                    // Check if the given factoryProducer returns a valid factory
                    val factory = factoryProducer.invoke(context)
                        ?: throw IllegalArgumentException("Supplied producer for ViewModelProvider.Factory returned an invalid producer")
                    return@tryAndLog ViewModelProvider(
                        ownerProducer.invoke(),
                        factory
                    ).get(VM::class.java)
                } else {
                    return@tryAndLog ViewModelProvider(ownerProducer.invoke()).get(VM::class.java)
                }
            }
        }
    }

    /**
     * Convenience for popping this fragment from the back stack of the [Fragment]'s [NavController]
     * @receiver Fragment
     * @return true if the stack was popped and the user has been navigated to another destination, false otherwise
     */
    @MainThread
    fun Fragment.popNavBackStack() = findNavController().popBackStack()

    /**
     * Convenience for popping this fragment from the back stack of the [Fragment]'s [NavController]
     * @receiver Fragment
     *
     * @param destinationId The topmost destination to retain
     * @param inclusive Whether the given destination should also be popped (default false).
     *
     * @return true if the stack was popped at least once and the user has been navigated to
     * another destination, false otherwise
     */
    @MainThread
    fun Fragment.popNavBackStack(@IdRes destinationId: Int, inclusive: Boolean = false) =
        findNavController().popBackStack(destinationId, inclusive)