#! /usr/bin/env python3
# -*- coding: iso-8859-1 -*-

import glob
import hashlib
import logging
import os
import sys
from zipfile import ZipFile, ZipInfo

from clint.textui import progress

import requests


cachefiles = [
  ('https://dl.google.com/android/repository/platform-tools-latest-linux.zip',
   '',
   'platform-tools'),
  ('https://dl.google.com/android/repository/tools_r25.2.5-linux.zip',
   '577516819c8b5fae680f049d39014ff1ba4af870b687cab10595783e6f22d33e',
   'tools'),
  ('https://dl.google.com/android/repository/android-19_r04.zip',
   '5efc3a3a682c1d49128daddb6716c433edf16e63349f32959b6207524ac04039',
   'platform'),
  ('https://dl.google.com/android/repository/build-tools_r26-linux.zip',
   '7422682f92fb471d4aad4c053c9982a9a623377f9d5e4de7a73cd44ebf2f3c61',
   'build-tools'),
  ('https://dl.google.com/'
   'android/repository/android-ndk-r12b-linux-x86_64.zip',
   'eafae2d614e5475a3bcfd7c5f201db5b963cc1290ee3e8ae791ff0c66757781e',
   'ndk'),
]

# https://stackoverflow.com/questions/39296101:


class MyZipFile(ZipFile):

    def extract(self, member, path=None, pwd=None):
        if not isinstance(member, ZipInfo):
            member = self.getinfo(member)

        if path is None:
            path = os.getcwd()

        ret_val = self._extract_member(member, path, pwd)
        attr = member.external_attr >> 16
        os.chmod(ret_val, attr)
        return ret_val

# Reused from fdroidserver:


def sha256_for_file(path):
    with open(path, 'rb') as f:
        s = hashlib.sha256()
        while True:
            data = f.read(4096)
            if not data:
                break
            s.update(data)
        return s.hexdigest()

# Adapted from fdroidserver:


def update_cache(cachedir, cachefiles):
    for srcurl, shasum, typ in cachefiles:
        filename = os.path.basename(srcurl)
        local_filename = os.path.join(cachedir, filename)

        if os.path.exists(local_filename):
            local_length = os.path.getsize(local_filename)
        else:

            local_length = -1

        if (typ == 'ndk') and (ndkloc is not None):
            continue
        elif (typ != 'tools') and (sdkloc is not None):
            continue

        resume_header = {}
        download = True

        try:
            r = requests.head(srcurl, allow_redirects=True, timeout=60)
            if r.status_code == 200:
                content_length = int(r.headers.get('content-length'))
            else:
                content_length = local_length  # skip the download
        except requests.exceptions.RequestException as e:
            content_length = local_length  # skip the download
            logger.warn('%s', e)

        if local_length == content_length:
            download = False
        elif local_length > content_length:
            logger.info('deleting corrupt file from cache: %s',
                        local_filename)
            os.remove(local_filename)
            logger.info("Downloading %s to cache", filename)
        elif local_length > -1 and local_length < content_length:
            logger.info("Resuming download of %s", local_filename)
            resume_header = {
                'Range': 'bytes=%d-%d' % (local_length, content_length)}
        else:
            logger.info("Downloading %s to cache", filename)

        if download:
            r = requests.get(srcurl, headers=resume_header,
                             stream=True, verify=False, allow_redirects=True)
            content_length = int(r.headers.get('content-length'))
            with open(local_filename, 'ab') as f:
                for chunk in progress.bar(
                        r.iter_content(chunk_size=65536),
                        expected_size=(content_length / 65536) + 1):
                    if chunk:  # filter out keep-alive new chunks
                        f.write(chunk)
        if not shasum == '':
            v = sha256_for_file(local_filename)
            if v == shasum:
                logger.info("Shasum verified for %s", local_filename)
            else:
                logger.critical(
                  "Invalid shasum of '%s' detected for %s", v, local_filename)
                os.remove(local_filename)
                sys.exit(1)

# Build the sdk from zips.


def build_sdk(sdkdir, cachedir, cachfiles):
    for srcurl, shasum, typ in cachefiles:
        filename = os.path.basename(srcurl)
        local_filename = os.path.join(cachedir, filename)

        if typ == 'tools':
            if os.path.exists(local_filename):
                print('Extract: %s' % local_filename)
                zf = MyZipFile(local_filename)
                zf.extractall(sdkdir)
        elif typ == 'platform-tools':
            if (sdkloc is None) and (os.path.exists(local_filename)):
                print('Extract: %s' % local_filename)
                zf = MyZipFile(local_filename)
                zf.extractall(sdkdir)
            else:
                print('Link to: %s' % sdkloc)
                os.symlink(sdkloc + '/platform-tools',
                           sdkdir + '/platform-tools')
        elif typ == 'platform':
            if (sdkloc is None) and (os.path.exists(local_filename)):
                print('Extract: %s' % local_filename)
                zf = MyZipFile(local_filename)
                zf.extractall(sdkdir + '/platforms')
            else:
                print('Link to: %s' % sdkloc)
                os.symlink(sdkloc + '/platforms', sdkdir + '/platforms')
        elif typ == 'build-tools':
            if (sdkloc is None) and (os.path.exists(local_filename)):
                print('Extract: %s' % local_filename)
                zf = MyZipFile(local_filename)
                zf.extractall(sdkdir + '/build-tools')
            else:
                print('Link to: %s' % sdkloc)
                os.symlink(sdkloc + '/build-tools', sdkdir + '/build-tools')
        elif typ == 'ndk':
            if ndkloc is None:
                print('Extract: %s' % local_filename)
                zf = MyZipFile(local_filename)
                zf.extractall(sdkdir)
                lst = glob.glob(sdkdir + '/*-ndk-*')
                print(lst)
                os.rename(lst[0], sdkdir + '/ndk-bundle')
            else:
                print('Link to: %s' % ndkloc)
                os.symlink(ndkloc, sdkdir + '/ndk-bundle')


logger = logging.getLogger('prepare-fdroid-build')
logging.basicConfig(format='%(message)s', level=logging.INFO)
logger.setLevel(logging.INFO)

# command line arguments

sdkloc = None
ndkloc = None
if len(sys.argv) > 1:
    sdkloc = sys.argv[1]
    if (len(sdkloc) > 0) and (sdkloc[-1] == '/'):
        sdkloc = sdkloc[:-1]
    if not os.path.isdir(sdkloc):
        sdkloc = None

if len(sys.argv) > 2:
    ndkloc = sys.argv[2]
    if (len(ndkloc) > 0) and (ndkloc[-1] == '/'):
        ndkloc = ndkloc[:-1]
    if not os.path.isdir(ndkloc):
        ndkloc = None

fdroidmode = None
if len(sys.argv) > 3:
    fdroidmode = sys.argv[3]
    if (len(fdroidmode) > 0):
        fdroidmode = '1'

if sdkloc == "":
    sdkloc = None
if ndkloc == "":
    ndkloc = None

logger.info('sdkloc = %s' % sdkloc)
logger.info('ndkloc = %s' % ndkloc)

# sdkloc and ndkloc already set by the user and fdroidmode:
# nothing to do.

if (sdkloc is not None) and (ndkloc is not None) and (fdroidmode is not None):
    sys.exit(0)

# cache dir (using the same as in fdroidserver/buildserver)
cachedir = os.path.join(os.getenv('HOME'), '.cache', 'fdroidserver')
logger.info('cachedir name is: %s', cachedir)

if not os.path.exists(cachedir):
    os.makedirs(cachedir, 0o755)
    logger.info('created cachedir %s', cachedir)

# sdkdir location
sdkdir = os.path.join(os.getenv('HOME'), '.cache', 'sdk-for-p4a')
logger.info('sdkdir name is: %s', sdkdir)

if not os.path.exists(sdkdir):
    os.makedirs(sdkdir, 0o755)
    logger.debug('created sdkdir %s', sdkdir)

    update_cache(cachedir, cachefiles)
    build_sdk(sdkdir, cachedir, cachefiles)
else:
    logger.info('sdkdir %s already exists', sdkdir)
