# -*- coding: utf-8 -*-
#  coding=utf-8
import json
import os
import math
import logging
import requests
import time

from map_download.cmd.BaseDownloader import DownloadEngine, BaseDownloaderThread, latlng2tile_terrain, BoundBox


def get_access_token(token):
    resp = None
    request_count = 0
    url = "https://api.cesium.com/v1/assets/1/endpoint"
    while True:
        if request_count > 4:
            break
        try:
            request_count += 1
            param = {'access_token': token}
            resp = requests.get(url, params=param, timeout=2)
            if resp.status_code != 200:
                continue
            break
        except Exception as e:
            resp = None
            time.sleep(3)
    if resp is None:
        return None
    resp_json = resp.json()
    return resp_json.get('accessToken')


class TerrainDownloaderThread(BaseDownloaderThread):
    URL = "https://assets.cesium.com/1/{z}/{x}/{y}.terrain?extensions=octvertexnormals-watermask&v=1.1.0"

    def __init__(self, root_dir, bbox, token, task_q, logger=None, write_db=False):
        super(TerrainDownloaderThread, self).__init__(
            root_dir, bbox, task_q, logger, write_db=write_db, db_file_name='Terrain.db')
        self.token = token
        self._init_metadata(
            format='terrain',
            bounds='%f,%f,%f,%f' % (self.bbox.min_lng, self.bbox.min_lat, self.bbox.max_lng, self.bbox.max_lat))

    def get_url(self, x, y, z):
        return self.URL.format(x=x, y=y, z=z)

    def _download(self, x, y, z):
        file_path = '%s/%s/%i/%i/%i.%s' % (self.root_dir, 'Terrain', z, x, y, 'terrain')
        if os.path.exists(file_path):
            self._data2DB(x, y, z, file_path)
            return 0
        os.makedirs(os.path.dirname(file_path), exist_ok=True)
        resp = None
        requre_count = 0
        _url = ''
        access_token = get_access_token(self.token)
        if access_token is None:
            return -1
        param = {'extensions': 'octvertexnormals-watermask', 'v': '1.1.0', 'access_token': access_token}
        while True:
            if requre_count > 4: break
            try:
                _url = self.get_url(x, y, z)
                resp = requests.get(_url, params=param, stream=True, timeout=2)
                break
            except Exception as e:
                resp = None
                time.sleep(3)
            requre_count += 1
        if resp is None:
            return -1
        if resp.status_code != 200:
            return -1
        try:
            with open(file_path, 'wb') as f:
                for chunk in resp.iter_content(chunk_size=1024):
                    if chunk:
                        f.write(chunk)
        except Exception as e:
            return -1
        self._data2DB(x, y, z, file_path)
        return 1


class TerrainDownloadEngine(DownloadEngine):
    root_dir = ''

    def __init__(self, root_dir, bbox, token, thread_num, logger=None, write_db=False):
        super(TerrainDownloadEngine, self).__init__(bbox, thread_num, logger, write_db=write_db)
        self.root_dir = root_dir
        self.token = token

    def bbox2xyz(self, bbox, z):
        min_x, min_y = latlng2tile_terrain(bbox.min_lat, bbox.min_lng, z)
        max_x, max_y = latlng2tile_terrain(bbox.max_lat, bbox.max_lng, z)
        return math.floor(min_x), math.floor(min_y), math.ceil(max_x) + 1, math.ceil(max_y) + 1

    def generate_metadata(self):
        try:
            metadatas = {
                "attribution": "© Analytical Graphics Inc., © CGIAR-CSI, Produced using Copernicus data and "
                               "information funded by the European Union - EU-DEM layers",
                "available": [
                    [
                        {
                            "endX": 1,
                            "endY": 0,
                            "startX": 0,
                            "startY": 0
                        }
                    ],
                    [
                        {
                            "endX": 3,
                            "endY": 1,
                            "startX": 0,
                            "startY": 0
                        }
                    ],
                    [
                        {
                            "endX": 7,
                            "endY": 3,
                            "startX": 0,
                            "startY": 0
                        }
                    ],
                    [
                        {
                            "endX": 15,
                            "endY": 7,
                            "startX": 0,
                            "startY": 0
                        }
                    ],
                    [
                        {
                            "endX": 31,
                            "endY": 15,
                            "startX": 0,
                            "startY": 0
                        }
                    ],
                    [
                        {
                            "endX": 63,
                            "endY": 31,
                            "startX": 0,
                            "startY": 0
                        }
                    ],
                    [
                        {
                            "endX": 127,
                            "endY": 63,
                            "startX": 0,
                            "startY": 0
                        }
                    ],
                    [
                        {
                            "endX": 255,
                            "endY": 127,
                            "startX": 0,
                            "startY": 0
                        }
                    ],
                    [
                        {
                            "endX": 511,
                            "endY": 255,
                            "startX": 0,
                            "startY": 0
                        }
                    ],
                    [
                        {
                            "endX": 1023,
                            "endY": 511,
                            "startX": 0,
                            "startY": 0
                        }
                    ],
                    [
                        {
                            "endX": 2047,
                            "endY": 1023,
                            "startX": 0,
                            "startY": 0
                        }
                    ],
                    [
                        {
                            "endX": 4095,
                            "endY": 2047,
                            "startX": 0,
                            "startY": 0
                        }
                    ],
                            [
                        {
                            "endX": 8191,
                            "endY": 4095,
                            "startX": 0,
                            "startY": 0
                        }
                    ],
                            [
                        {
                            "endX": 16383,
                            "endY": 8191,
                            "startX": 0,
                            "startY": 0
                        }
                    ],
                        [
                            {
                                "endX": 32767,
                                "endY": 16383,
                                "startX": 0,
                                "startY": 0
                            }
                    ]
                ],
                "bounds": [-180, -90, 180, 90, ],
                "description": "STK World Terrain Premium Tileset, v1.3. 10m - 30m resolution CONUS, 30m resolution "
                               "SRTM between 60N and 60S, 30m Europe.   Minimum global coverage of 1000m.",
                "extensions": ["watermask", "vertexnormals", "octvertexnormals", ],
                "format": "quantized-mesh-1.0",
                "maxzoom": 13,
                "minzoom": 0,
                "name": "world",
                "projection": "EPSG:4326",
                "scheme": "tms",
                "tilejson": "2.1.0",
                "tiles": ["{z}/{x}/{y}.terrain?v={version}", ],
                "version": "1.31376.0"
            }
            _dir = os.path.join(self.root_dir, 'Terrain')
            os.makedirs(_dir, exist_ok=True)
            metadatas_path = os.path.join(_dir, 'layer.json')
            with open(metadatas_path, 'w') as f:
                json.dump(metadatas, f)
        except Exception as e:
            if self.logger is not None:
                self.logger.exception(e)

    def run(self):
        try:
            self.generate_metadata()
            count = 0
            bboxs = self.cut_bbox()
            for bbox in bboxs:
                _count = self.get_task_count(bbox)
                count += _count
            self.division_done_signal.emit(count)
            for bbox in bboxs:
                while True:
                    if not self.running:
                        time.sleep(0.01)
                    else:
                        break
                task_q = self.get_task_queue(bbox)
                self.threads = []
                for i in range(self.thread_num):
                    thread = TerrainDownloaderThread(self.root_dir, self.bbox, self.token, task_q, self.logger,
                                                     write_db=self.write_db)
                    thread.sub_progressBar_updated_signal.connect(self.sub_update_progressBar)
                    self.threads.append(thread)
                for thread in self.threads:
                    thread.start()
                for thread in self.threads:
                    thread.wait()
                for t in self.threads:
                    t.stop()
                    t.quit()
                self.threads = []
            self.download_done_signal.emit()
        except Exception as e:
            if self.logger is not None:
                self.logger.error(e)


if __name__ == '__main__':
    if 1:
        logger = logging.getLogger('down')
        try:
            root = r'/Users/cugxy/Documents/data/downloader'
            formatter = logging.Formatter('%(levelname)s-%(message)s')
            hdlr = logging.StreamHandler()
            log_file = os.path.join(root, 'down.log')
            file_hdlr = logging.FileHandler(log_file)
            file_hdlr.setFormatter(formatter)
            logger.addHandler(file_hdlr)
            logger.addHandler(hdlr)
            logger.setLevel(logging.INFO)
            min_lng = -180.0
            max_lng = 180.0
            min_lat = -90.0
            max_lat = 90.0
            start_zoom = 0
            end_zoom = 5
            bbox = BoundBox(max_lat, max_lng, min_lat, min_lng, start_zoom, end_zoom)
            d = TerrainDownloadEngine(root, bbox, 8, logger)
            d.start()
            time.sleep(10000)
            logger.error('main thread out')
        except Exception as e:
            logger.error(e)
    if 0:
        accessToken = get_access_token()
    pass


[((52, 11, 52, 36), 'os.path.exists', 'os.path.exists', ({(52, 26, 52, 35): 'file_path'}, {}), '(file_path)', False, 'import os\n'), ((97, 23, 97, 73), 'map_download.cmd.BaseDownloader.latlng2tile_terrain', 'latlng2tile_terrain', ({(97, 43, 97, 55): 'bbox.min_lat', (97, 57, 97, 69): 'bbox.min_lng', (97, 71, 97, 72): 'z'}, {}), '(bbox.min_lat, bbox.min_lng, z)', False, 'from map_download.cmd.BaseDownloader import DownloadEngine, BaseDownloaderThread, latlng2tile_terrain, BoundBox\n'), ((98, 23, 98, 73), 'map_download.cmd.BaseDownloader.latlng2tile_terrain', 'latlng2tile_terrain', ({(98, 43, 98, 55): 'bbox.max_lat', (98, 57, 98, 69): 'bbox.max_lng', (98, 71, 98, 72): 'z'}, {}), '(bbox.max_lat, bbox.max_lng, z)', False, 'from map_download.cmd.BaseDownloader import DownloadEngine, BaseDownloaderThread, latlng2tile_terrain, BoundBox\n'), ((289, 17, 289, 42), 'logging.getLogger', 'logging.getLogger', ({(289, 35, 289, 41): '"""down"""'}, {}), "('down')", False, 'import logging\n'), ((23, 19, 23, 61), 'requests.get', 'requests.get', (), '', False, 'import requests\n'), ((55, 20, 55, 46), 'os.path.dirname', 'os.path.dirname', ({(55, 36, 55, 45): 'file_path'}, {}), '(file_path)', False, 'import os\n'), ((99, 15, 99, 32), 'math.floor', 'math.floor', ({(99, 26, 99, 31): 'min_x'}, {}), '(min_x)', False, 'import math\n'), ((99, 34, 99, 51), 'math.floor', 'math.floor', ({(99, 45, 99, 50): 'min_y'}, {}), '(min_y)', False, 'import math\n'), ((242, 19, 242, 57), 'os.path.join', 'os.path.join', ({(242, 32, 242, 45): 'self.root_dir', (242, 47, 242, 56): '"""Terrain"""'}, {}), "(self.root_dir, 'Terrain')", False, 'import os\n'), ((243, 12, 243, 44), 'os.makedirs', 'os.makedirs', (), '', False, 'import os\n'), ((244, 29, 244, 61), 'os.path.join', 'os.path.join', ({(244, 42, 244, 46): '_dir', (244, 48, 244, 60): '"""layer.json"""'}, {}), "(_dir, 'layer.json')", False, 'import os\n'), ((292, 24, 292, 70), 'logging.Formatter', 'logging.Formatter', ({(292, 42, 292, 69): '"""%(levelname)s-%(message)s"""'}, {}), "('%(levelname)s-%(message)s')", False, 'import logging\n'), ((293, 19, 293, 42), 'logging.StreamHandler', 'logging.StreamHandler', ({}, {}), '()', False, 'import logging\n'), ((294, 23, 294, 53), 'os.path.join', 'os.path.join', ({(294, 36, 294, 40): 'root', (294, 42, 294, 52): '"""down.log"""'}, {}), "(root, 'down.log')", False, 'import os\n'), ((295, 24, 295, 53), 'logging.FileHandler', 'logging.FileHandler', ({(295, 44, 295, 52): 'log_file'}, {}), '(log_file)', False, 'import logging\n'), ((306, 19, 306, 85), 'map_download.cmd.BaseDownloader.BoundBox', 'BoundBox', ({(306, 28, 306, 35): 'max_lat', (306, 37, 306, 44): 'max_lng', (306, 46, 306, 53): 'min_lat', (306, 55, 306, 62): 'min_lng', (306, 64, 306, 74): 'start_zoom', (306, 76, 306, 84): 'end_zoom'}, {}), '(max_lat, max_lng, min_lat, min_lng, start_zoom, end_zoom)', False, 'from map_download.cmd.BaseDownloader import DownloadEngine, BaseDownloaderThread, latlng2tile_terrain, BoundBox\n'), ((309, 12, 309, 29), 'time.sleep', 'time.sleep', ({(309, 23, 309, 28): '(10000)'}, {}), '(10000)', False, 'import time\n'), ((29, 12, 29, 25), 'time.sleep', 'time.sleep', ({(29, 23, 29, 24): '(3)'}, {}), '(3)', False, 'import time\n'), ((67, 23, 67, 79), 'requests.get', 'requests.get', (), '', False, 'import requests\n'), ((99, 53, 99, 69), 'math.ceil', 'math.ceil', ({(99, 63, 99, 68): 'max_x'}, {}), '(max_x)', False, 'import math\n'), ((99, 75, 99, 91), 'math.ceil', 'math.ceil', ({(99, 85, 99, 90): 'max_y'}, {}), '(max_y)', False, 'import math\n'), ((246, 16, 246, 39), 'json.dump', 'json.dump', ({(246, 26, 246, 35): 'metadatas', (246, 37, 246, 38): 'f'}, {}), '(metadatas, f)', False, 'import json\n'), ((71, 16, 71, 29), 'time.sleep', 'time.sleep', ({(71, 27, 71, 28): '(3)'}, {}), '(3)', False, 'import time\n'), ((263, 24, 263, 40), 'time.sleep', 'time.sleep', ({(263, 35, 263, 39): '(0.01)'}, {}), '(0.01)', False, 'import time\n')]