package connections.usb

import java.io.{File, IOException}

import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener
import com.android.ddmlib.{AndroidDebugBridge, IDevice}
import slide.{Const, FileManager, SystemInfo}
import enums.OperatingSystem

object Adb {

    var usbAvailable: Boolean = false

    var isAdbInstalled: Boolean = false
    var adbFilePath: String = ""
    /** Make's sure init only gets called once */
    private var called: Boolean = false

    /**
     * Initializes Adb
     * @param fileManager File manager to download needed libraries.
     */
    def init(fileManager: FileManager): Unit = {
        if (!called) {
            SystemInfo.operatingSystem match {
                case OperatingSystem.WINDOWS =>
                    adbFilePath = Const.ADB + SystemInfo.systemExtension
                    if (!isAdbAvailable) { // ADB is unavailable
                        fileManager.downloadFile(Const.MAINT_BASE + "adb/win/adb.exe", "adb.exe")
                        fileManager.downloadFile(Const.MAINT_BASE + "adb/win/AdbWinApi.dll", "AdbWinApi.dll")
                        fileManager.downloadFile(Const.MAINT_BASE + "adb/win/AdbWinUsbApi.dll", "AdbWinUsbApi.dll")
                    }
                case _ => adbFilePath = "/usr/bin/adb"
            }
            called = true
        } else {
            throw new Exception("init() can not be called more than once.")
        }

        AndroidDebugBridge.init(false)
        try {
            val debugBridge: AndroidDebugBridge = AndroidDebugBridge.createBridge(adbFilePath, true)
            isAdbInstalled = debugBridge.getDevices.nonEmpty
        } catch {
            case e: Exception =>
        }

        if (isAdbAvailable) {
            AndroidDebugBridge.addDeviceChangeListener(new IDeviceChangeListener {
                override def deviceChanged(device: IDevice, arg: Int) {}

                override def deviceConnected(device: IDevice) {
                    usbAvailable = true
                }

                override def deviceDisconnected(device: IDevice) {
                    usbAvailable = false
                }
            })
        }
    }

    def isAdbAvailable: Boolean = isAdbInstalled || isAdbFilePresent

    def isAdbFilePresent: Boolean = new File(adbFilePath).exists()

    @throws(classOf[IOException])
    private def executeAdbProcess(process: ProcessBuilder): Process = {
        if (isAdbInstalled) {
            process.start()
        } else if (isAdbFilePresent) {
            if (SystemInfo.chmod != "") {
                new ProcessBuilder("chmod", SystemInfo.chmod, adbFilePath).start
            }

            try {
                process.start()
            } catch {
                case e: Exception =>
                    println("ADB is unavailable")
                    null
            }
        } else {
            null
        }
    }

    /**
     * Forwards ADB ports.
     * @throws java.io.IOException If there is trouble accessing ADB.
     */
    def startAdb(): Process = {
        executeAdbProcess(new ProcessBuilder(adbFilePath, "forward", "tcp:" + Const.USB_PORT, "tcp:" + Const.USB_PORT))
    }

    def adbDevices(): Process = {
        executeAdbProcess(new ProcessBuilder(adbFilePath, "devices"))
    }

    def restartAdb(): Process = {
        executeAdbProcess(new ProcessBuilder(adbFilePath, "kill-server"))
        executeAdbProcess(new ProcessBuilder(adbFilePath, "start-server"))
    }
}