# -*- coding: utf-8 -*-
#  Copyright (C) 2016- BOUFFALO LAB (NANJING) CO., LTD.
#
#  Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to deal
#  in the Software without restriction, including without limitation the rights
#  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#  copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in all
#  copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#  SOFTWARE.

import os
import sys
import serial
import hashlib
from binascii import *
from libs import bflb_utils

try:
    from serial.tools.list_ports import comports
except ImportError:
    raise exception.GetSerialPortsError(os.name)


class FileSerial(object):
    @staticmethod
    def int_to_hex(data):
        hex_size = hex(data).replace("0x", "0x00")[-4:]
        low_hex_size = hex_size[-2:]
        hight_hex_size = hex_size[:-2]
        return low_hex_size, hight_hex_size

    @staticmethod
    def str_to_hex(data):
        message = hexlify(data)
        new_message = ""
        for i in range(0, len(message), 2):
            new_message += message[i : i + 2].decode() + " "
        return new_message

    @staticmethod
    def get_file_hash(file_path):
        with open(file_path, "rb") as f:
            message = f.read()
        data_sha = hashlib.sha256()
        data_sha.update(message)
        return data_sha.hexdigest()

    def open_listen(self, dev_com, baudrate, file_path, chunk_size=4096, timeout=1):
        sdio_file_ser_dict = {}
        file_dict = {}

        if sys.platform.startswith("win"):
            for p, d, h in comports():
                if not p:
                    continue
                if "PID=1D6B" in h.upper():
                    ser_value = h.split(" ")[2][4:]
                    if ser_value not in sdio_file_ser_dict:
                        sdio_file_ser_dict[ser_value] = p
                    else:
                        if "LOCATION" in h.upper():
                            file_dict[sdio_file_ser_dict[ser_value]] = p
                        else:
                            file_dict[p] = sdio_file_ser_dict[ser_value]
        elif sys.platform.startswith("linux") or sys.platform.startswith("darwin"):
            for p, d, h in comports():
                if not p:
                    continue
                if "PID=1D6B" in h.upper():
                    ser_value = h.split(" ")[2][4:]
                    if ser_value not in sdio_file_ser_dict:
                        sdio_file_ser_dict[ser_value] = p
                    else:
                        if sdio_file_ser_dict[ser_value] > p:
                            file_dict[p] = sdio_file_ser_dict[ser_value]
                        else:
                            file_dict[sdio_file_ser_dict[ser_value]] = p

        if " (" in dev_com:
            sdio_dev = dev_com[: dev_com.find(" (")]
        else:
            sdio_dev = dev_com
        file_com = file_dict[sdio_dev]
        print(file_com)
        _ser = serial.Serial(
            file_com,
            int(baudrate),
            timeout=5.0,
            xonxoff=False,
            rtscts=False,
            write_timeout=None,
            dsrdtr=False,
        )
        _ser.timeout = timeout
        size = os.path.getsize(file_path)
        hex_size = hex(size).replace("0x", "0x0000000")
        first_message = bytes.fromhex(
            "F0 00 04 00 {} {} {} {}".format(hex_size[-2:], hex_size[-4:-2], hex_size[-6:-4], hex_size[-8:-6])
        )
        _ser.write(first_message)
        recv_message = _ser.read(2)

        if recv_message != b"OK":
            _ser.close()
            bflb_utils.printf("Send Failed! Error 1: First send failed")
            return False

        with open(file_path, "rb") as f:
            while True:
                message = f.read(chunk_size)
                if message:
                    len_mess = len(message)
                    low_middle_size, hight_middle_size = self.int_to_hex(len_mess)
                    if recv_message == b"OK":
                        recv_message = b""
                        new_message = self.str_to_hex(message)
                        middle_message = bytes.fromhex(
                            "F1 00 {} {} {}".format(low_middle_size, hight_middle_size, new_message)
                        )
                        _ser.write(middle_message)
                        recv_message = _ser.read(2)
                        message = ""
                    else:
                        _ser.close()
                        bflb_utils.printf("Send Failed! Error 2: File send failed")
                        return False
                else:
                    break
        recv_message = b""
        end_message = bytes.fromhex("F2 00 00 00")
        _ser.write(end_message)

        recv_message = _ser.read(66)
        if b"OK" in recv_message:
            file_sha256 = self.get_file_hash(file_path)
            if recv_message[2:].hex() == file_sha256:
                _ser.write(b"check hash")
                _ser.close()
                return True
            else:
                _ser.close()
                bflb_utils.printf("Send Failed! Error 4: Hash check failed")
                return False
        else:
            _ser.close()
            bflb_utils.printf("Send Failed! Error 3: Hash return failed")
            return False


if __name__ == "__main__":
    fs = FileSerial()
    fs.open_listen(
        "/dev/ttyACM1",
        2000000,
        "/home/tanjiaxi/git/bouffalo_dev_cube/chips/bl602/img_create_mcu/img_if.bin",
    )
