package com.duolebo.blyrobot.data

import android.content.Context
import android.os.Build
import android.os.Environment
import android.util.Log
import android.widget.Toast
import com.duolebo.appbase.AppBaseHandler
import com.duolebo.appbase.IAppBaseCallback
import com.duolebo.appbase.IProtocol
import com.duolebo.blyrobot.protocol.ApkReportProtocol
import com.duolebo.blyrobot.util.AdbUtil
import com.duolebo.blyrobot.util.AppUtil
import com.duolebo.blyrobot.util.Config
import net.gotev.uploadservice.*
import net.gotev.uploadservice.ftp.FTPUploadRequest
import net.gotev.uploadservice.ftp.UnixPermissions
import org.json.JSONArray
import org.json.JSONObject
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.ArrayList


class Task : IAppBaseCallback {

    private val TAG = "Task"

    val STATUS_MAP = mapOf(
            "0" to "未处理",
            "1" to "进行中",
            "2" to "中断",
            "3" to "图片上传中",
            "4" to "上报失败",
            "5" to "处理完成")

    lateinit var apkInfo: ApkInfo
    // 截图存放路径
    private lateinit var imagePath: String
    // 抓包存放路径
    private var capturePath = Environment
            .getExternalStorageDirectory().absolutePath + "/capture.txt"

    private var uploadImages = ArrayList<String>()
    private var uploadId = ""

    private var channelIndex = 0
    private var status = "0"


    private var reportProtocol: ApkReportProtocol
    private var dataHandler:AppBaseHandler
    private lateinit var reportJson: JSONObject

    private var context: Context
    private val imageDateFormat = SimpleDateFormat("yyyy_MM_dd_HH_mm_ss", Locale.CHINA)
    var proc: Process? = null

    constructor(context: Context) {
        this.context = context
        this.reportProtocol = ApkReportProtocol(context, Config.instance)
        this.dataHandler = AppBaseHandler(this)
    }

    fun from(apkInfo: ApkInfo) {
        this.apkInfo = apkInfo
//        this.imagePath = this.context.cacheDir.absolutePath + "/" + apkInfo.packageName
        this.imagePath = Environment
                .getExternalStorageDirectory().absolutePath + "/upload/" + apkInfo.packageName
        val dir = File(this.imagePath)
        if (!dir.exists())
            dir.mkdirs()
    }

    // 准备上传报告信息
    private fun prepareReport() {
        this.reportJson = JSONObject()
        reportJson.putOpt(ApkInfo.NAME, this.apkInfo.name)
        reportJson.putOpt(ApkInfo.PACKAGE_NAME, this.apkInfo.packageName)
        reportJson.putOpt("device", Build.BRAND + "|" + Build.HARDWARE + "|" + Build.VERSION.RELEASE)
        reportJson.putOpt("channels", JSONArray())
    }

    fun start() {
        this.status = "1"
        this.prepareReport()
        // 启动应用
        this.launchApp()
        // 频道轮询处理
        try {
            this.processChannels()
            // 上传图片
            this.uploadImage()
            this.status = "3"
        } catch (e: java.lang.Exception) {
            this.status = "2"
            e.printStackTrace()
        }

        // 退出应用
        AdbUtil.stopApp(this.apkInfo.packageName)
    }

    fun reset() {
        this.status = "0"
        this.channelIndex = 0
        this.uploadImages.clear()

        val dir = File(this.imagePath)
        if (dir.exists()) {
            for (file in dir.listFiles())
                file.delete()
        }
    }

    fun destroy() {
        // 杀掉tcpdump进程
        AdbUtil.killTcpdump()

        // 退出应用
        AdbUtil.stopApp(this.apkInfo.packageName)

        // 取消ftp上传
        if (!uploadId.isNullOrEmpty())
            UploadService.stopUpload(uploadId)
    }

    private fun finish(result: Boolean) {
        this.taskListener?.run {
            onComplete(result)
        }
    }

    private fun launchApp(reset: Boolean = false) {
        Log.i(TAG, "launchApp")
        if (reset) {
            try {
                AdbUtil.resetApp(this.apkInfo.packageName)
            } catch (e: java.lang.Exception) {
                e.printStackTrace()
            }
        }

        AdbUtil.launchApp("${this.apkInfo.packageName}/${this.apkInfo.launcher}")
        Thread.sleep(2 * 1000L)
        // 启动2s开始抓取，避免第一个频道没有抓到
        capture(this.apkInfo.launchDelay + this.apkInfo.captureDelay)
        Thread.sleep(this.apkInfo.launchDelay * 1000L)
    }

    private fun processChannels() {
        Log.i(TAG, "processChannels")
        step()
        for (i in 1 until this.apkInfo.channelCount) {
            // 模拟按键事件. 切换频道进行抓取
            this.channelIndex = i
            capture(apkInfo.captureDelay)
            AdbUtil.sendMultiKey(this.apkInfo.channelKeyEvent)
            step()
        }
    }

    private fun step() {
        // 截图保存
        val screenShots = saveScreenShot()
        if (screenShots.size > 0)
            uploadImages.addAll(screenShots)
        val playUrlItems = analysisCapture()

        val channels = this.reportJson.optJSONArray("channels")
        val channelJson = JSONObject()
        channelJson.putOpt("channelImage", screenShots[0])
        channelJson.putOpt("channelEpgImage", screenShots[1])
        channelJson.putOpt("channelId", this.channelIndex)

        val playUrls = JSONArray()
        for (playUrl in playUrlItems) {
            playUrls.put(playUrl.url)
        }

        channelJson.putOpt("playUrls", playUrls)

        channels.put(channelJson)
    }

    // 抓包处理
    private fun capture(delay: Int) {
        Log.i(TAG, "capture")
        val file = File(this.capturePath)
        if (file.exists())
            file.delete()
        Thread(Runnable {
            proc = AdbUtil.tcpCapture(this.capturePath)
        }).start()
        Log.i(TAG, "capture sleep $delay seconds")
        Thread.sleep(delay * 1000L)
        proc?.destroy()
        AdbUtil.killTcpdump()
        Thread.sleep(2000L)
    }

    // 分析http抓包文件
    private fun analysisCapture(): ArrayList<PlayInfo> {
        val playUrlItems = ArrayList<PlayInfo>()
        val file = File(this.capturePath)
        if (!file.exists())
            return playUrlItems

        val lines = file.readLines()

        var partUrl = ""
        var timeStr = ""
        val tagGet = "GET /"
        lines.forEach {
            if (it.contains("IP (")) {
                val end = it.indexOf('.')
                timeStr = it.substring(0, end)
            }

            if (it.contains(tagGet)) {
                val start = it.indexOf(tagGet) + tagGet.length - 1
                val end = it.lastIndexOf(' ')
                partUrl = it.substring(start, end)
            }

            if (it.contains("Host")) {
                val start = it.indexOf(' ') + 1
                val host = it.substring(start)
                val url = "http://$host$partUrl"

                var add = true
                // 如果筛选url不为空，这里需要进行过滤
                if (!this.apkInfo.filterUrl.isNullOrEmpty()) {
                    if (url.contains(this.apkInfo.filterUrl))
                        add = false
                }

                if (add) {
                    // 这里进行媒体视频播放地址识别
                    val extArr = this.apkInfo.mediaExt.split("|")
                    for (ext in extArr) {
                        if (url.contains(ext)) {
                            add = true
                            break
                        }
                    }
                }

                if (add) {
                    val item = PlayInfo()
                    item.url = url
                    item.time = timeStr

                    playUrlItems.add(item)
                }

                partUrl = ""
            }
        }

        return playUrlItems
    }

    // 截取一张频道图+一张显示频道节目的图
    private fun saveScreenShot(): ArrayList<String> {
        val screenImages = ArrayList<String>()

        var time = imageDateFormat.format(Date())
        val absName = this.imagePath + "/${this.apkInfo.packageName}_${channelIndex}_$time"
        screenShot(absName)
        screenImages.add("$absName.jpg")

        AdbUtil.sendMultiKey(this.apkInfo.menuKeyEvent)
        time = imageDateFormat.format(Date())
        val absOkName = this.imagePath + "/${this.apkInfo.packageName}_${channelIndex}_${time}_ok"
        screenShot(absOkName)
        screenImages.add("$absOkName.jpg")

        return screenImages
    }

    // 截图处理，转成jpg
    private fun screenShot(absName : String) {
        val pngPath = "$absName.png"
        val jpgPath = pngPath.replace(".png", ".jpg")
        AdbUtil.screenShot(pngPath)
        // png转换成jpg
        AppUtil.pngToJpg(pngPath, jpgPath)
    }

    private fun uploadComplete(errorCount: Int) {
        report()
    }

    private fun uploadImageCallback(): UploadStatusDelegate {
        val totalUpload = this.uploadImages.size
        Log.i(TAG, "total upload count : $totalUpload")
        var uploadCount = 0

        return object: UploadStatusDelegate {
            override fun onCancelled(context: Context?, uploadInfo: UploadInfo?) {
            }

            override fun onProgress(context: Context?, uploadInfo: UploadInfo?) {
            }

            override fun onError(context: Context?, uploadInfo: UploadInfo?, serverResponse: ServerResponse?, exception: java.lang.Exception?) {
                Log.i(TAG, "upload error uploadCount: $uploadCount " + uploadInfo?.toString())
                uploadCount++
                if (uploadCount == totalUpload - 1) {
                    uploadComplete(uploadCount - uploadInfo?.successfullyUploadedFiles!!.size)
                }
            }

            override fun onCompleted(context: Context?, uploadInfo: UploadInfo?, serverResponse: ServerResponse?) {
                Log.i(TAG, "onCompleted...uploadCount: $uploadCount")
                uploadCount++
                if (uploadCount == totalUpload - 1) {
                    uploadComplete(uploadCount - uploadInfo?.successfullyUploadedFiles!!.size)
                }
            }
        }
    }

    private fun uploadImage() {

        try {
            val uploadRequest = FTPUploadRequest(context, Config.instance.getFtpServer(), 21)
                    .setUsernameAndPassword(Config.instance.getFtpUserName(), Config.instance.getFtpPassword())
                    .setNotificationConfig(UploadNotificationConfig())
                    .setCreatedDirectoriesPermissions(UnixPermissions("777"))
                    .setSocketTimeout(5000)
                    .setConnectTimeout(5000)
                    .setDelegate(uploadImageCallback())
                    .setMaxRetries(4)

            this.uploadImages.forEach {
                val uploadFile = File(it)
                if (uploadFile.exists()) {
                    uploadRequest.addFileToUpload(it, Config.instance.getFtpRemotePath() + "/" + uploadFile.name)
                }
            }

            this.uploadId = uploadRequest.startUpload()
            Log.i(TAG, "upload id $uploadId")
        } catch (exc: Exception) {
            Log.e(TAG, exc.message, exc)
        }
    }

    private fun report() {
        reportProtocol.withBody(reportJson.toString()).execute(dataHandler)
    }

    override fun onProtocolFailed(p0: IProtocol?) {
        this.status = "4"
        finish(false)
    }

    override fun onHttpFailed(p0: IProtocol?) {
        this.status = "4"
        finish(false)
    }

    override fun onProtocolSucceed(p0: IProtocol?) {
        this.status = "5"
        finish(true)
    }


    var taskListener: OnTaskListener? = null

    interface OnTaskListener {
        fun onComplete(result: Boolean)
    }

    class PlayInfo {
        var time = ""
        var url = ""
    }

}