/*
 * Copyright (c) 2012-2016 Arne Schwabe
 * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
 */

package de.blinkt.openvpn.activities

import android.Manifest
import android.annotation.TargetApi
import android.app.Activity
import android.app.AlertDialog
import android.content.ActivityNotFoundException
import android.content.Intent
import android.content.pm.PackageManager
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.OpenableColumns
import android.security.KeyChain
import android.text.TextUtils
import android.util.Base64
import android.util.Pair
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.lifecycle.lifecycleScope
import de.blinkt.openvpn.R
import de.blinkt.openvpn.VpnProfile
import de.blinkt.openvpn.core.ConfigParser
import de.blinkt.openvpn.core.ConfigParser.ConfigParseError
import de.blinkt.openvpn.core.ProfileManager
import de.blinkt.openvpn.fragments.Utils
import de.blinkt.openvpn.views.FileSelectLayout
import de.blinkt.openvpn.views.FileSelectLayout.FileSelectCallback
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.*
import java.net.URLDecoder
import java.util.*

class ConfigConverter : BaseActivity(), FileSelectCallback, View.OnClickListener {

    private var mResult: VpnProfile? = null

    @Transient
    private var mPathsegments: List<String>? = null

    private var mAliasName: String? = null


    private val fileSelectMap = HashMap<Utils.FileType, FileSelectLayout?>()
    private var mEmbeddedPwFile: String? = null
    private val mLogEntries = Vector<String>()
    private var mSourceUri: Uri? = null
    private lateinit var mProfilename: EditText
    private lateinit var mCompatmode: Spinner
    private lateinit var mCompatmodeLabel: TextView
    private lateinit var mTLSProfile: Spinner
    private lateinit var mTLSProfileLabel: TextView
    private lateinit var mLogLayout: LinearLayout
    private lateinit var mProfilenameLabel: TextView

    override fun onClick(v: View) {
        if (v.id == R.id.fab_save)
            userActionSaveProfile()
        if (v.id == R.id.permssion_hint && Build.VERSION.SDK_INT == Build.VERSION_CODES.M)
            doRequestSDCardPermission(PERMISSION_REQUEST_EMBED_FILES)

    }

    @TargetApi(Build.VERSION_CODES.M)
    private fun doRequestSDCardPermission(requestCode: Int) {
        requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), requestCode)
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        // Permission declined, do nothing
        if (grantResults.size == 0 || grantResults[0] == PackageManager.PERMISSION_DENIED)
            return

        // Reset file select dialogs
        findViewById<View>(R.id.files_missing_hint).visibility = View.GONE
        findViewById<View>(R.id.permssion_hint).visibility = View.GONE
        val fileroot = findViewById<View>(R.id.config_convert_root) as LinearLayout
        var i = 0
        while (i < fileroot.childCount) {
            if (fileroot.getChildAt(i) is FileSelectLayout)
                fileroot.removeViewAt(i)
            else
                i++
        }

        if (requestCode == PERMISSION_REQUEST_EMBED_FILES)
            embedFiles(null)
        else if (requestCode == PERMISSION_REQUEST_READ_URL) {
            if (mSourceUri != null)
                doImportUri(mSourceUri!!)
        }
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        if (item.itemId == R.id.cancel) {
            setResult(Activity.RESULT_CANCELED)
            finish()
        } else if (item.itemId == R.id.ok) {
            return userActionSaveProfile()
        }

        return super.onOptionsItemSelected(item)

    }

    private fun userActionSaveProfile(): Boolean {
        if (mResult == null) {
            log(R.string.import_config_error)
            Toast.makeText(this, R.string.import_config_error, Toast.LENGTH_LONG).show()
            return true
        }

        mResult!!.mName = mProfilename.text.toString()
        val vpl = ProfileManager.getInstance(this)
        if (vpl.getProfileByName(mResult!!.mName) != null) {
            mProfilename.error = getString(R.string.duplicate_profile_name)
            return true
        }

        mResult!!.mCompatMode = Utils.mapCompatMode(mCompatmode.selectedItemPosition)

        val selectedTLSProfile = translSelectionToProfileName(mTLSProfile.selectedItemPosition)
        if (selectedTLSProfile != "legacy" || !mResult!!.mTlSCertProfile.isNullOrEmpty()) {
            mResult!!.mTlSCertProfile = selectedTLSProfile
        }

        /* If you need compability with such an old version or such a low security profile
         * there is a high chance that the legacy provider is needed as well */
        if (mResult!!.mCompatMode in 1..20400 || selectedTLSProfile == "insecure")
            mResult!!.mUseLegacyProvider = true;

        val intent = installPKCS12()

        if (intent != null)
            startActivityForResult(intent, RESULT_INSTALLPKCS12)
        else
            saveProfile()

        return true
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        if (mResult != null)
            outState.putSerializable(VPNPROFILE, mResult)
        outState.putString("mAliasName", mAliasName)


        val logentries = mLogEntries.toTypedArray()

        outState.putStringArray("logentries", logentries)

        val fileselects = IntArray(fileSelectMap.size)
        var k = 0
        for (key in fileSelectMap.keys) {
            fileselects[k] = key.value
            k++
        }
        outState.putIntArray("fileselects", fileselects)
        outState.putString("pwfile", mEmbeddedPwFile)
        outState.putParcelable("mSourceUri", mSourceUri)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, result: Intent?) {
        if (requestCode == RESULT_INSTALLPKCS12 && resultCode == Activity.RESULT_OK) {
            showCertDialog()
        }

        if (resultCode == Activity.RESULT_OK && requestCode >= CHOOSE_FILE_OFFSET) {
            val type = Utils.FileType.getFileTypeByValue(requestCode - CHOOSE_FILE_OFFSET)


            val fs = fileSelectMap[type]
            fs!!.parseResponse(result, this)

            val data = fs.data

            when (type) {
                Utils.FileType.USERPW_FILE -> mEmbeddedPwFile = data
                Utils.FileType.PKCS12 -> mResult!!.mPKCS12Filename = data
                Utils.FileType.TLS_AUTH_FILE -> mResult!!.mTLSAuthFilename = data
                Utils.FileType.CA_CERTIFICATE -> mResult!!.mCaFilename = data
                Utils.FileType.CLIENT_CERTIFICATE -> mResult!!.mClientCertFilename = data
                Utils.FileType.KEYFILE -> mResult!!.mClientKeyFilename = data
                Utils.FileType.CRL_FILE -> mResult!!.mCrlFilename = data
                else -> throw RuntimeException("Type is wrong somehow?")
            }
        }

        super.onActivityResult(requestCode, resultCode, result)
    }

    private fun saveProfile() {
        val result = Intent()
        val vpl = ProfileManager.getInstance(this)

        if (!TextUtils.isEmpty(mEmbeddedPwFile))
            ConfigParser.useEmbbedUserAuth(mResult, mEmbeddedPwFile)

        vpl.addProfile(mResult)
        ProfileManager.saveProfile(this, mResult)
        vpl.saveProfileList(this)
        result.putExtra(VpnProfile.EXTRA_PROFILEUUID, mResult!!.uuid.toString())
        setResult(Activity.RESULT_OK, result)
        finish()
    }

    fun showCertDialog() {
        try {

            KeyChain.choosePrivateKeyAlias(
                this,
                { alias ->
                    // Credential alias selected.  Remember the alias selection for future use.
                    mResult!!.mAlias = alias
                    saveProfile()
                },
                arrayOf("RSA", "EC"), null, // issuer, null for any
                mResult!!.mServerName, // host name of server requesting the cert, null if unavailable
                -1, // port of server requesting the cert, -1 if unavailable
                mAliasName
            )// List of acceptable key types. null for any
            // alias to preselect, null if unavailable
        } catch (anf: ActivityNotFoundException) {
            val ab = AlertDialog.Builder(this)
            ab.setTitle(R.string.broken_image_cert_title)
            ab.setMessage(R.string.broken_image_cert)
            ab.setPositiveButton(android.R.string.ok, null)
            ab.show()
        }

    }


    private fun installPKCS12(): Intent? {

        if (!(findViewById<View>(R.id.importpkcs12) as CheckBox).isChecked) {
            setAuthTypeToEmbeddedPKCS12()
            return null

        }
        var pkcs12datastr = mResult!!.mPKCS12Filename
        if (VpnProfile.isEmbedded(pkcs12datastr)) {
            val inkeyIntent = KeyChain.createInstallIntent()

            pkcs12datastr = VpnProfile.getEmbeddedContent(pkcs12datastr)


            val pkcs12data = Base64.decode(pkcs12datastr, Base64.DEFAULT)


            inkeyIntent.putExtra(KeyChain.EXTRA_PKCS12, pkcs12data)

            if (mAliasName == "")
                mAliasName = null

            if (mAliasName != null) {
                inkeyIntent.putExtra(KeyChain.EXTRA_NAME, mAliasName)
            }
            return inkeyIntent

        }
        return null
    }


    private fun setAuthTypeToEmbeddedPKCS12() {
        if (VpnProfile.isEmbedded(mResult!!.mPKCS12Filename)) {
            if (mResult!!.mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE)
                mResult!!.mAuthenticationType = VpnProfile.TYPE_USERPASS_PKCS12

            if (mResult!!.mAuthenticationType == VpnProfile.TYPE_KEYSTORE)
                mResult!!.mAuthenticationType = VpnProfile.TYPE_PKCS12

        }
    }


    private fun getUniqueProfileName(possibleName: String?): String {

        var i = 0

        val vpl = ProfileManager.getInstance(this)

        var newname = possibleName

        // 	Default to
        if (mResult!!.mName != null && ConfigParser.CONVERTED_PROFILE != mResult!!.mName)
            newname = mResult!!.mName

        while (newname == null || vpl.getProfileByName(newname) != null) {
            i++
            if (i == 1)
                newname = getString(R.string.converted_profile)
            else
                newname = getString(R.string.converted_profile_i, i)
        }

        return newname
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        val inflater = menuInflater
        inflater.inflate(R.menu.import_menu, menu)
        return true
    }

    private fun embedFile(
        filename: String?,
        type: Utils.FileType,
        onlyFindFileAndNullonNotFound: Boolean
    ): String? {
        if (filename == null)
            return null

        // Already embedded, nothing to do
        if (VpnProfile.isEmbedded(filename))
            return filename

        val possibleFile = findFile(filename, type)
        return if (possibleFile == null)
            if (onlyFindFileAndNullonNotFound)
                null
            else
                filename
        else if (onlyFindFileAndNullonNotFound)
            possibleFile.absolutePath
        else
            readFileContent(possibleFile, type == Utils.FileType.PKCS12)

    }


    private fun getFileDialogInfo(type: Utils.FileType): Pair<Int, String> {
        var titleRes = 0
        var value: String? = null
        when (type) {
            Utils.FileType.KEYFILE -> {
                titleRes = R.string.client_key_title
                if (mResult != null)
                    value = mResult!!.mClientKeyFilename
            }
            Utils.FileType.CLIENT_CERTIFICATE -> {
                titleRes = R.string.client_certificate_title
                if (mResult != null)
                    value = mResult!!.mClientCertFilename
            }
            Utils.FileType.CA_CERTIFICATE -> {
                titleRes = R.string.ca_title
                if (mResult != null)
                    value = mResult!!.mCaFilename
            }
            Utils.FileType.TLS_AUTH_FILE -> {
                titleRes = R.string.tls_auth_file
                if (mResult != null)
                    value = mResult!!.mTLSAuthFilename
            }
            Utils.FileType.PKCS12 -> {
                titleRes = R.string.client_pkcs12_title
                if (mResult != null)
                    value = mResult!!.mPKCS12Filename
            }

            Utils.FileType.USERPW_FILE -> {
                titleRes = R.string.userpw_file
                value = mEmbeddedPwFile
            }

            Utils.FileType.CRL_FILE -> {
                titleRes = R.string.crl_file
                value = mResult!!.mCrlFilename
            }
            Utils.FileType.OVPN_CONFIG -> {
                titleRes = 0
                value = null
            }
        }

        return Pair.create(titleRes, value)

    }

    private fun findFile(filename: String?, fileType: Utils.FileType): File? {
        val foundfile = findFileRaw(filename)

        if (foundfile == null && filename != null && filename != "") {
            log(R.string.import_could_not_open, filename)
        }
        fileSelectMap[fileType] = null

        return foundfile
    }

    private fun addMissingFileDialogs() {
        for ((key, value) in fileSelectMap) {
            if (value == null)
                addFileSelectDialog(key)
        }
    }

    private fun addFileSelectDialog(type: Utils.FileType?) {

        val fileDialogInfo = getFileDialogInfo(type!!)

        val isCert =
            type == Utils.FileType.CA_CERTIFICATE || type == Utils.FileType.CLIENT_CERTIFICATE
        val fl = FileSelectLayout(this, getString(fileDialogInfo.first), isCert, false)
        fileSelectMap[type] = fl
        fl.layoutParams = LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.WRAP_CONTENT
        )

        (findViewById<View>(R.id.config_convert_root) as LinearLayout).addView(fl, 2)
        findViewById<View>(R.id.files_missing_hint).visibility = View.VISIBLE
        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M)
            checkPermission()

        fl.setData(fileDialogInfo.second, this)
        val i = getFileLayoutOffset(type)
        fl.setCaller(this, i, type)

    }

    @TargetApi(Build.VERSION_CODES.M)
    private fun checkPermission() {
        if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            findViewById<View>(R.id.permssion_hint).visibility = View.VISIBLE
            findViewById<View>(R.id.permssion_hint).setOnClickListener(this)
        }
    }

    private fun getFileLayoutOffset(type: Utils.FileType): Int {
        return CHOOSE_FILE_OFFSET + type.value
    }


    private fun findFileRaw(filename: String?): File? {
        if (filename == null || filename == "")
            return null

        // Try diffent path relative to /mnt/sdcard
        val sdcard = Environment.getExternalStorageDirectory()
        val root = File("/")

        val dirlist = HashSet<File>()

        for (i in mPathsegments!!.indices.reversed()) {
            var path = ""
            for (j in 0..i) {
                path += "/" + mPathsegments!![j]
            }
            // Do a little hackish dance for the Android File Importer
            // /document/primary:ovpn/openvpn-imt.conf


            if (path.indexOf(':') != -1 && path.lastIndexOf('/') > path.indexOf(':')) {
                var possibleDir = path.substring(path.indexOf(':') + 1, path.length)
                // Unquote chars in the  path
                try {
                    possibleDir = URLDecoder.decode(possibleDir, "UTF-8")
                } catch (ignored: UnsupportedEncodingException) {
                }

                possibleDir = possibleDir.substring(0, possibleDir.lastIndexOf('/'))




                dirlist.add(File(sdcard, possibleDir))

            }
            dirlist.add(File(path))


        }
        dirlist.add(sdcard)
        dirlist.add(root)


        val fileparts = filename.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
        for (rootdir in dirlist) {
            var suffix = ""
            for (i in fileparts.indices.reversed()) {
                if (i == fileparts.size - 1)
                    suffix = fileparts[i]
                else
                    suffix = fileparts[i] + "/" + suffix

                val possibleFile = File(rootdir, suffix)
                if (possibleFile.canRead())
                    return possibleFile

            }
        }
        return null
    }

    internal fun readFileContent(possibleFile: File, base64encode: Boolean): String? {
        val filedata: ByteArray
        try {
            filedata = readBytesFromFile(possibleFile)
        } catch (e: IOException) {
            log(e.localizedMessage)
            return null
        }

        val data: String
        if (base64encode) {
            data = Base64.encodeToString(filedata, Base64.DEFAULT)
        } else {
            data = String(filedata)

        }

        return VpnProfile.DISPLAYNAME_TAG + possibleFile.name + VpnProfile.INLINE_TAG + data

    }


    @Throws(IOException::class)
    private fun readBytesFromFile(file: File): ByteArray {
        val input = FileInputStream(file)

        val len = file.length()
        if (len > VpnProfile.MAX_EMBED_FILE_SIZE)
            throw IOException("File size of file to import too large.")

        // Create the byte array to hold the data
        val bytes = ByteArray(len.toInt())

        // Read in the bytes
        var offset = 0
        var bytesRead: Int
        do {
            bytesRead = input.read(bytes, offset, bytes.size - offset)
            offset += bytesRead
        } while (offset < bytes.size && bytesRead >= 0)

        input.close()
        return bytes
    }

    internal fun embedFiles(cp: ConfigParser?) {
        // This where I would like to have a c++ style
        // void embedFile(std::string & option)

        if (mResult!!.mPKCS12Filename != null) {
            val pkcs12file = findFileRaw(mResult!!.mPKCS12Filename)
            if (pkcs12file != null) {
                mAliasName = pkcs12file.name.replace(".p12", "")
            } else {
                mAliasName = "Imported PKCS12"
            }
        }


        mResult!!.mCaFilename =
            embedFile(mResult!!.mCaFilename, Utils.FileType.CA_CERTIFICATE, false)
        mResult!!.mClientCertFilename =
            embedFile(mResult!!.mClientCertFilename, Utils.FileType.CLIENT_CERTIFICATE, false)
        mResult!!.mClientKeyFilename =
            embedFile(mResult!!.mClientKeyFilename, Utils.FileType.KEYFILE, false)
        mResult!!.mTLSAuthFilename =
            embedFile(mResult!!.mTLSAuthFilename, Utils.FileType.TLS_AUTH_FILE, false)
        mResult!!.mPKCS12Filename =
            embedFile(mResult!!.mPKCS12Filename, Utils.FileType.PKCS12, false)
        mResult!!.mCrlFilename = embedFile(mResult!!.mCrlFilename, Utils.FileType.CRL_FILE, true)
        if (cp != null) {
            mEmbeddedPwFile = cp.authUserPassFile
            mEmbeddedPwFile = embedFile(cp.authUserPassFile, Utils.FileType.USERPW_FILE, false)
        }

    }

    private fun updateFileSelectDialogs() {
        for ((key, value) in fileSelectMap) {
            value?.setData(getFileDialogInfo(key).second, this)
        }
    }

    private fun translateTLSProfileToSelection(tlsprofile: String?): Int {
        return when (tlsprofile) {
            "insecure" -> 0
            "legacy" -> 1
            "preferred" -> 2
            "suiteb" -> 3
            else -> 1
        }
    }

    private fun translSelectionToProfileName(selection: Int): String {
        return when (selection) {
            0 -> "insecure"
            1 -> "legacy"
            2 -> "preferred"
            3 -> "suiteb"
            else -> "legacy"
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val v  = layoutInflater.inflate(R.layout.config_converter, null)
        setUpEdgeEdgeInsetsListener(v, R.id.root_layout_config_converter)
        setContentView(v)

        val fab_button = findViewById<ImageButton?>(R.id.fab_save)
        if (fab_button != null) {
            fab_button.setOnClickListener(this)
            findViewById<View>(R.id.fab_footerspace).visibility = View.VISIBLE
        }

        mLogLayout = findViewById<View>(R.id.config_convert_root) as LinearLayout


        mProfilename = findViewById<View>(R.id.profilename) as EditText
        mProfilenameLabel = findViewById<View>(R.id.profilename_label) as TextView

        mCompatmode = findViewById(R.id.compatmode) as Spinner
        mCompatmodeLabel = findViewById(R.id.compatmode_label) as TextView

        mTLSProfile = findViewById(R.id.tls_profile) as Spinner
        mTLSProfileLabel = findViewById(R.id.tls_profile_label) as TextView

        if (savedInstanceState != null && savedInstanceState.containsKey(VPNPROFILE)) {
            mResult = savedInstanceState.getSerializable(VPNPROFILE) as VpnProfile?
            mAliasName = savedInstanceState.getString("mAliasName")
            mEmbeddedPwFile = savedInstanceState.getString("pwfile")
            mSourceUri = savedInstanceState.getParcelable("mSourceUri")
            mProfilename.setText(mResult!!.mName)
            mCompatmode.setSelection(Utils.mapCompatVer(mResult!!.mCompatMode))
            mTLSProfile.setSelection(translateTLSProfileToSelection(mResult?.mTlSCertProfile))

            if (savedInstanceState.containsKey("logentries")) {

                for (logItem in savedInstanceState.getStringArray("logentries")!!)
                    log(logItem)
            }
            if (savedInstanceState.containsKey("fileselects")) {

                for (k in savedInstanceState.getIntArray("fileselects")!!) {
                    addFileSelectDialog(Utils.FileType.getFileTypeByValue(k))
                }
            }
            return
        }


        if (intent != null) {
            doImportIntent(intent)

            // We parsed the intent, relay on saved instance for restoring
            setIntent(null)
        }
    }

    private fun doImportIntent(intent: Intent) {
        if (intent.action.equals(IMPORT_PROFILE_DATA)) {
            val data = intent.getStringExtra(Intent.EXTRA_TEXT)

            if (data != null) {
                lifecycleScope.launch {
                    startImportTask(
                        Uri.fromParts("inline", "inlinetext", null),
                        "imported profiles from AS", data)
                }
            }
        } else if (intent.action.equals(IMPORT_PROFILE) || intent.action.equals(Intent.ACTION_VIEW)) {
            val data = intent.data
            if (data != null) {
                mSourceUri = data
                doImportUri(data)

            }
        }
    }

    private fun doImportUri(data: Uri) {
        //log(R.string.import_experimental);
        log(R.string.importing_config, data.toString())
        var possibleName: String? = null
        if (data.scheme != null && data.scheme == "file" || data.lastPathSegment != null && (data.lastPathSegment!!.endsWith(
                ".ovpn"
            ) || data.lastPathSegment!!.endsWith(".conf"))
        ) {
            possibleName = data.lastPathSegment
            if (possibleName!!.lastIndexOf('/') != -1)
                possibleName = possibleName.substring(possibleName.lastIndexOf('/') + 1)

        }

        mPathsegments = data.pathSegments

        var cursor: Cursor? = null
        try {
            cursor = contentResolver.query(data, null, null, null, null)

            if (cursor != null && cursor.moveToFirst()) {
                var columnIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)

                if (columnIndex != -1) {
                    val displayName = cursor.getString(columnIndex)
                    if (displayName != null)
                        possibleName = displayName
                }
                columnIndex = cursor.getColumnIndex("mime_type")
                if (columnIndex != -1) {
                    log("Mime type: " + cursor.getString(columnIndex))
                }
            }
        } catch (se: SecurityException) {
            log("Importing failed: ${se.localizedMessage}")
        } finally {
            cursor?.close()
        }
        if (possibleName != null) {
            possibleName = possibleName.replace(".ovpn", "")
            possibleName = possibleName.replace(".conf", "")
        }

        lifecycleScope.launch {
            startImportTask(data, possibleName, "")
        }
    }

    private suspend fun startImportTask(data: Uri, possibleName: String?, inlineData: String) {
        val mProgress: ProgressBar
        withContext(Dispatchers.Main)
        {
            mProgress = ProgressBar(this@ConfigConverter)
            addViewToLog(mProgress)
        }
        var errorCode = 0
        withContext(Dispatchers.IO)
        {

            try {
                val inputStream: InputStream?
                if (data.scheme.equals("inline")) {
                    inputStream = inlineData.byteInputStream()
                } else {
                    inputStream = contentResolver.openInputStream(data)
                }

                if (inputStream != null) {
                    doImport(inputStream)
                }
                if (mResult == null)
                    errorCode = -3
            } catch (se: IOException) {
                log(R.string.import_content_resolve_error.toString() + ":" + se.localizedMessage)
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
                    checkMarschmallowFileImportError(data)
                errorCode = -2
            } catch (se: SecurityException) {
                log(R.string.import_content_resolve_error.toString() + ":" + se.localizedMessage)
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
                    checkMarschmallowFileImportError(data)
                errorCode = -2
            }
        }
        withContext(Dispatchers.Main)
        {
            mLogLayout.removeView(mProgress)
            addMissingFileDialogs()
            updateFileSelectDialogs()

            if (errorCode == 0) {
                displayWarnings()
                val result = mResult!!
                result.mName = getUniqueProfileName(possibleName)
                mProfilename.visibility = View.VISIBLE
                mProfilenameLabel.visibility = View.VISIBLE
                mProfilename.setText(result.name)

                mCompatmode.visibility = View.VISIBLE
                mCompatmodeLabel.visibility = View.VISIBLE
                mCompatmode.setSelection(Utils.mapCompatVer(result.mCompatMode))

                mTLSProfile.visibility = View.VISIBLE
                mTLSProfileLabel.visibility = View.VISIBLE
                mTLSProfile.setSelection(translateTLSProfileToSelection(result.mTlSCertProfile))

                log(R.string.import_done)
            }
        }
    }


    @TargetApi(Build.VERSION_CODES.M)
    private fun checkMarschmallowFileImportError(data: Uri?) {
        // Permission already granted, not the source of the error
        if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
            return

        // We got a file:/// URL and have no permission to read it. Technically an error of the calling app since
        // it makes an assumption about other apps being able to read the url but well ...
        if (data != null && "file" == data.scheme) {
            log("An external app instructed OpenVPN for Android to open a file:// URI. This kind of URI have been deprecated since Android 7 and no longer work on modern Android versions at all.")
            doRequestSDCardPermission(PERMISSION_REQUEST_READ_URL)
        }

    }


    override fun onStart() {
        super.onStart()
    }

    private fun log(logmessage: String?) {
        runOnUiThread {
            val tv = TextView(this@ConfigConverter)
            mLogEntries.add(logmessage)
            tv.text = logmessage

            addViewToLog(tv)
        }
    }

    private fun addViewToLog(view: View?) {
        mLogLayout.addView(view, mLogLayout.childCount - 1)
    }

    private fun doImport(inputStream: InputStream) {
        val cp = ConfigParser()
        try {
            val isr = InputStreamReader(inputStream)

            cp.parseConfig(isr)
            mResult = cp.convertProfile()
            embedFiles(cp)
            return

        } catch (e: IOException) {
            log(R.string.error_reading_config_file)
            log(e.localizedMessage)
        } catch (e: ConfigParseError) {
            log(R.string.error_reading_config_file)
            log(e.localizedMessage)
        } finally {
            inputStream.close()
        }

        mResult = null
    }


    private fun displayWarnings() {
        if (mResult!!.mUseCustomConfig) {
            log(R.string.import_warning_custom_options)
            var copt = mResult!!.mCustomConfigOptions
            if (copt.startsWith("#")) {
                val until = copt.indexOf('\n')
                copt = copt.substring(until + 1)
            }

            log(copt)
        }

        if (mResult!!.mAuthenticationType == VpnProfile.TYPE_KEYSTORE || mResult!!.mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE) {
            findViewById<View>(R.id.importpkcs12).visibility = View.VISIBLE
        }

    }

    private fun log(ressourceId: Int, vararg formatArgs: Any) {
        log(getString(ressourceId, *formatArgs))
    }

    companion object {

        @kotlin.jvm.JvmField
        val IMPORT_PROFILE = "de.blinkt.openvpn.IMPORT_PROFILE"
        val IMPORT_PROFILE_DATA = "de.blinkt.openvpn.IMPORT_PROFILE_DATA"
        private val RESULT_INSTALLPKCS12 = 7
        private val CHOOSE_FILE_OFFSET = 1000
        val VPNPROFILE = "vpnProfile"
        private val PERMISSION_REQUEST_EMBED_FILES = 37231
        private val PERMISSION_REQUEST_READ_URL = PERMISSION_REQUEST_EMBED_FILES + 1
    }

}
