#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# (c) B.Kerler 2020 under MIT license
# If you use my code, make sure you refer to my name
# If you want to use in a commercial product, ask me before integrating it
import time
import serial
import serial.tools.list_ports
import argparse
import requests
from telnetlib import Telnet
import usb.core
from enum import Enum

import os, sys, inspect
current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
sys.path.insert(0, current_dir)

try:
    from edlclient.Tools.qc_diag import qcdiag
except ImportError as e:
    print(current_dir)
    from qc_diag import qcdiag
    pass

try:
    from edlclient.Library.utils import LogBase
except ImportError as e:
    from Library.utils import LogBase


class vendor(Enum):
    sierra = 0x1199
    quectel = 0x2c7c
    zte = 0x19d2
    netgear = 0x0846
    telit = 0x413c

class deviceclass:
    vid=0
    pid=0
    def __init__(self,vid,pid):
        self.vid=vid
        self.pid=pid


class connection(metaclass=LogBase):
    def __init__(self, port=""):
        self.serial = None
        self.tn = None
        self.connected = False
        if port == "":
            port = self.detect(port)
            if port == "":
                try:
                    self.tn = Telnet("192.168.1.1", 5510, 5)
                    self.connected = True
                except:
                    self.connected = False
        if port != "":
            self.serial = serial.Serial(port=port, baudrate=115200, bytesize=8, parity='N', stopbits=1, timeout=1)
            self.connected = self.serial.is_open

    def waitforusb(self,vid,pid):
        timeout = 0
        while timeout < 10:
            for device in self.detectusbdevices():
                if device.vid == vid:
                    if device.pid == pid:
                        return True
            time.sleep(1)
            timeout += 1
        return False

    def websend(self,url):
        headers = {'Referer': 'http://192.168.0.1/index.html', 'Accept-Charset': 'UTF-8'}
        r = requests.get(url, headers=headers)
        if b"FACTORY:ok" in r.content or b"success" in r.content:
            print(f"Detected a ZTE in web mode .... switching mode success (convert back by sending \"AT+ZCDRUN=F\" via AT port)")
            return self.waitforusb(vendor.zte.value,0x0016)
        return False

    def getserialports(self):
        return [port for port in serial.tools.list_ports.comports()]

    def detectusbdevices(self):
        dev = usb.core.find(find_all=True)
        ids=[deviceclass(cfg.idVendor,cfg.idProduct) for cfg in dev]
        return ids

    def detect(self, port):
        vendortable={
            0x1199:["Sierra Wireless",3],
            0x2c7c:["Quectel",3],
            0x19d2:["ZTE",2],
            0x0846:["Netgear", 2],
            0x413c:["Telit",0]
        }
        mode="Unknown"
        for device in self.detectusbdevices():
            if device.vid==vendor.zte.value:
                if device.pid==0x0016:
                    print(f"Detected a {vendortable[device.vid][0]} device with pid {hex(device.pid)} in Diag mode")
                    mode="AT"
                    break
                elif device.pid==0x1403:
                    print(f"Detected a {vendortable[device.vid][0]} device with pid {hex(device.pid)} in Web mode")
                    mode="Web"
                    # url = 'http://192.168.0.1/goform/goform_set_cmd_process?goformId=USB_MODE_SWITCH&usb_mode=1' #adb
                    url = 'http://192.168.0.1/goform/goform_process?goformId=MODE_SWITCH&switchCmd=FACTORY'
                    if self.websend(url):
                        mode="AT"
                        break
            elif device.vid==vendor.telit.value:
                if device.pid==0x81d7:
                    print(f"Detected a {vendortable[device.vid][0]} device with pid {hex(device.pid)} in Diag mode")
                    print("Sending download mode command")
                    interface = 5
                    diag = qcdiag(loglevel=self.__logger.level, portconfig=[[0x413c, 0x81d7, interface]])
                    if diag.connect():
                        data=diag.hdlc.receive_reply()
                        res = diag.send(b"\x4b\x65\x01\x00")
                        if res[0]==0x4B:
                            print("Sending download mode succeeded")
                        diag.disconnect()
                    break
        if mode=="AT" or mode=="Unknown":
            for port in self.getserialports():
                if port.vid in vendortable:
                    portid = port.location[-1:]
                    if int(portid) == vendortable[port.vid][1]:
                        print(f"Detected a {vendortable[port.vid][0]} at interface at: " + port.device)
                        return port.device
        return ""

    def readreply(self):
        info = []
        timeout=0
        if self.serial is not None:
            while True:
                tmp = self.serial.readline().decode('utf-8').replace('\r', '').replace('\n', '')
                if "OK" in tmp:
                    info.append(tmp)
                    return info
                elif "ERROR" in tmp:
                    return -1
                if tmp!="":
                    info.append(tmp)
                else:
                    timeout+=1
                    if timeout==20:
                        break
        return info

    def send(self, cmd):
        if self.tn is not None:
            self.tn.write(bytes(cmd + "\r", 'utf-8'))
            time.sleep(0.05)
            data = ""
            while True:
                tmp = self.tn.read_eager()
                if tmp != b"":
                    data += tmp.strip().decode('utf-8')
                else:
                    break
            return data.split("\r\n")
        elif self.serial is not None:
            self.serial.write(bytes(cmd + "\r", 'utf-8'))
            time.sleep(0.05)
            return self.readreply()

    def close(self):
        if self.tn is not None:
            self.tn.close()
            self.connected = False
        if self.serial is not None:
            self.serial.close()
            self.connected = False

    def ati(self):
        data={}
        info = self.send("ATI")
        if info != -1:
            for line in info:
                if "Revision" in line:
                    data["revision"] = line.split(":")[1].strip()
                if "Model" in line:
                    data["model"] = line.split(":")[1].strip()
                if "Quectel" in line:
                    data["vendor"] = "Quectel"
                if "Manufacturer" in line:
                    data["manufacturer"]=line.split(":")[1].strip()
                    if "Sierra Wireless" in data["manufacturer"]:
                        data["vendor"]="Sierra Wireless"
                    elif "ZTE CORPORATION" in data["manufacturer"]:
                        data["vendor"]="ZTE"
                    elif "Netgear" in data["manufacturer"]:
                        data["vendor"]="Netgear"
                    elif "Telit" in data["manufacturer"]:
                        data["vendor"]="Telit"
        return data

class dwnloadtools(metaclass=LogBase):
    def sendcmd(self, tn,cmd):
        tn.write(bytes(cmd,'utf-8')+b"\n")
        time.sleep(0.05)
        return tn.read_eager().strip().decode('utf-8')

    def run(self, args):
        port = args.port
        cn = connection(port)
        if cn.connected:
            info=cn.ati()
            if "vendor" in info:
                if info["vendor"]=="Sierra Wireless" or info["vendor"]=="Netgear":
                    print("Sending download mode command")
                    print(cn.send("AT!BOOTHOLD\r"))
                    print(cn.send('AT!QPSTDLOAD\r'))
                    print("Done switching to download mode")
                elif info["vendor"]=="Quectel":
                    print("Sending download mode command")
                    interface=0
                    diag = qcdiag(loglevel=self.__logger.level, portconfig=[[0x2c7c,0x0125,interface]])
                    if diag.connect():
                        diag.hdlc.receive_reply()
                        res = diag.send(b"\x4b\x65\x01\x00")
                        diag.disconnect()
                        print("Done switching to download mode")
                elif info["vendor"]=="Telit":
                    print("Sending download mode command")
                    interface=0
                    diag = qcdiag(loglevel=self.__logger.level, portconfig=[[0x2c7c,0x0125,interface]])
                    if diag.connect():
                        diag.hdlc.receive_reply()
                        res = diag.send(b"\x4b\x65\x01\x00")
                        diag.disconnect()
                        print("Done switching to download mode")
                elif info["vendor"]=="ZTE":
                    print("Sending download mode command")
                    interface=0
                    diag = qcdiag(loglevel=self.__logger.level, portconfig=[[0x19d2,0x0016, interface]])
                    if diag.connect():
                        diag.hdlc.receive_reply()
                        res = diag.send(b"\x4b\x65\x01\x00")
                        if res[0]==0x4B:
                            print("Done switching to ENANDPRG mode")
                        else:
                            res = diag.send(b"\x3a")
                            if res[0]==0x3A:
                                while True:
                                    state=cn.waitforusb(vendor.zte.value,0x0076)
                                    if not state:
                                        diag.disconnect()
                                        if diag.connect():
                                            res = diag.send(b"\x3a")
                                    else:
                                        break
                                if state:
                                    print("Done switching to NANDPRG mode")
                                else:
                                    print("Failed switching to download mode")
                        diag.disconnect()
        cn.close()

def main():
    version = "1.1"
    info = 'Modem Gimme-EDL ' + version + ' (c) B. Kerler 2020-2021'
    parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,description=info)
    parser.add_argument(
        '-port', '-p',
        help='use com port for auto unlock',
        default="")
    parser.add_argument(
        '-logfile', '-l',
        help='use logfile for debug log',
        default="")
    args = parser.parse_args()
    dw=dwnloadtools()
    dw.run(args)

if __name__=="__main__":
    main()
