/*
 * Copyright (c) 2019 toastkidjp.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompany this distribution.
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html.
 */
package jp.toastkid.image.list

import android.Manifest
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.LayoutRes
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import jp.toastkid.image.R
import jp.toastkid.image.databinding.FragmentImageViewerBinding
import jp.toastkid.image.setting.ExcludingSettingFragment
import jp.toastkid.lib.ContentScrollable
import jp.toastkid.lib.ContentViewModel
import jp.toastkid.lib.fragment.CommonFragmentAction
import jp.toastkid.lib.preference.PreferenceApplier
import jp.toastkid.lib.view.RecyclerViewScroller
import jp.toastkid.lib.viewmodel.PageSearcherViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch

/**
 * @author toastkidjp
 */
class ImageViewerFragment : Fragment(), CommonFragmentAction, ContentScrollable {

    private lateinit var binding: FragmentImageViewerBinding

    private lateinit var bucketLoader: BucketLoader

    private lateinit var imageLoader: ImageLoader

    private lateinit var preferenceApplier: PreferenceApplier

    private lateinit var imageLoaderUseCase: ImageLoaderUseCase

    private lateinit var imageFilterUseCase: ImageFilterUseCase

    private var adapter: Adapter? = null

    private val requestPermissionLauncher =
        registerForActivityResult(ActivityResultContracts.RequestPermission()) {
            if (it) {
                imageLoaderUseCase()
                return@registerForActivityResult
            }

            activity?.let {
                ViewModelProvider(it).get(ContentViewModel::class.java)
                    .snackShort(R.string.message_audio_file_is_not_found)
            }
            activity?.supportFragmentManager?.popBackStack()
        }

    private val disposables: Job by lazy { Job() }

    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View {
        binding = DataBindingUtil.inflate(inflater, LAYOUT_ID, container, false)
        binding.fragment = this
        setHasOptionsMenu(true)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val context = view.context

        val contentResolver = context.contentResolver ?: return
        bucketLoader = BucketLoader(contentResolver)
        imageLoader = ImageLoader(contentResolver)

        preferenceApplier = PreferenceApplier(context)

        val viewModelProvider = ViewModelProvider(this)
        val viewModel =
                viewModelProvider.get(ImageViewerFragmentViewModel::class.java)

        adapter = Adapter(parentFragmentManager, viewModel)

        val viewLifecycleOwner = viewLifecycleOwner
        viewModel.onClick.observe(viewLifecycleOwner, {
            CoroutineScope(Dispatchers.IO).launch(disposables) { imageLoaderUseCase(it) }
        })

        viewModel.onLongClick.observe(viewLifecycleOwner, {
            preferenceApplier.addExcludeItem(it)
            imageLoaderUseCase()
        })

        viewModel.refresh.observe(viewLifecycleOwner, {
            attemptLoad()
        })

        observePageSearcherViewModel()

        binding.images.adapter = adapter
        binding.images.layoutManager =
                GridLayoutManager(context, 2, RecyclerView.VERTICAL, false)

        imageLoaderUseCase = ImageLoaderUseCase(
                preferenceApplier,
                adapter,
                BucketLoader(contentResolver),
                imageLoader,
                this::refreshContent
        )

        imageFilterUseCase = ImageFilterUseCase(
                preferenceApplier,
                adapter,
                imageLoaderUseCase,
                imageLoader,
                this::refreshContent
        )

        parentFragmentManager.setFragmentResultListener(
            "excluding",
            viewLifecycleOwner,
            { _, _ -> refreshContent() }
        )
    }

    private fun observePageSearcherViewModel() {
        val activity = activity ?: return
        ViewModelProvider(activity).get(PageSearcherViewModel::class.java)
                .also { viewModel ->
                    viewModel.find.observe(viewLifecycleOwner, Observer {
                        imageFilterUseCase(it)
                    })
                }
    }

    override fun pressBack(): Boolean {
        if (adapter?.isBucketMode() == true) {
            activity?.supportFragmentManager?.popBackStack()
        } else {
            imageLoaderUseCase.clearCurrentBucket()
            imageLoaderUseCase()
        }
        return true
    }

    override fun onStart() {
        super.onStart()

        attemptLoad()
    }

    private fun attemptLoad() {
        requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
    }

    private fun refreshContent() {
        activity?.runOnUiThread {
            adapter?.notifyDataSetChanged()
            RecyclerViewScroller.toTop(binding.images, adapter?.itemCount ?: 0)
        }
    }

    override fun toTop() {
        RecyclerViewScroller.toTop(binding.images, adapter?.itemCount ?: 0)
    }

    override fun toBottom() {
        RecyclerViewScroller.toBottom(binding.images, adapter?.itemCount ?: 0)
    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        super.onCreateOptionsMenu(menu, inflater)
        inflater.inflate(R.menu.image_viewer, menu)
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.excluding_items_setting -> {
                val fragment = ExcludingSettingFragment()
                fragment.show(parentFragmentManager, "setting")
            }
            R.id.sort_by_date -> {
                preferenceApplier.setImageViewerSort(Sort.DATE.name)
                imageLoaderUseCase()
            }
            R.id.sort_by_name -> {
                preferenceApplier.setImageViewerSort(Sort.NAME.name)
                imageLoaderUseCase()
            }
            R.id.sort_by_count -> {
                preferenceApplier.setImageViewerSort(Sort.ITEM_COUNT.name)
                imageLoaderUseCase()
            }
        }
        return super.onOptionsItemSelected(item)
    }

    override fun onDetach() {
        disposables.cancel()
        parentFragmentManager.clearFragmentResultListener("excluding")
        requestPermissionLauncher.unregister()
        super.onDetach()
    }

    companion object {

        @LayoutRes
        private val LAYOUT_ID = R.layout.fragment_image_viewer
    }
}