# coding=utf-8
"""
MBTiles provider for MapView
============================

This provider is based on .mbfiles from MapBox.
See: http://mbtiles.org/
"""

__all__ = ["MBTilesMapSource"]


import io
import sqlite3
import threading

from kivy.core.image import Image as CoreImage
from kivy.core.image import ImageLoader

from mapview.downloader import Downloader
from mapview.source import MapSource


class MBTilesMapSource(MapSource):
    def __init__(self, filename, **kwargs):
        super().__init__(**kwargs)
        self.filename = filename
        self.db = sqlite3.connect(filename)

        # read metadata
        c = self.db.cursor()
        metadata = dict(c.execute("SELECT * FROM metadata"))
        if metadata["format"] == "pbf":
            raise ValueError("Only raster maps are supported, not vector maps.")
        self.min_zoom = int(metadata["minzoom"])
        self.max_zoom = int(metadata["maxzoom"])
        self.attribution = metadata.get("attribution", "")
        self.bounds = bounds = None
        cx = cy = 0.0
        cz = 5
        if "bounds" in metadata:
            self.bounds = bounds = tuple(map(float, metadata["bounds"].split(",")))
        if "center" in metadata:
            cx, cy, cz = tuple(map(float, metadata["center"].split(",")))
        elif self.bounds:
            cx = (bounds[2] + bounds[0]) / 2.0
            cy = (bounds[3] + bounds[1]) / 2.0
            cz = self.min_zoom
        self.default_lon = cx
        self.default_lat = cy
        self.default_zoom = int(cz)
        self.projection = metadata.get("projection", "")
        self.is_xy = self.projection == "xy"

    def fill_tile(self, tile):
        if tile.state == "done":
            return
        Downloader.instance(self.cache_dir).submit(self._load_tile, tile)

    def _load_tile(self, tile):
        # global db context cannot be shared across threads.
        ctx = threading.local()
        if not hasattr(ctx, "db"):
            ctx.db = sqlite3.connect(self.filename)

        # get the right tile
        c = ctx.db.cursor()
        c.execute(
            (
                "SELECT tile_data FROM tiles WHERE "
                "zoom_level=? AND tile_column=? AND tile_row=?"
            ),
            (tile.zoom, tile.tile_x, tile.tile_y),
        )
        row = c.fetchone()
        if not row:
            tile.state = "done"
            return

        # no-file loading
        try:
            data = io.BytesIO(row[0])
        except Exception:
            # android issue, "buffer" does not have the buffer interface
            # ie row[0] buffer is not compatible with BytesIO on Android??
            data = io.BytesIO(bytes(row[0]))
        im = CoreImage(
            data,
            ext='png',
            filename="{}.{}.{}.png".format(tile.zoom, tile.tile_x, tile.tile_y),
        )

        if im is None:
            tile.state = "done"
            return

        return self._load_tile_done, (tile, im,)

    def _load_tile_done(self, tile, im):
        tile.texture = im.texture
        tile.state = "need-animation"

    def get_x(self, zoom, lon):
        if self.is_xy:
            return lon
        return super().get_x(zoom, lon)

    def get_y(self, zoom, lat):
        if self.is_xy:
            return lat
        return super().get_y(zoom, lat)

    def get_lon(self, zoom, x):
        if self.is_xy:
            return x
        return super().get_lon(zoom, x)

    def get_lat(self, zoom, y):
        if self.is_xy:
            return y
        return super().get_lat(zoom, y)