/*
 * Nextcloud Talk application
 *
 * @author Mario Danic
 * @author Andy Scherzinger
 * Copyright (C) 2022 Andy Scherzinger <info@andy-scherzinger.de>
 * Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
 *
 * 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 <http://www.gnu.org/licenses/>.
 */
package com.nextcloud.talk.controllers

import android.annotation.SuppressLint
import android.media.MediaPlayer
import android.media.RingtoneManager
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.text.TextUtils
import android.util.Log
import android.view.MenuItem
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import autodagger.AutoInjector
import com.bluelinelabs.logansquare.LoganSquare
import com.nextcloud.talk.R
import com.nextcloud.talk.adapters.items.NotificationSoundItem
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
import com.nextcloud.talk.controllers.base.BaseController
import com.nextcloud.talk.controllers.util.viewBinding
import com.nextcloud.talk.databinding.ControllerGenericRvBinding
import com.nextcloud.talk.models.RingtoneSettings
import com.nextcloud.talk.utils.NotificationUtils
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ARE_CALL_SOUNDS
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.SelectableAdapter
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import java.io.IOException

@AutoInjector(NextcloudTalkApplication::class)
class RingtoneSelectionController(args: Bundle) :
    BaseController(
        R.layout.controller_generic_rv,
        args
    ),
    FlexibleAdapter.OnItemClickListener {
    private val binding: ControllerGenericRvBinding? by viewBinding(ControllerGenericRvBinding::bind)

    private var adapter: FlexibleAdapter<*>? = null
    private var adapterDataObserver: RecyclerView.AdapterDataObserver? = null
    private val abstractFlexibleItemList: MutableList<AbstractFlexibleItem<*>> = ArrayList()
    private val callNotificationSounds: Boolean
    private var mediaPlayer: MediaPlayer? = null
    private var cancelMediaPlayerHandler: Handler? = null

    override fun onViewBound(view: View) {
        super.onViewBound(view)
        if (adapter == null) {
            adapter = FlexibleAdapter(abstractFlexibleItemList, activity, false)
            adapter!!.setNotifyChangeOfUnfilteredItems(true).mode = SelectableAdapter.Mode.SINGLE
            adapter!!.addListener(this)
            cancelMediaPlayerHandler = Handler()
        }
        adapter!!.addListener(this)
        prepareViews()
        fetchNotificationSounds()
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return if (item.itemId == android.R.id.home) {
            router.popCurrentController()
        } else {
            super.onOptionsItemSelected(item)
        }
    }

    private fun prepareViews() {
        val layoutManager: RecyclerView.LayoutManager = SmoothScrollLinearLayoutManager(activity)
        binding?.recyclerView?.layoutManager = layoutManager
        binding?.recyclerView?.setHasFixedSize(true)
        binding?.recyclerView?.adapter = adapter
        adapterDataObserver = object : RecyclerView.AdapterDataObserver() {
            override fun onChanged() {
                super.onChanged()
                findSelectedSound()
            }
        }
        adapter!!.registerAdapterDataObserver(adapterDataObserver!!)
        binding?.swipeRefreshLayout?.isEnabled = false
    }

    @SuppressLint("LongLogTag")
    private fun findSelectedSound() {
        var foundDefault = false
        var preferencesString: String? = null
        val callsEnabledButNoRingtone = callNotificationSounds &&
            TextUtils.isEmpty(appPreferences.callRingtoneUri.also { preferencesString = it })
        val noCallsAndNoMessageTone = !callNotificationSounds &&
            TextUtils.isEmpty(appPreferences.messageRingtoneUri.also { preferencesString = it })
        if (callsEnabledButNoRingtone || noCallsAndNoMessageTone) {
            adapter!!.toggleSelection(1)
            foundDefault = true
        }
        if (!TextUtils.isEmpty(preferencesString) && !foundDefault) {
            try {
                val ringtoneSettings: RingtoneSettings =
                    LoganSquare.parse<RingtoneSettings>(preferencesString, RingtoneSettings::class.java)
                if (ringtoneSettings.ringtoneUri == null) {
                    adapter!!.toggleSelection(0)
                } else if (ringtoneSettings.ringtoneUri!!.toString() == ringtoneString) {
                    adapter!!.toggleSelection(1)
                } else {
                    var notificationSoundItem: NotificationSoundItem?
                    for (i in 2 until adapter!!.itemCount) {
                        notificationSoundItem = adapter!!.getItem(i) as NotificationSoundItem?
                        if (
                            notificationSoundItem!!.notificationSoundUri == ringtoneSettings.ringtoneUri!!.toString()
                        ) {
                            adapter!!.toggleSelection(i)
                            break
                        }
                    }
                }
            } catch (e: IOException) {
                Log.e(TAG, "Failed to parse ringtone settings")
            }
        }
        adapter!!.unregisterAdapterDataObserver(adapterDataObserver!!)
        adapterDataObserver = null
    }

    private val ringtoneString: String
        get() = if (callNotificationSounds) {
            NotificationUtils.DEFAULT_CALL_RINGTONE_URI
        } else {
            NotificationUtils.DEFAULT_MESSAGE_RINGTONE_URI
        }

    private fun fetchNotificationSounds() {
        abstractFlexibleItemList.add(
            NotificationSoundItem(
                resources!!.getString(R.string.nc_settings_no_ringtone),
                null
            )
        )
        abstractFlexibleItemList.add(
            NotificationSoundItem(
                resources!!.getString(R.string.nc_settings_default_ringtone),
                ringtoneString
            )
        )
        if (activity != null) {
            val manager = RingtoneManager(activity)
            if (callNotificationSounds) {
                manager.setType(RingtoneManager.TYPE_RINGTONE)
            } else {
                manager.setType(RingtoneManager.TYPE_NOTIFICATION)
            }
            val cursor = manager.cursor
            var notificationSoundItem: NotificationSoundItem
            while (cursor.moveToNext()) {
                val notificationTitle = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX)
                val notificationUri = cursor.getString(RingtoneManager.URI_COLUMN_INDEX)
                val completeNotificationUri = notificationUri + "/" + cursor.getString(RingtoneManager.ID_COLUMN_INDEX)
                notificationSoundItem = NotificationSoundItem(notificationTitle, completeNotificationUri)
                abstractFlexibleItemList.add(notificationSoundItem)
            }
        }
        adapter!!.updateDataSet(abstractFlexibleItemList as List<Nothing>?, false)
    }

    override fun onItemClick(view: View, position: Int): Boolean {
        val notificationSoundItem = adapter!!.getItem(position) as NotificationSoundItem?
        var ringtoneUri: Uri? = null
        if (!TextUtils.isEmpty(notificationSoundItem!!.notificationSoundUri)) {
            ringtoneUri = Uri.parse(notificationSoundItem.notificationSoundUri)
            endMediaPlayer()
            mediaPlayer = MediaPlayer.create(activity, ringtoneUri)
            cancelMediaPlayerHandler = Handler()
            cancelMediaPlayerHandler!!.postDelayed(
                { endMediaPlayer() },
                (mediaPlayer!!.duration + DURATION_EXTENSION).toLong()
            )
            mediaPlayer!!.start()
        }
        if (adapter!!.selectedPositions.size == 0 || adapter!!.selectedPositions[0] != position) {
            val ringtoneSettings = RingtoneSettings()
            ringtoneSettings.ringtoneName = notificationSoundItem.notificationSoundName
            ringtoneSettings.ringtoneUri = ringtoneUri
            if (callNotificationSounds) {
                try {
                    appPreferences!!.callRingtoneUri = LoganSquare.serialize(ringtoneSettings)
                    adapter!!.toggleSelection(position)
                    adapter!!.notifyDataSetChanged()
                } catch (e: IOException) {
                    Log.e(TAG, "Failed to store selected ringtone for calls")
                }
            } else {
                try {
                    appPreferences!!.messageRingtoneUri = LoganSquare.serialize(ringtoneSettings)
                    adapter!!.toggleSelection(position)
                    adapter!!.notifyDataSetChanged()
                } catch (e: IOException) {
                    Log.e(TAG, "Failed to store selected ringtone for calls")
                }
            }
        }
        return true
    }

    private fun endMediaPlayer() {
        if (cancelMediaPlayerHandler != null) {
            cancelMediaPlayerHandler!!.removeCallbacksAndMessages(null)
        }
        if (mediaPlayer != null) {
            if (mediaPlayer!!.isPlaying) {
                mediaPlayer!!.stop()
            }
            mediaPlayer!!.release()
            mediaPlayer = null
        }
    }

    public override fun onDestroy() {
        endMediaPlayer()
        super.onDestroy()
    }

    companion object {
        private const val TAG = "RingtoneSelection"
        private const val DURATION_EXTENSION = 25
    }

    init {
        setHasOptionsMenu(true)
        sharedApplication!!.componentApplication.inject(this)
        callNotificationSounds = args.getBoolean(KEY_ARE_CALL_SOUNDS, false)
    }

    override val title: String
        get() =
            resources!!.getString(R.string.nc_settings_notification_sounds)
}
