# -*- coding: utf-8 -*-
from fiona.env import Env
from fiona._env import get_gdal_version_num, calc_gdal_version_num


# Here is the list of available drivers as (name, modes) tuples. Currently,
# we only expose the defaults (excepting FileGDB). We also don't expose
# the CSV or GeoJSON drivers. Use Python's csv and json modules instead.
# Might still exclude a few more of these after making a pass through the
# entries for each at https://gdal.org/drivers/vector/index.html to screen
# out the multi-layer formats.

supported_drivers = dict([
    # OGR Vector Formats
    # Format Name 	Code 	Creation 	Georeferencing 	Compiled by default
    # Aeronav FAA files 	AeronavFAA 	No 	Yes 	Yes
    ("AeronavFAA", "r"),
    # ESRI ArcObjects 	ArcObjects 	No 	Yes 	No, needs ESRI ArcObjects
    # Arc/Info Binary Coverage 	AVCBin 	No 	Yes 	Yes
    # multi-layer
    #   ("AVCBin", "r"),
    # Arc/Info .E00 (ASCII) Coverage 	AVCE00 	No 	Yes 	Yes
    # multi-layer
    #    ("AVCE00", "r"),
    # Arc/Info Generate 	ARCGEN 	No 	No 	Yes
    ("ARCGEN", "r"),
    # Atlas BNA 	BNA 	Yes 	No 	Yes
    ("BNA", "rw"),
    # AutoCAD DWG 	DWG 	No 	No 	No
    # AutoCAD DXF 	DXF 	Yes 	No 	Yes
    ("DXF", "rw"),
    # Comma Separated Value (.csv) 	CSV 	Yes 	No 	Yes
    ("CSV", "raw"),
    # CouchDB / GeoCouch 	CouchDB 	Yes 	Yes 	No, needs libcurl
    # DODS/OPeNDAP 	DODS 	No 	Yes 	No, needs libdap
    # EDIGEO 	EDIGEO 	No 	Yes 	Yes
    # multi-layer? Hard to tell from the OGR docs
    #   ("EDIGEO", "r"),
    # ElasticSearch 	ElasticSearch 	Yes (write-only) 	- 	No, needs libcurl
    # ESRI FileGDB 	FileGDB 	Yes 	Yes 	No, needs FileGDB API library
    # multi-layer
    ("FileGDB", "raw"),
    ("OpenFileGDB", "r"),
    # ESRI Personal GeoDatabase 	PGeo 	No 	Yes 	No, needs ODBC library
    # ESRI ArcSDE 	SDE 	No 	Yes 	No, needs ESRI SDE
    # ESRIJSON 	ESRIJSON 	No 	Yes 	Yes 
    ("ESRIJSON", "r"),
    # ESRI Shapefile 	ESRI Shapefile 	Yes 	Yes 	Yes
    ("ESRI Shapefile", "raw"),
    # FMEObjects Gateway 	FMEObjects Gateway 	No 	Yes 	No, needs FME
    ("FlatGeobuf", "rw"),
    # GeoJSON 	GeoJSON 	Yes 	Yes 	Yes
    ("GeoJSON", "raw"),
    # GeoJSONSeq 	GeoJSON sequences 	Yes 	Yes 	Yes 
    ("GeoJSONSeq", "rw"),
    # Géoconcept Export 	Geoconcept 	Yes 	Yes 	Yes
    # multi-layers
    #   ("Geoconcept", "raw"),
    # Geomedia .mdb 	Geomedia 	No 	No 	No, needs ODBC library
    # GeoPackage	GPKG	Yes	Yes	No, needs libsqlite3
    ("GPKG", "raw"),
    # GeoRSS 	GeoRSS 	Yes 	Yes 	Yes (read support needs libexpat)
    # Google Fusion Tables 	GFT 	Yes 	Yes 	No, needs libcurl
    # GML 	GML 	Yes 	Yes 	Yes (read support needs Xerces or libexpat)
    ("GML", "rw"),
    # GMT 	GMT 	Yes 	Yes 	Yes
    ("GMT", "rw"),
    # GMT renamed to OGR_GMT for GDAL 2.x
    ("OGR_GMT", "rw"),
    # GPSBabel 	GPSBabel 	Yes 	Yes 	Yes (needs GPSBabel and GPX driver)
    # GPX 	GPX 	Yes 	Yes 	Yes (read support needs libexpat)
    ("GPX", "rw"),
    # GRASS 	GRASS 	No 	Yes 	No, needs libgrass
    # GPSTrackMaker (.gtm, .gtz) 	GPSTrackMaker 	Yes 	Yes 	Yes
    ("GPSTrackMaker", "rw"),
    # Hydrographic Transfer Format 	HTF 	No 	Yes 	Yes
    # TODO: Fiona is not ready for multi-layer formats: ("HTF", "r"),
    # Idrisi Vector (.VCT) 	Idrisi 	No 	Yes 	Yes
    ("Idrisi", "r"),
    # Informix DataBlade 	IDB 	Yes 	Yes 	No, needs Informix DataBlade
    # INTERLIS 	"Interlis 1" and "Interlis 2" 	Yes 	Yes 	No, needs Xerces (INTERLIS model reading needs ili2c.jar)
    # INGRES 	INGRES 	Yes 	No 	No, needs INGRESS
    # KML 	KML 	Yes 	Yes 	Yes (read support needs libexpat)
    # LIBKML 	LIBKML 	Yes 	Yes 	No, needs libkml
    # Mapinfo File 	MapInfo File 	Yes 	Yes 	Yes
    ("MapInfo File", "raw"),
    # Microstation DGN 	DGN 	Yes 	No 	Yes
    ("DGN", "raw"),
    # Access MDB (PGeo and Geomedia capable) 	MDB 	No 	Yes 	No, needs JDK/JRE
    # Memory 	Memory 	Yes 	Yes 	Yes
    # MySQL 	MySQL 	No 	Yes 	No, needs MySQL library
    # NAS - ALKIS 	NAS 	No 	Yes 	No, needs Xerces
    # Oracle Spatial 	OCI 	Yes 	Yes 	No, needs OCI library
    # ODBC 	ODBC 	No 	Yes 	No, needs ODBC library
    # MS SQL Spatial 	MSSQLSpatial 	Yes 	Yes 	No, needs ODBC library
    # Open Document Spreadsheet 	ODS 	Yes 	No 	No, needs libexpat
    # OGDI Vectors (VPF, VMAP, DCW) 	OGDI 	No 	Yes 	No, needs OGDI library
    # OpenAir 	OpenAir 	No 	Yes 	Yes
    # multi-layer
    #   ("OpenAir", "r"),
    # PCI Geomatics Database File 	PCIDSK 	No 	No 	Yes, using internal PCIDSK SDK (from GDAL 1.7.0)
    ("PCIDSK", "raw"),
    # PDS 	PDS 	No 	Yes 	Yes
    ("PDS", "r"),
    # PDS renamed to OGR_PDS for GDAL 2.x
    ("OGR_PDS", "r"),
    # PGDump 	PostgreSQL SQL dump 	Yes 	Yes 	Yes
    # PostgreSQL/PostGIS 	PostgreSQL/PostGIS 	Yes 	Yes 	No, needs PostgreSQL client library (libpq)
    # EPIInfo .REC 	REC 	No 	No 	Yes
    # S-57 (ENC) 	S57 	No 	Yes 	Yes
    # multi-layer
    ("S57", "r"),
    # SDTS 	SDTS 	No 	Yes 	Yes
    # multi-layer
    #   ("SDTS", "r"),
    # SEG-P1 / UKOOA P1/90 	SEGUKOOA 	No 	Yes 	Yes
    # multi-layers
    #   ("SEGUKOOA", "r"),
    # SEG-Y 	SEGY 	No 	No 	Yes
    ("SEGY", "r"),
    # Norwegian SOSI Standard 	SOSI 	No 	Yes 	No, needs FYBA library
    # SQLite/SpatiaLite 	SQLite 	Yes 	Yes 	No, needs libsqlite3 or libspatialite
    ("SQLite", "raw"),
    # SUA 	SUA 	No 	Yes 	Yes
    ("SUA", "r"),
    # SVG 	SVG 	No 	Yes 	No, needs libexpat
    # TopoJSON 	TopoJSON 	No 	Yes 	Yes 
    ("TopoJSON", "r"),
    # UK .NTF 	UK. NTF 	No 	Yes 	Yes
    # multi-layer
    #   ("UK. NTF", "r"),
    # U.S. Census TIGER/Line 	TIGER 	No 	Yes 	Yes
    # multi-layer
    #   ("TIGER", "r"),
    # VFK data 	VFK 	No 	Yes 	Yes
    # multi-layer
    #   ("VFK", "r"),
    # VRT - Virtual Datasource 	VRT 	No 	Yes 	Yes
    # multi-layer
    #   ("VRT", "r"),
    # OGC WFS (Web Feature Service) 	WFS 	Yes 	Yes 	No, needs libcurl
    # MS Excel format 	XLS 	No 	No 	No, needs libfreexl
    # Office Open XML spreadsheet 	XLSX 	Yes 	No 	No, needs libexpat
    # X-Plane/Flighgear aeronautical data 	XPLANE 	No 	Yes 	Yes
    # multi-layer
    #   ("XPLANE", "r")
])


# Minimal gdal version for different modes
driver_mode_mingdal = {

    'r': {'GPKG': (1, 11, 0),
          'GeoJSONSeq': (2, 4, 0),
          'FlatGeobuf': (3, 1, 0)},

    'w': {'GPKG': (1, 11, 0),
          'PCIDSK': (2, 0, 0),
          'GeoJSONSeq': (2, 4, 0),
          'FlatGeobuf': (3, 1, 3)},

    'a': {'GPKG': (1, 11, 0),
          'PCIDSK': (2, 0, 0),
          'GeoJSON': (2, 1, 0),
          'MapInfo File': (2, 0, 0)}
}


def _driver_supports_mode(driver, mode):
    """ Returns True if driver supports mode, False otherwise

        Note: this function is not part of Fiona's public API.
    """
    if driver not in supported_drivers:
        return False
    if mode not in supported_drivers[driver]:
        return False
    if driver in driver_mode_mingdal[mode]:
        if get_gdal_version_num() < calc_gdal_version_num(*driver_mode_mingdal[mode][driver]):
            return False
    return True


# Removes drivers in the supported_drivers dictionary that the
# machine's installation of OGR due to how it is compiled.
# OGR may not have optional libraries compiled or installed.
def _filter_supported_drivers():
    global supported_drivers

    with Env() as gdalenv:
        ogrdrv_names = gdalenv.drivers().keys()
        supported_drivers_copy = supported_drivers.copy()
        for drv in supported_drivers.keys():
            if drv not in ogrdrv_names:
                del supported_drivers_copy[drv]

    supported_drivers = supported_drivers_copy


_filter_supported_drivers()

# driver_converts_to_str contains field type, driver combinations that are silently converted to string
# None: field type is always converted to str
# (2, 0, 0): starting from gdal 2.0 field type is not converted to string
_driver_converts_to_str = {
    'time': {
        'CSV': None,
        'PCIDSK': None,
        'GeoJSON': (2, 0, 0),
        'GPKG': None,
        'GMT': None,
        'OGR_GMT': None
    },
    'datetime': {
        'CSV': None,
        'PCIDSK': None,
        'GeoJSON': (2, 0, 0),
        'GML': (3, 1, 0),
    },
    'date': {
        'CSV': None,
        'PCIDSK': None,
        'GeoJSON': (2, 0, 0),
        'GMT': None,
        'OGR_GMT': None,
        'GML': (3, 1, 0),
    }
}


def _driver_converts_field_type_silently_to_str(driver, field_type):
    """ Returns True if the driver converts the field_type silently to str, False otherwise

        Note: this function is not part of Fiona's public API.
    """
    if field_type in _driver_converts_to_str and driver in _driver_converts_to_str[field_type]:
        if _driver_converts_to_str[field_type][driver] is None:
            return True
        elif get_gdal_version_num() < calc_gdal_version_num(*_driver_converts_to_str[field_type][driver]):
            return True
    return False


# None: field type is never supported, (2, 0, 0) field type is supported starting with gdal 2.0
_driver_field_type_unsupported = {
    'time': {
        'ESRI Shapefile': None,
        'GPKG': (2, 0, 0),
        'GPX': None,
        'GPSTrackMaker': None,
        'GML': (3, 1, 0),
        'DGN': None,
        'BNA': None,
        'DXF': None,
        'PCIDSK': (2, 1, 0),
        'FileGDB': None,
        'FlatGeobuf': None
    },
    'datetime': {
        'ESRI Shapefile': None,
        'GPKG': (2, 0, 0),
        'DGN': None,
        'BNA': None,
        'DXF': None,
        'PCIDSK': (2, 1, 0)
    },
    'date': {
        'GPX': None,
        'GPSTrackMaker': None,
        'DGN': None,
        'BNA': None,
        'DXF': None,
        'PCIDSK': (2, 1, 0),
        'FileGDB': None,
        'FlatGeobuf': None
    }
}


def _driver_supports_field(driver, field_type):
    """ Returns True if the driver supports the field_type, False otherwise

        Note: this function is not part of Fiona's public API.
    """
    if field_type in _driver_field_type_unsupported and driver in _driver_field_type_unsupported[field_type]:
        if _driver_field_type_unsupported[field_type][driver] is None:
            return False
        elif get_gdal_version_num() < calc_gdal_version_num(*_driver_field_type_unsupported[field_type][driver]):
            return False

    return True


# None: field type never supports timezones, (2, 0, 0): field type supports timezones with GDAL 2.0.0
_drivers_not_supporting_timezones = {
    'datetime': {
        'MapInfo File': None,
        'GPKG': (3, 1, 0),
        'GPSTrackMaker': (3, 1, 1),
        'FileGDB': None,
        'SQLite': (2, 4, 0)
    },
    'time': {
        'MapInfo File': None,
        'GPKG': None,
        'GPSTrackMaker': None,
        'GeoJSON': None,
        'GeoJSONSeq': None,
        'GML': None,
        'CSV': None,
        'GMT': None,
        'OGR_GMT': None,
        'SQLite': None 
    }
}


def _driver_supports_timezones(driver, field_type):
    """ Returns True if the driver supports timezones for field_type, False otherwise

        Note: this function is not part of Fiona's public API.
    """
    if field_type in _drivers_not_supporting_timezones and driver in _drivers_not_supporting_timezones[field_type]:
        if _drivers_not_supporting_timezones[field_type][driver] is None:
            return False
        elif get_gdal_version_num() < calc_gdal_version_num(*_drivers_not_supporting_timezones[field_type][driver]):
            return False
    return True


# None: driver never supports timezones, (2, 0, 0): driver supports timezones with GDAL 2.0.0
_drivers_not_supporting_milliseconds = {
    'GPSTrackMaker': None,
    'FileGDB': None
}


def _driver_supports_milliseconds(driver):
    """ Returns True if the driver supports milliseconds, False otherwise

        Note: this function is not part of Fiona's public API.
    """
    # GDAL 2.0 introduced support for milliseconds
    if get_gdal_version_num() < calc_gdal_version_num(2, 0, 0):
        return False

    if driver in _drivers_not_supporting_milliseconds:
        if _drivers_not_supporting_milliseconds[driver] is None:
            return False
        elif calc_gdal_version_num(*_drivers_not_supporting_milliseconds[driver]) < get_gdal_version_num():
            return False

    return True

