package com.kdanmobile.reader.copyfile

import android.content.Context
import android.content.Intent
import android.net.Uri
import com.kdanmobile.reader.utils.UriToPathUtil
import java.io.File
import java.net.URLEncoder

/**
 * When PDF Reader opens a file which is not in the 'KdanPDFReader' folder,
 * the target file would be copied into that folder.
 *
 * In order to determine the file is in the 'KdanPDFReader' folder or not,
 * we need to know its absolute path.
 *
 * If a user launches PDF Reader by a third-party application,
 * that application would send PDF Reader a uri packaged in intent.
 *
 * The uri is generated by the content provider of that application,
 * and only its content resolver knows how to resolve it.
 *
 * The uri may be encoded or even may look like a random string,
 * and there is no common specification among different applications.
 *
 * We use the function CopyFileInfoFactory::getFilePathFromUri (actually UriToPathUtil::getPath in most case) to get absolute file path through uri.
 *
 * However, sometimes the UriToPathUtil::getPath function may not work well (due to the wrong implementation of content provider of third-party applications),
 *
 * In that case, we would try to use a workaround to fix it.
 *
 * The workaround: we try to find whether a file name '.../KdanPDFReader/${filename of decoded uri}' or '.../KdanPDFReader/${filename of raw uri} exists of not,
 *                 and we consider the file as the target if the file exists.
 *
 * If finally the workaround still fails, we cannot get the correct file path.
 *
 * We always copy files when we do not know those files are in the 'KdanPDFReader' folder or not.
 *
 * Note: uri.path / uri.pathSegments / uri.lastPathSegment /... is automatically decoded even if the raw uri is not encoded,
 * and it would cause some problems if the raw file path contains '%' characters.
 *
 * To avoid those problems, we need to additionally encode each segment of the raw uri in the above case.
 */

class CopyFileInfoFactory {
    fun createInfo(applicationContext: Context, intent: Intent, kdanPdfReaderFolder: File): CopyFileInfo {
        val shouldCopyFile = intent.getBooleanExtra(CopyFileActivity.KEY_SHOULD_COPY_FILE, true)
        val isActionView = intent.action == Intent.ACTION_VIEW
        val srcFilePath = if (intent.action == Intent.ACTION_VIEW) {
            getFilePathFromUri(applicationContext, intent.data, kdanPdfReaderFolder)
        } else {
            intent.getStringExtra(CopyFileActivity.KEY_FILE_ABSOLUTE_PATH)
        }
        val uri = getUriFromIntent(applicationContext, intent)
        return CopyFileInfo(shouldCopyFile, isActionView, srcFilePath, uri)
    }

    private fun getFilePathFromUri(applicationContext: Context, uri: Uri?, kdanPdfReaderFolder: File): String? {
        if (null == uri) return null
        var filePath = UriToPathUtil.getInstance().getPath(applicationContext, uri)
        //  workaround
        if (filePath.isNullOrEmpty()) {
            val folderName = if (kdanPdfReaderFolder.name.last() == File.separatorChar) kdanPdfReaderFolder.name else (kdanPdfReaderFolder.name + File.separatorChar)

            fun fixFilePath(path: String) {
                val index = path.indexOf(folderName)
                if (index != -1) {
                    val filename = path.substring(index + folderName.length)
                    val file = File("${kdanPdfReaderFolder.absolutePath}/$filename")
                    if (file.exists()) {
                        filePath = file.absolutePath
                    }
                }
            }

            uri.path?.also {
                fixFilePath(it)
            }
            if (null == filePath) {
                fixFilePath(uri.toString())
            }
        }
        if (null != filePath && !File(filePath).exists()) {
            filePath = Uri.decode(filePath)
        }
        return if (null != filePath && File(filePath).exists()) filePath else null
    }

    private fun getUriFromIntent(applicationContext: Context, intent: Intent): Uri? {
        val uri = intent.data ?: return null
        return if (isFileExist(applicationContext, uri)) {
            uri
        } else {
            val encodeChar = URLEncoder.encode("%", "UTF-8")
            val fileUri = Uri.parse(uri.toString().replace("%", encodeChar))
            if (isFileExist(applicationContext, fileUri)) {
                fileUri
            } else {
                uri
            }
        }
    }

    private fun isFileExist(applicationContext: Context, fileUri: Uri): Boolean {
        return try {
            val assetFileDescriptor = applicationContext.contentResolver.openAssetFileDescriptor(fileUri, "r")
            assetFileDescriptor?.close()
            true
        } catch (e: Exception) {
            false
        }
    }
}