package com.kdanmobile.reader.thumb

import android.content.Context
import android.net.Uri
import android.support.v4.provider.DocumentFile
import com.kdanmobile.reader.utils.CloseUtils
import java.io.*
import kotlin.collections.ArrayList

object FileUtil {
    private var readerFolder: File? = null
    private const val EXTENSION_SEPARATOR = '.'
    private const val UNIX_SEPARATOR = '/'
    private const val NOT_FOUND = -1
    private const val WINDOWS_SEPARATOR = '\\'

    @Synchronized
    fun init(folder: File) {
        if (!folder.exists() || !folder.isDirectory) {
            folder.mkdirs()
        }
        readerFolder = folder
    }

    @Synchronized
    fun getKdanPDFReaderFolder(): File? {
        return readerFolder
    }

    fun generateSelectedPagesString(selectPage: ArrayList<Int>): String {
        val pages = StringBuffer()
        for (n in selectPage) {
            val position = n + 1
            pages.append(position).append(",")
        }
        pages.deleteCharAt(pages.length - 1)
        return pages.toString()
    }

    fun createExtractBlankFile(fileName: String, pages: String, folder: File?): File {
        val ext = getExtension(fileName)
        val name = removeExtension(fileName) + String.format("(page-%s)", pages)
        var file = File(folder, String.format("%s.%s", name, ext))
        var i = 2
        while (file.exists()) {
            file = File(folder, String.format("%s(%d).%s", name, i, ext))
            i++
        }
        return file
    }

    private fun generateUniqueFile(fileName: String): File {
        val name = removeExtension(fileName)
        val extension = getExtension(fileName)
        var file = File(readerFolder, "$name.$extension")
        var index = 2
        while (file.exists()) {
            file = File(readerFolder, "$name($index).$extension")
            index++
        }
        return file
    }

    enum class CopyMode {
        OVERWRITE, DO_NOTHING_IF_FILE_EXIST, DO_NOTHING, CREATE_NEW_FILE
    }

    fun saveFileToKdanPdfReaderFolder(context: Context, uri: Uri, mode: CopyMode = CopyMode.DO_NOTHING_IF_FILE_EXIST): String? {
        if (mode == CopyMode.DO_NOTHING) return null
        if (null == readerFolder) return null

        var dstFilePath: File? = null
        val documentFile = DocumentFile.fromSingleUri(context, uri)
        var inputStream: InputStream? = null
        var outputStream: FileOutputStream? = null
        try {
            val srcFilename = documentFile!!.name
            if (srcFilename.isNullOrEmpty()) return null
            dstFilePath = File(readerFolder, srcFilename)
            if (dstFilePath.exists()) {
                when (mode) {
                    CopyMode.DO_NOTHING_IF_FILE_EXIST -> return dstFilePath.absolutePath
                    CopyMode.CREATE_NEW_FILE -> dstFilePath = generateUniqueFile(srcFilename!!)
                    else -> {}
                }
            }

            inputStream = context.contentResolver.openInputStream(uri)
            if (null == inputStream) return null
            outputStream = FileOutputStream(dstFilePath)
            copyFile(inputStream, outputStream)
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            CloseUtils.closeIO(inputStream, outputStream)
        }
        return dstFilePath?.absolutePath
    }

    fun copyFileToKdanPdfReaderFolder(srcFilePath: String, mode: CopyMode = CopyMode.DO_NOTHING_IF_FILE_EXIST): String {
        if (mode == CopyMode.DO_NOTHING) return srcFilePath
        if (null == readerFolder) return srcFilePath
        if (srcFilePath.startsWith(readerFolder!!.absolutePath)) return srcFilePath

        val src = File(srcFilePath)
        val srcFilename = src.name
        var dstFilePath = File(readerFolder, srcFilename)
        if (dstFilePath.exists()) {
            when (mode) {
                CopyMode.DO_NOTHING_IF_FILE_EXIST -> return srcFilePath
                CopyMode.CREATE_NEW_FILE -> dstFilePath = generateUniqueFile(srcFilename)
                else -> {}
            }
        }
        var inputStream: FileInputStream? = null
        var outputStream: FileOutputStream? = null
        try {
            inputStream = FileInputStream(srcFilePath)
            outputStream = FileOutputStream(dstFilePath)
            copyFile(inputStream, outputStream)
        } catch (e: Exception) {

        } finally {
            CloseUtils.closeIO(inputStream, outputStream)
        }
        return dstFilePath.absolutePath
    }

    private fun copyFile(inputStream: InputStream, outputStream: OutputStream) {
        val buffer = ByteArray(1024)
        var read: Int
        while (true) {
            read = inputStream.read(buffer)
            if (read == -1) break
            outputStream.write(buffer, 0, read)
        }
    }

    private fun getExtension(filename: String?): String? {
        if (filename == null) {
            return null
        }
        val index = indexOfExtension(filename)
        return if (index == NOT_FOUND) {
            ""
        } else {
            filename.substring(index + 1)
        }
    }

    private fun indexOfExtension(filename: String?): Int {
        if (filename == null) {
            return NOT_FOUND
        }
        val extensionPos = filename.lastIndexOf(EXTENSION_SEPARATOR)
        val lastSeparator = indexOfLastSeparator(filename)
        return if (lastSeparator > extensionPos) NOT_FOUND else extensionPos
    }

    private fun indexOfLastSeparator(filename: String?): Int {
        if (filename == null) {
            return NOT_FOUND
        }
        val lastUnixPos = filename.lastIndexOf(UNIX_SEPARATOR)
        val lastWindowsPos = filename.lastIndexOf(WINDOWS_SEPARATOR)
        return Math.max(lastUnixPos, lastWindowsPos)
    }

    private fun removeExtension(filename: String?): String? {
        if (filename == null) {
            return null
        }
        failIfNullBytePresent(filename)

        val index = indexOfExtension(filename)
        return if (index == NOT_FOUND) {
            filename
        } else {
            filename.substring(0, index)
        }
    }

    private fun failIfNullBytePresent(path: String) {
        val len = path.length
        for (i in 0 until len) {
            if (path[i].toInt() == 0) {
                throw IllegalArgumentException("Null byte present in file/path name. There are no " + "known legitimate use cases for such data, but several injection attacks may use it")
            }
        }
    }
}