#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# 2020 - Marco Trevisan
#
# Initializer for ThinkPad's validity sensors 138a:0090 and 138a:0097 and 06cb:009a
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import argparse
import hashlib
import os
import shutil
import subprocess
import sys
import tempfile
import urllib.request

from usb import core as usb_core

from validitysensor.init_data_dir import PYTHON_VALIDITY_DATA_DIR
from validitysensor.firmware_tables import FIRMWARE_NAMES, FIRMWARE_URIS
from validitysensor.usb import SupportedDevices



def download_and_extract_fw(dev_type, fwdir, fwuri=None):
    fwuri = fwuri if fwuri else FIRMWARE_URIS[dev_type]['driver']
    expected_hash = FIRMWARE_URIS[dev_type]['sha512']
    fwarchive = os.path.join(fwdir, 'fwinstaller.exe')
    fwname = FIRMWARE_NAMES[dev_type]

    print('Downloading {} to extract {}'.format(fwuri, fwname))

    req = urllib.request.Request(fwuri)
    req.add_header('Referer', FIRMWARE_URIS[dev_type].get('referral', ''))
    req.add_header('User-Agent', 'Mozilla/5.0 (X11; U; Linux)')

    hash = hashlib.sha512()
    with urllib.request.urlopen(req) as response:
        with open(fwarchive, 'wb') as out_file:
            data = response.read()
            hash.update(data)
            out_file.write(data)

    actual_hash = hash.hexdigest()
    if actual_hash != expected_hash:
        raise Exception('Hash mismatch for driver download! Expected {}, got {}'.format(
            expected_hash, actual_hash))

    subprocess.check_call([
        'innoextract', '--output-dir', fwdir, '--include', fwname, '--collisions', 'overwrite',
        fwarchive
    ])

    fwpath = subprocess.check_output(['find', fwdir, '-name', fwname]).decode('utf-8').strip()
    print('Found firmware at {}'.format(fwpath))

    if not fwpath:
        raise Exception('No {} found in the archive'.format(fwname))

    return fwpath


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('--driver-uri')

    args = parser.parse_args()

    if os.geteuid() != 0:
        raise Exception('This script needs to be executed as root')

    dev_type = None
    for d in SupportedDevices:
        dev = usb_core.find(idVendor=d.value[0], idProduct=d.value[1])
        if dev:
            dev_type = d

    if not dev_type:
        raise Exception('No supported validity device found')

    try:
        subprocess.check_call(['innoextract', '--version'], stdout=subprocess.DEVNULL)
    except Exception as e:
        print('Impossible to run innoextract: {}'.format(e))
        sys.exit(1)

    with tempfile.TemporaryDirectory() as fwdir:
        fwpath = download_and_extract_fw(dev_type, fwdir, fwuri=args.driver_uri)
        shutil.copy(fwpath, PYTHON_VALIDITY_DATA_DIR)
