package org.wntr.mdeditor

import android.app.Activity
import android.app.AlertDialog
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.net.Uri.parse
import android.os.Build
import android.os.Bundle
import android.os.ParcelFileDescriptor
import android.provider.DocumentsContract
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.WindowInsets
import android.view.WindowManager
import android.webkit.ConsoleMessage
import android.webkit.JavascriptInterface
import android.webkit.WebChromeClient
import android.webkit.WebView
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.ResponseBody
import okhttp3.ResponseBody.Companion.toResponseBody
import org.json.JSONObject
import retrofit2.Response
import java.io.BufferedReader
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStreamReader
import java.lang.Thread.sleep
import java.net.URLDecoder
import java.time.Instant
import java.util.Timer
import kotlin.concurrent.fixedRateTimer


class MainActivity : AppCompatActivity() {
    private val easyMDEscript = """
        if (typeof easyMDE != 'undefined') throw new Error("easyMDE already loaded"); 
        easyMDE = new EasyMDE({
            spellChecker: false,
            nativeSpellcheck: false,
            maxHeight: String(windowHeight-120)+"px",
            inputStyle: "textarea",
            autoDownloadFontAwesome: false,
            theme: "solarized",
            status: [
                {
                    className: "editor-statusbar-left",
                    onUpdate: (el) => {
                        el.innerHTML = saveStatus()
                    }    
                },
                {
                    className: "displayName",
                    defaultValue: "None",
                    onUpdate: (el) => {
                        el.innerHTML = displayName()
                    },
                }, "lines", "words", "cursor",
                {
                    className: "editor-statusbar-right",
                    onUpdate: (el) => {
                        el.innerHTML = "<i class=\"fa fa-square\"></i>"
                    }
                }
            ],
            toolbar: [
                {
                    name: "toggleTheme",
                    action: toggleTheme,
                    className: "fa fa-moon",
                    title: "Toggle Theme"
                },
                {
                    name: "share",
                    action: shareText,
                    className: "fa fa-share-nodes",
                    title: "Share"
                },"strikethrough", "horizontal-rule","undo",
                {
                    name: "preview",
                    action: myPreview,
                    className: "fa fa-eye",
                    title: "Preview",
                    noDisable: true
                },"redo",
                "bold", "italic","link","code",
                {
                    name: "toggle",
                    action: toggleBar,
                    className: "fa fa-expand",
                    title: "Toggle Bar",
                }
            ]
        });
"""
    
    companion object {
        const val CREATE_FILE = 1
        const val OPEN_FILE = 2
        var deleteVisible = false
        var mdeValue: String = ""
        var mdToAppend: String = ""
        var truncate = false
        lateinit var pickMultipleVisualMedia: ActivityResultLauncher<PickVisualMediaRequest>
        lateinit var ghostSettings: ActivityResultLauncher<Intent>
        lateinit var ghostMetaData: ActivityResultLauncher<Intent>
        lateinit var webView: WebView
        lateinit var api: ghostAPI
        lateinit var autosaveTimer: Timer
        var ghostConnection = false
        lateinit var credManager: CredentialManager
        var intentScheme = "none"
        var easyMDELoaded = false
        var readOnResume = true
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        supportActionBar!!.setDisplayShowTitleEnabled(false)

        deleteCache(applicationContext)

        Log.i(javaClass.simpleName, "intent data on start: ${intent.data.toString()}\nIntent action: ${intent.action}")

        credManager = CredentialManager(applicationContext)
        webView = findViewById<WebView>(R.id.mde_webview)
        webView.settings.javaScriptEnabled = true
        webView.setLongClickable(true);

        webView.webChromeClient = object : WebChromeClient() {
            override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
                Log.d("WebView", consoleMessage.message())
                return true
            }
            override fun onProgressChanged(view: WebView?, newProgress: Int) {
                super.onProgressChanged(view, newProgress)
                Log.d(javaClass.simpleName, "new progress: ${newProgress}")
                if (newProgress == 100) {
                    webView.evaluateJavascript(easyMDEscript, {
                        easyMDELoaded = true
                        Log.d(javaClass.simpleName, "easyMDE loaded")
                    })
                }
            }
        }
        webView.loadUrl("file:///android_res/raw/index.html")

        val jsi = object {
            /*@JavascriptInterface
            fun getHeight(): Int {
                val displayMetrics = DisplayMetrics()
                windowManager.defaultDisplay.getMetrics(displayMetrics)
                val height = displayMetrics.heightPixels
                Log.i(javaClass.simpleName, "display is $height pixels high.")
                return height
            }*/

            @JavascriptInterface
            fun triggerDisplayName(): String {
                return getDisplayName(applicationContext, thisFileUri)
            }

            @JavascriptInterface
            fun triggerShare(sharedText: String) {
                val htmlFile = createHtmlFile(applicationContext, sharedText)
                if (htmlFile == null) return
                val htmlUri = FileProvider.getUriForFile(applicationContext, "org.wntr.mdeditor.fileprovider", htmlFile)

                with(Intent(Intent.ACTION_SEND)) {
                    htmlFile?.also {
                        type = "text/plain"
                        putExtra(Intent.EXTRA_STREAM, htmlUri)
                        startActivity(Intent.createChooser(this, "Share html"))
                    }
                }
            }

            @JavascriptInterface
            fun getMdToAppend(): String {
                val md = mdToAppend
                mdToAppend = ""
                return md
            }

            @JavascriptInterface
            fun isFullscreen() : Boolean{
                return supportActionBar!!.isShowing
            }

            @JavascriptInterface
            fun toggleBar() {
                this@MainActivity.runOnUiThread({
                    if (supportActionBar!!.isShowing) {
                        @Suppress("DEPRECATION")
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                            window.insetsController?.hide(WindowInsets.Type.statusBars())
                        } else {
                            window.setFlags(
                                WindowManager.LayoutParams.FLAG_FULLSCREEN,
                                0
                            )
                        }
                        supportActionBar!!.hide()
                    }
                    else {
                        @Suppress("DEPRECATION")
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                            window.insetsController?.show(WindowInsets.Type.statusBars())
                        } else {
                            window.setFlags(
                                WindowManager.LayoutParams.FLAG_FULLSCREEN,
                                WindowManager.LayoutParams.FLAG_FULLSCREEN
                            )
                        }
                        supportActionBar!!.show()
                    }
                })
            }
        }
        webView.addJavascriptInterface(jsi, "Android")

        pickMultipleVisualMedia = registerForActivityResult(
            ActivityResultContracts.PickMultipleVisualMedia(
                5
            )
        ) { uris ->
            // Process URIs
            Toast.makeText(
                this@MainActivity,
                "Uploading\n$uris",
                Toast.LENGTH_LONG
            ).show()

            // TODO: This check for connection needs to be refined once multiple accounts are supported
            checkGhostConnection()

            for (uri in uris) pushImage(uri)

            Log.d(javaClass.simpleName, "Photo Picker URIs count ${uris.size}")
        }
        ghostSettings = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            checkGhostConnection()
        }
        ghostMetaData = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            Log.d(javaClass.simpleName,"Saving new Metadata to file")
            saveFile()
        }
    }

    override fun onResume() {
        super.onResume()


        if (!readOnResume) {
            readOnResume = true
            autosaveTimer = fixedRateTimer("timer",true,0,5000){
                this@MainActivity.runOnUiThread {
                    val script = "easyMDE.codemirror.doc.isClean();"
                    webView.evaluateJavascript(script , {
                        if (it == "false" && thisFileUri != null) {
                            saveFile()
                        }
                    })
                }
            }
            Log.d(javaClass.simpleName, "AutosaveTimer started.")
            return
        }

        if (!loadMetaFromSharedPrefs(applicationContext)) {
            with(AlertDialog.Builder(this)){
                setPositiveButton("Open", { dialog, id ->
                    openFile()
                })
                setNeutralButton("New", { dialog, id ->
                    selectFileForSaveAs()
                })
                show()
            }
        }

        if (intent.data !== null) {
            readFile(intent.data!!)
            Log.d(javaClass.simpleName,"Loading on resume from intent: ${thisFileUri}")
            intent.data = null
        } else if (thisFileUri !== null ) {
            readFile(thisFileUri!!)
            Log.d(javaClass.simpleName,"Loading on resume from thisFileUri: ${thisFileUri}")
        }
    }
    override fun onNewIntent(intent:Intent) {
        super.onNewIntent(intent)

        Log.i(javaClass.simpleName, "intent data onNewIntent: ${intent.data.toString()}\nIntent action: ${intent.action}")

        var uri:Uri? = null
        var mimeType: String? = null

        if (intent.action == Intent.ACTION_SEND && intent.extras != null) {
            if (intent.extras!!.get(Intent.EXTRA_STREAM) != null){
                uri = intent.extras!!.get(Intent.EXTRA_STREAM) as Uri
                mimeType = "image"
            } else {
                uri = parse(intent.extras!!.getCharSequence(Intent.EXTRA_TEXT).toString())
            }
        } else if (intent.action == Intent.ACTION_VIEW && intent.data != null) {
            uri = parse(intent.dataString)
            mimeType = contentResolver.getType(uri)
            if (mimeType != null) {
                if (mimeType.split("/")[0] == "text" ) {
                    Log.i(javaClass.simpleName, "Wanna open a text file")
                    mimeType = "text"
                }
                else if (mimeType.split("/")[0] == "image" ) {
                    Log.i(javaClass.simpleName, "Wanna upload a image")
                    mimeType = "image"
                }
            }
        }
        Log.i(javaClass.simpleName,"Got a new intent with URI: $uri")
        if (uri != null && uri.toString()!="null") {
            intentScheme = uri.scheme!!
            if (intentScheme == "content") {
                Log.i(javaClass.simpleName, "content intent")
            }
            else if (intentScheme =="http" || intentScheme == "https") {
                intentScheme = "link"
                Log.i(javaClass.simpleName, "link intent")
            }
            else Log.i(javaClass.simpleName, "unknown intent")
        }

        if (intentScheme == "link" && uri.toString() !="null") {
            mdToAppend += "[](${uri})\n"
            intent.putExtra(Intent.EXTRA_TEXT, null as CharSequence?)
        }
        if (intentScheme == "content") {
            if (mimeType == "image" && uri != null) {
                pushImage(uri)
                intent.putExtra(Intent.EXTRA_STREAM, null as Uri?)
            } else if (mimeType == "text" && intent.data != null ) {
                thisFileUri = uri
                saveMetaToSharedPrefs(applicationContext)
                Log.d(javaClass.simpleName,"wanna start app with new txt")
            }
        }
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        super.onCreateOptionsMenu(menu)
        Log.d(javaClass.simpleName,"create options menu")
        menuInflater.inflate(R.menu.ghost_menu, menu)
        return true
    }

    override fun onPrepareOptionsMenu(menu: Menu): Boolean {
        menu.findItem(R.id.delete_ghost).setVisible(deleteVisible)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        val id = item.itemId
        when (id) {
            R.id.new_file -> {
                truncate = true
                selectFileForSaveAs()
            }

            R.id.open_file -> {
                openFile()
            }

            R.id.save_file -> {
                selectFileForSaveAs()
            }

            R.id.push_ghost -> {
                with(AlertDialog.Builder(this)){
                    if (metaData.get("url") != null) setTitle("Update ghost posting to ${metaData.get("url")}?")
                    else setTitle("Push to ghost instance ${credManager.instance}?")

                    setPositiveButton("Yes", { dialog, id ->
                        webView.evaluateJavascript("getHtml();", {
                            val msg = URLDecoder.decode(it.removeSurrounding("\""))
                            if (metaData.metaData.get("url") !== null) {
                                CoroutineScope(Dispatchers.Main).launch {
                                    withContext(Dispatchers.IO) {
                                        if (metaData.getId() == null) {
                                            this@MainActivity.runOnUiThread({
                                                Toast.makeText(
                                                    this@MainActivity,
                                                    "No ID for url in Metadata found. Deleted? Trying repost.",
                                                    Toast.LENGTH_LONG
                                                ).show()
                                            })
                                            sendPosting(html=msg, author = credManager.username)
                                        } else {
                                            Log.i(javaClass.simpleName,"posting $msg")
                                            updatePost(
                                                title = metaData.get("title") ?: getDisplayName(applicationContext, thisFileUri),
                                                author = credManager.username,
                                                html = msg,
                                                id = metaData.getId()!!
                                            )
                                        }
                                    }
                                }
                            } else
                                sendPosting(html=msg, author = credManager.username)
                        })
                    })
                    setNeutralButton("No", { dialog, id -> })
                    show()
                }
            }
            R.id.settings -> {
                ghostSettings.launch(Intent(this, LoginActivity::class.java))
            }
            R.id.image -> {
                pickMultipleVisualMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageAndVideo))
            }
            R.id.metadata -> {
                ghostMetaData.launch(Intent(this, MetadataActivity::class.java))
            }
            R.id.delete_ghost -> {
                checkGhostConnection()
                var response: retrofit2.Response<ResponseBody> = retrofit2.Response.error(
                    444,
                    "error".toResponseBody("text/plain".toMediaTypeOrNull())
                )
                CoroutineScope(Dispatchers.Main).launch {
                    withContext(Dispatchers.IO) {
                        if (metaData.ID == null) {
                            if (metaData.getId() == null) {
                                // No ID for URL found
                                saveFile()
                                deleteVisible = false
                                invalidateOptionsMenu()
                                return@withContext false
                            }
                        }
                        try {
                            response = api.postApi.deletePost(metaData.ID!!).execute()
                        } catch (e:Exception) {
                            Log.d(javaClass.simpleName, "Exception during DELETE: $e")
                        }
                        Log.d(javaClass.simpleName, "result: ${response.code()}")
                    }
                    if (response.isSuccessful) {
                        this@MainActivity.runOnUiThread({
                            Toast.makeText(
                                this@MainActivity,
                                "Post under ${metaData.metaData.get("url")} got deleted.",
                                Toast.LENGTH_LONG
                            ).show()
                        })
                        Log.d(javaClass.simpleName, "Post under ${metaData.metaData.get("url")} got deleted.")
                        metaData.metaData.minusAssign("url")
                        metaData.ID=null
                        deleteVisible = false
                        invalidateOptionsMenu()
                        saveFile()
                    }
                }
            }
        }
        return true
    }

    fun pushImage(uri: Uri) {
        // see https://androidfortechs.blogspot.com/2020/12/how-to-convert-uri-to-file-android-10.html
        val file = getFile(applicationContext, uri)!!

        val instance = credManager.instance
        if (instance == "nowhere") {
            checkGhostConnection()
            return
        }

        api = ghostAPI(applicationContext, instance)

        var response: retrofit2.Response<imagesObj> = retrofit2.Response.error(
            444,
            "error".toResponseBody("text/plain".toMediaTypeOrNull())
        )
        CoroutineScope(Dispatchers.Main).launch {
            withContext(Dispatchers.IO) {
                try {
                    response = api.postApi.pushMyImage(
                        MultipartBody.Part.createFormData(
                            "file",
                            file.name,
                            file.asRequestBody("image/jpeg".toMediaTypeOrNull())
                        )
                    ).execute()
                } catch (e: Exception) {
                    this@MainActivity.runOnUiThread(Runnable() {
                        Toast.makeText(
                            this@MainActivity,
                            "Exception during image upload: $e",
                            Toast.LENGTH_SHORT
                        ).show()
                    })
                    Log.i(javaClass.simpleName, "Exception during image upload:\n$e")
                }

                if (response.isSuccessful) {
                    val imgUrl = response.body()!!.images[0].url
                    Log.d(javaClass.simpleName, "\"${file.name}\" uploaded to \"$imgUrl\"")
                    mdToAppend += "![${file.name}]($imgUrl)\n"
                    this@MainActivity.runOnUiThread(Runnable() {
                        Toast.makeText(
                            this@MainActivity,
                            "\"${file.name}\" uploaded to \"$imgUrl\"",
                            Toast.LENGTH_SHORT
                        ).show()
                        webView.evaluateJavascript("pasteText()") {}
                    })
                }
            }
        }
    }

    fun updatePost(title: String, html: String, author: String, id: String) : retrofit2.Response<Any> {
        val post = sendPost(title, updated_at = metaData.updatedAt!!, authors = listOf(author), html, feature_image = metaData.get("feature_image"))
        val postings = sendPostList(listOf(post))

        val mUrl = parse(metaData.get("url"))
        val mApiHost = mUrl.scheme + "://" + mUrl.host

        if (api.baseUrl != mApiHost) api = ghostAPI(applicationContext, mApiHost)
        credManager.saveCredentialsToSharedPrefs(Credentials(mApiHost,author))
        Log.d(javaClass.simpleName,"Updating post to ${mUrl}")

        try {
            val response = api.postApi.updatePost(id, postings).execute()

            if (response.isSuccessful) {
                val resp = JSONObject(response.body()!!.string())
                Log.d(javaClass.simpleName, "Updated: ${resp.getJSONArray("posts").getJSONObject(0).getString("url")}")

                metaData.updatedAt= resp.getJSONArray("posts").getJSONObject(0).getString("updated_at")

                val intent = Intent(Intent.ACTION_VIEW).setData(parse(metaData.metaData.get("url")))
                try {
                    startActivity(intent)
                } catch (e: ActivityNotFoundException) {
                    this.runOnUiThread({
                        Toast.makeText(this, "No eligble app installed.", Toast.LENGTH_LONG).show()
                        Log.i(javaClass.simpleName, e.toString())
                    })
                }
            } else {
                this.runOnUiThread({
                    Toast.makeText(
                        this,
                        "error while updating post.\n${response.errorBody()!!.string()}",
                        Toast.LENGTH_LONG
                    ).show()
                })
                Log.i(javaClass.simpleName, response.errorBody()!!.string())
            }
            return response as retrofit2.Response<Any>
        } catch (ex: Exception) {
            Log.d(javaClass.simpleName, "Couldn't update posting. Exception: ${ex}")
            return retrofit2.Response.error(
                444,
                "error".toResponseBody("text/plain".toMediaTypeOrNull())
            )
        }
    }
    fun sendPosting(html: String, author: String){
        val title = metaData.get("title") ?: getDisplayName(applicationContext, thisFileUri)
        val post = sendPost(title, updated_at = Instant.now().toString(), authors = listOf(author), html, feature_image = metaData.get("feature_image"))
        val postings = sendPostList(listOf(post))
        lateinit var response: Response<ResponseBody>
        CoroutineScope(Dispatchers.Main).launch {
            withContext(Dispatchers.IO) {
                try {
                    val instance = credManager.instance
                    if (instance == "nowhere") {
                        checkGhostConnection()
                        return@withContext
                    }

                    api = ghostAPI(applicationContext, instance)
                    response = api.postApi.pushPost(postings).execute()

                    Log.d(javaClass.simpleName, "result: ${response.code()}")
                    if (response.isSuccessful) {
                        val resp = JSONObject(response.body()!!.string())
                        val post = resp.getJSONArray("posts").getJSONObject(0)
                        val uri = parse(post.getString("url"))
                        metaData.ID = post.getString("id")
                        metaData.updatedAt = post.getString("updated_at")
                        Log.d(javaClass.simpleName, "Uploaded to: $uri\nID: ${metaData.ID}")

                        metaData.put("url", post.getString("url"))

                        saveFile()

                        deleteVisible = true
                        invalidateOptionsMenu()

                        val intent = Intent(Intent.ACTION_VIEW).setData(uri)
                        try {
                            startActivity(intent)
                        } catch (e: ActivityNotFoundException) {
                            this@MainActivity.runOnUiThread({
                                Toast.makeText(
                                    this@MainActivity,
                                    "No eligble app installed.",
                                    Toast.LENGTH_LONG
                                )
                                    .show()
                                Log.i(javaClass.simpleName, e.toString())
                            })
                        }
                    } else if (response.code() == 403) {
                        this@MainActivity.runOnUiThread({
                            Toast.makeText(
                                this@MainActivity,
                                "You are not authorized to add posts",
                                Toast.LENGTH_LONG
                            ).show()
                        })
                    }
                } catch (ex: Exception) {
                    Log.d(javaClass.simpleName, "Couldn't send posting. Exception: ${ex}")
                    return@withContext
                }
            }
        }
    }

    fun checkGhostConnection(): Boolean {
        if (ghostConnection) return true
        if (SharedPrefsCookiePersistor(applicationContext).loadAll().size == 0) {
            ghostSettings.launch(Intent(this, LoginActivity::class.java))
            return false
        }
        // we have a cookie
        return true
    }

     fun openFile() {
        val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
            addCategory(Intent.CATEGORY_OPENABLE)
            putExtra(DocumentsContract.EXTRA_INITIAL_URI, thisFileUri)

            type = "text/*"

            addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
            addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
        }
        startActivityForResult(intent, OPEN_FILE)
    }

    fun selectFileForSaveAs() {
        val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
            addCategory(Intent.CATEGORY_OPENABLE)
            type = "text/*"
            putExtra(Intent.EXTRA_TITLE, getDisplayName(applicationContext, thisFileUri))
            addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
            addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
        }
        this.runOnUiThread({
            Toast.makeText(
                this,
                "Please select a location for the new file.",
                Toast.LENGTH_LONG
            ).show()
        })
        startActivityForResult(intent, CREATE_FILE)
        Log.i(javaClass.simpleName, "Buffer geschrieben")
    }

    /* Cut is not possible currently because of the deactivation of the custom text selection and
   context menu mechanisms.

    override fun onActionModeStarted(mode: ActionMode?) {
        super.onActionModeStarted(mode)
        val myContextMenu = mode!!.menu
        myContextMenu.add(NONE, CONTEXT_MENU_CUT, NONE,"Cut" )
        myContextMenu.getItem(0).setOnMenuItemClickListener {
            Log.i(javaClass.simpleName,"somwhere in between")
            when(it.itemId) {
                CONTEXT_MENU_CUT -> {
                    webView.evaluateJavascript("dispatchCut();", ValueCallback<String>() {})
                    true
                }
                else -> {false}
            }
        }
    }
*/
    override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
        super.onActivityResult(requestCode, resultCode, resultData)

        if (requestCode == CREATE_FILE && resultCode == Activity.RESULT_OK) {
            // The result data contains a URI for the document or directory that
            // the user selected.
            resultData?.data?.also { uri ->
                getContentResolver().takePersistableUriPermission(
                    uri,
                    Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                )
                Log.d(javaClass.simpleName, "Saving to: ${getDisplayName(applicationContext, uri)}")
                if (getDisplayName(applicationContext, thisFileUri) + " (1)" != getDisplayName(applicationContext, uri)) thisFileUri = uri
                saveAs()
            }
        } else if (requestCode == OPEN_FILE && resultCode == Activity.RESULT_OK) {
            resultData?.data?.also { uri ->
                getContentResolver().takePersistableUriPermission(
                    uri,
                    Intent.FLAG_GRANT_READ_URI_PERMISSION and Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                )
                thisFileUri = uri
                saveMetaToSharedPrefs(applicationContext)
                if (metaData.metaData.get("url") == null) {
                    deleteVisible = false
                    invalidateOptionsMenu()
                } else {
                    deleteVisible = true
                    invalidateOptionsMenu()
                }
            }
        }
    }

    fun saveFile() {
        if (thisFileUri == null) {
            Log.d(javaClass.simpleName, "File Uri got null. Can't save.")
            return
        }
        lateinit var textFile: ParcelFileDescriptor

        try {
            textFile = contentResolver.openFileDescriptor(thisFileUri!!, "w")!!
            textFile.checkError()
        } catch (e: java.lang.IllegalArgumentException) {
            Log.d(javaClass.simpleName, "Problem with saving file\nBug in Android 11?\n${e.stackTraceToString()}")
            this.runOnUiThread(
                {
                    Toast.makeText(
                        this,
                        "Problem with saving file\n" +
                                "Bug in Android 11?\n" +
                                "Workaround: Load file with normal file selection",
                        Toast.LENGTH_LONG
                    ).show()
                }
            )
            return
            // TODO: implement workaround for this bug. See: https://stackoverflow.com/q/69248596
        } catch (e: java.io.FileNotFoundException) {
            Log.d(javaClass.simpleName, "File not found. Deleted while loaded?\n${e.stackTraceToString()}")
            this.runOnUiThread(
                {
                    Toast.makeText(
                        this,
                        "File not found. Deleted while loaded?",
                        Toast.LENGTH_LONG
                    ).show()
                }
            )
            selectFileForSaveAs()
            return
        }
        catch (e: Exception) {
            Log.d(javaClass.simpleName, "Problem with accessing file\n${e.stackTraceToString()}")
            this.runOnUiThread(
                {
                    Toast.makeText(
                        this,
                        "Problem with accessing file\n$e",
                        Toast.LENGTH_LONG
                    ).show()
                }
            )
            return
        }

        try {
            this.runOnUiThread({
                webView.evaluateJavascript("getValue();") {
                    if (it.equals("") || it.equals("\"\"") || it.equals("\"null\"") || it.equals("null")) {
                        Log.d(javaClass.simpleName,"Editor delivered empty content. No save.")
                        Toast.makeText(
                            this,
                            "Editor delivered empty content. Didn't save. Please reopen manually.",
                            Toast.LENGTH_LONG
                        ).show()
                        return@evaluateJavascript
                    }
                    mdeValue =
                        metaData.toString() + URLDecoder.decode(it.removeSurrounding("\""))
                    if (mdeValue.length.toLong() == textFile.statSize) {
                        Log.d(javaClass.simpleName, "No change on disk, file not saved.\n$mdeValue")
                        return@evaluateJavascript
                    }
                    contentResolver.openFileDescriptor(thisFileUri!!, "wt")?.use {
                        FileOutputStream(it.fileDescriptor).use {
                            it.write(mdeValue.toByteArray())
                        }
                    }
                    Log.d(javaClass.simpleName, "File saved: ${thisFileUri}")
                    textFile.close()
                    this@MainActivity.runOnUiThread {
                        webView.evaluateJavascript(
                            "easyMDE.codemirror.doc.markClean();" +
                                   "easyMDE.updateStatusBar(\"editor-statusbar-left\",saveStatus());"
                            , {})
                    }
                }
            })
        } catch (e: Exception) {
            Toast.makeText(
                this,
                "Error during writing.\n$e",
                Toast.LENGTH_LONG
            ).show()
            return
        }
    }

    private fun saveAs() {
        try {
            lateinit var textFile: ParcelFileDescriptor
            try {
                textFile = contentResolver.openFileDescriptor(thisFileUri!!, "w")!!
                textFile.checkError()
            } catch (e: Exception) {
                Toast.makeText(
                    this,
                    "Problem with accessing file\n$e",
                    Toast.LENGTH_LONG
                ).show()
                return
            }
            if (truncate) {
                mdeValue = ""
                truncate = false
            }
            FileOutputStream(textFile.fileDescriptor).use {
                it.write(mdeValue.toByteArray())
            }
            Toast.makeText(
                this,
                "File saved.",
                Toast.LENGTH_LONG
            ).show()
            textFile.close()
            saveMetaToSharedPrefs(applicationContext)
            Log.i(javaClass.simpleName, "file newly written")
        } catch (e: Exception) {
            Toast.makeText(
                this,
                "Error during writing.\n$e",
                Toast.LENGTH_LONG
            ).show()
        }
    }

    @Throws(IOException::class)
    private fun readFile(uri: Uri) {
        try{
            autosaveTimer.cancel()
            Log.d(javaClass.simpleName, "Stopped autosaveTimer.")
        } catch (e:UninitializedPropertyAccessException) {
            Log.d (javaClass.simpleName, "Couldn't cancel autosaveTimer. Not yet initialized")
        }

        //TODO: Which permissions are really needed?
        try {
            contentResolver.takePersistableUriPermission(
                uri,
                Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
            )
        } catch (e: java.lang.SecurityException) {
            Log.d(javaClass.simpleName, "Couldn't get persistable URI permissions. Trying to grant 'em.\n${e.stackTraceToString()}")
        }
        try {
            grantUriPermission(
                "org.wntr.mdeditor",
                uri,
                Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
            )
        } catch (e: java.lang.SecurityException) {
            Log.d(javaClass.simpleName, e.stackTraceToString())
            this.runOnUiThread({
                Toast.makeText(
                    this,
                    "Security exception during file load. Try open manually.",
                    Toast.LENGTH_LONG
                ).show()
            })
        }

        CoroutineScope(Dispatchers.Main).launch {
            withContext(Dispatchers.IO) {
                try {
                    if (!checkURIResource(applicationContext, uri)) {
                        Log.d(javaClass.simpleName, "URI unavailable")
                        this@MainActivity.runOnUiThread({
                            Toast.makeText(
                                this@MainActivity,
                                "URI check failed\n" +
                                        "Better open from another storage location\n" ,
                                Toast.LENGTH_LONG
                            ).show()
                        })
                        thisFileUri = null
                        return@withContext
                    }
                    contentResolver.openInputStream(uri)?.use { inputStream ->
                        BufferedReader(InputStreamReader(inputStream)).use { reader ->
                            var i = ""
                            if (reader.ready()) {
                                i = reader.readLine()
                                var line: String? = ""
                                while (line != null) {
                                    i += line + '\n'
                                    line = reader.readLine()
                                }
                            }
                            reader.close()
                            mdeValue = metaData.extractMetadataFromMarkdown(i)
                            if (metaData.get("url") !== null) {
                                val url = parse(metaData.get("url"))
                                deleteVisible = true
                                val apiHost = url.scheme + "://" + url.host
                                Log.i(javaClass.simpleName, "Starting api controller for: $apiHost")
                                api = ghostAPI(applicationContext, apiHost)
                            } else {
                                deleteVisible = false
                            }
                            invalidateOptionsMenu()
                        }
                    }
                }
                catch (e: java.io.FileNotFoundException) {
                    Log.d(javaClass.simpleName, "File not found during reading:\n${e.stackTraceToString()}")
                    this@MainActivity.runOnUiThread({
                        Toast.makeText(
                            this@MainActivity,
                            "File not found during reading.\n" +
                                 "Current Buffer is not connected to any file now.\n" +
                                 "Save with other file name or open another.",
                                    Toast.LENGTH_LONG
                        ).show()
                    })
                    thisFileUri = null
                    return@withContext
                }
                catch (e: java.lang.NullPointerException) {
                    Log.d(javaClass.simpleName, "Nullpointerexception. Maybe file deleted in the meantime?.\n$e")
                    this@MainActivity.runOnUiThread({
                        Toast.makeText(
                            this@MainActivity,
                            "Nullpointerexception. Maybe file deleted in the meantime?.\n$e",
                            Toast.LENGTH_LONG
                        ).show()
                    })
                    thisFileUri = null
                    return@withContext
                }
                catch (e: Exception) {
                    Log.d(javaClass.simpleName, e.stackTraceToString())
                    this@MainActivity.runOnUiThread({
                        Toast.makeText(
                            this@MainActivity,
                            "Error during reading.\n$e",
                            Toast.LENGTH_LONG
                        ).show()
                    })
                    thisFileUri = null
                    return@withContext
                }
                while (!easyMDELoaded) {
                    sleep(100)
                    Log.d(javaClass.simpleName,"waiting for easyMDE")
                }
                this@MainActivity.runOnUiThread({

                    val script = "if (typeof easyMDE !== 'undefined') {" +
                                 "easyMDE.codemirror.doc.setValue(`${mdeValue}`);" +
                                 "easyMDE.codemirror.doc.markClean();" +
                                 "easyMDE.updateStatusBar(\"editor-statusbar-left\",saveStatus());" +
                                 "easyMDE.codemirror.focus();" +
                                 "easyMDE.codemirror.doc.setCursor(JSON.parse(`${metaData.cursor}`));" +
                                 "pasteText();" +
                                 "easyMDE.updateStatusBar(\"displayName\",\"${getDisplayName(applicationContext, uri)}\");}"

                    Log.d(javaClass.simpleName, "executing in webview:\n${script}")
                    webView.evaluateJavascript(script, {
                        thisFileUri = uri
                        Log.d(javaClass.simpleName,"File read: ${thisFileUri}")
                        autosaveTimer = fixedRateTimer("timer",true,0,5000){
                            this@MainActivity.runOnUiThread {
                                val script = "easyMDE.codemirror.doc.isClean();"
                                webView.evaluateJavascript(script , {
                                    if (it == "false" && thisFileUri != null) {
                                        saveFile()
                                    }
                                })
                            }
                        }
                        Log.d(javaClass.simpleName, "AutosaveTimer started.")
                    })
                    webView.requestFocus()
                })
            }
        }
    }

    override fun onPause() {
        super.onPause()
        try{
            autosaveTimer.cancel()
        } catch (e:Exception) {
            Log.d (javaClass.simpleName, "Couldn't cancel autosaveTimer.\n$e")
        }
        saveFile()
        try {
            webView.evaluateJavascript("easyMDE.codemirror.doc.getCursor();") {
                metaData.cursor = it
                Log.i(javaClass.simpleName, "Cursor: $it")
                saveMetaToSharedPrefs(applicationContext)
            }
        } catch (e: UninitializedPropertyAccessException) {
            Log.d(javaClass.simpleName, "Webview not yet loaded.\n$e")
        }
        easyMDELoaded = false
        Log.i(javaClass.simpleName, "\"onPause\" durchlaufen")
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        super.onRestoreInstanceState(savedInstanceState)
        Log.i(javaClass.simpleName, "\"onRestoreInstanceState\" durchlaufen")
    }
}
