from nicegui import ui

from . import doc


@doc.demo(ui.leaflet)
def main_demo() -> None:
    m = ui.leaflet(center=(51.505, -0.09))
    ui.label().bind_text_from(m, 'center', lambda center: f'Center: {center[0]:.3f}, {center[1]:.3f}')
    ui.label().bind_text_from(m, 'zoom', lambda zoom: f'Zoom: {zoom}')

    with ui.grid(columns=2):
        ui.button('London', on_click=lambda: m.set_center((51.505, -0.090)))
        ui.button('Berlin', on_click=lambda: m.set_center((52.520, 13.405)))
        ui.button(icon='zoom_in', on_click=lambda: m.set_zoom(m.zoom + 1))
        ui.button(icon='zoom_out', on_click=lambda: m.set_zoom(m.zoom - 1))


@doc.demo('Changing the Map Style', '''
    The default map style is OpenStreetMap.
    You can find more map styles at <https://leaflet-extras.github.io/leaflet-providers/preview/>.
    Each call to `tile_layer` stacks upon the previous ones.
    So if you want to change the map style, you have to remove the default one first.

    *Updated in version 2.12.0: Both WMTS and WMS map services are supported.*
''')
def map_style() -> None:
    ui.label('Web Map Tile Service')
    map1 = ui.leaflet(center=(51.505, -0.090), zoom=3)
    map1.clear_layers()
    map1.tile_layer(
        url_template=r'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',
        options={
            'maxZoom': 17,
            'attribution':
                'Map data: &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, <a href="https://viewfinderpanoramas.org/">SRTM</a> | '
                'Map style: &copy; <a href="https://opentopomap.org">OpenTopoMap</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)'
        },
    )

    ui.label('Web Map Service')
    map2 = ui.leaflet(center=(51.505, -0.090), zoom=3)
    map2.clear_layers()
    map2.wms_layer(
        url_template='http://ows.mundialis.de/services/service?',
        options={
            'layers': 'TOPO-WMS,OSM-Overlay-WMS'
        },
    )


@doc.demo('Add Markers on Click', '''
    You can add markers to the map with `marker`.
    The `center` argument is a tuple of latitude and longitude.
    This demo adds markers by clicking on the map.
    Note that the "map-click" event refers to the click event of the map object,
    while the "click" event refers to the click event of the container div.
''')
def markers() -> None:
    from nicegui import events

    m = ui.leaflet(center=(51.505, -0.09))

    def handle_click(e: events.GenericEventArguments):
        lat = e.args['latlng']['lat']
        lng = e.args['latlng']['lng']
        m.marker(latlng=(lat, lng))
    m.on('map-click', handle_click)


@doc.demo('Move Markers', '''
    You can move markers with the `move` method.
''')
def move_markers() -> None:
    m = ui.leaflet(center=(51.505, -0.09))
    marker = m.marker(latlng=m.center)
    ui.button('Move marker', on_click=lambda: marker.move(51.51, -0.09))


@doc.demo('Image Overlays', '''
    Leaflet supports [image overlays](https://leafletjs.com/reference.html#imageoverlay).
    You can add an image overlay with the `image_overlay` method.

    *Added in version 2.17.0*
''')
def overlay_image():
    m = ui.leaflet(center=(52.5165, 13.4047), zoom=13)
    m.image_overlay(
        url='https://images.squarespace-cdn.com/content/v1/5b3e152e620b8559f2edcf7d/1613743643304-Y7SLCT43BQ2N8C2QA3JN/1660+berlin+%28Custom%29.jpg',
        bounds=[[52.5088, 13.3877], [52.5242, 13.4218]],
        options={'opacity': 0.8},
    )


@doc.demo('Video Overlays', '''
    Leaflet supports [video overlays](https://leafletjs.com/reference.html#videooverlay).
    You can add a video overlay with the `video_overlay` method.

    *Added in version 2.17.0*
''')
def overlay_video():
    m = ui.leaflet(center=(23.0, -115.0), zoom=3)
    m.video_overlay(
        url='https://www.mapbox.com/bites/00188/patricia_nasa.webm',
        bounds=[[32, -130], [13, -100]],
        options={'opacity': 0.8, 'autoplay': True, 'playsInline': True},
    )


@doc.demo('Vector Layers', '''
    Leaflet supports a set of [vector layers](https://leafletjs.com/reference.html#:~:text=VideoOverlay-,Vector%20Layers,-Path) like circle, polygon etc.
    These can be added with the `generic_layer` method.
    We are happy to review any pull requests to add more specific layers to simplify usage.
''')
def vector_layers() -> None:
    m = ui.leaflet(center=(51.505, -0.09)).classes('h-32')
    m.generic_layer(name='circle', args=[m.center, {'color': 'red', 'radius': 300}])


@doc.demo('Disable Pan and Zoom', '''
    There are [several options to configure the map in Leaflet](https://leafletjs.com/reference.html#map).
    This demo disables the pan and zoom controls.
''')
def disable_pan_zoom() -> None:
    options = {
        'zoomControl': False,
        'scrollWheelZoom': False,
        'doubleClickZoom': False,
        'boxZoom': False,
        'keyboard': False,
        'dragging': False,
    }
    ui.leaflet(center=(51.505, -0.09), options=options)


@doc.demo('Draw on Map', '''
    You can enable a toolbar to draw on the map.
    The `draw_control` can be used to configure the toolbar.
    This demo adds markers and polygons by clicking on the map.
    By setting "edit" and "remove" to `True` (the default), you can enable editing and deleting drawn shapes.
''')
def draw_on_map() -> None:
    from nicegui import events

    def handle_draw(e: events.GenericEventArguments):
        layer_type = e.args['layerType']
        coords = e.args['layer'].get('_latlng') or e.args['layer'].get('_latlngs')
        ui.notify(f'Drawn a {layer_type} at {coords}')

    draw_control = {
        'draw': {
            'polygon': True,
            'marker': True,
            'circle': True,
            'rectangle': True,
            'polyline': True,
            'circlemarker': True,
        },
        'edit': {
            'edit': True,
            'remove': True,
        },
    }
    m = ui.leaflet(center=(51.505, -0.09), draw_control=draw_control)
    m.classes('h-96')
    m.on('draw:created', handle_draw)
    m.on('draw:edited', lambda: ui.notify('Edit completed'))
    m.on('draw:deleted', lambda: ui.notify('Delete completed'))


@doc.demo('Draw with Custom Options', '''
    You can draw shapes with custom options like stroke color and weight.
    To hide the default rendering of drawn items, set `hide_drawn_items` to `True`.
''')
def draw_custom_options():
    from nicegui import events

    def handle_draw(e: events.GenericEventArguments):
        options = {'color': 'red', 'weight': 1}
        m.generic_layer(name='polygon', args=[e.args['layer']['_latlngs'], options])

    draw_control = {
        'draw': {
            'polygon': True,
            'marker': False,
            'circle': False,
            'rectangle': False,
            'polyline': False,
            'circlemarker': False,
        },
        'edit': {
            'edit': False,
            'remove': False,
        },
    }
    m = ui.leaflet(center=(51.5, 0), draw_control=draw_control, hide_drawn_items=True)
    m.on('draw:created', handle_draw)


@doc.demo('Run Map Methods', '''
    You can run methods of the Leaflet map object with `run_map_method`.
    This demo shows how to fit the map to the whole world.
''')
def run_map_methods() -> None:
    m = ui.leaflet(center=(51.505, -0.09)).classes('h-32')
    ui.button('Fit world', on_click=lambda: m.run_map_method('fitWorld'))


@doc.demo('Run Layer Methods', '''
    You can run methods of the Leaflet layer objects with `run_layer_method`.
    This demo shows how to change the opacity of a marker or change its icon.
''')
def run_layer_methods() -> None:
    m = ui.leaflet(center=(51.505, -0.09)).classes('h-32')
    marker = m.marker(latlng=m.center)
    ui.button('Hide', on_click=lambda: marker.run_method('setOpacity', 0.3))
    ui.button('Show', on_click=lambda: marker.run_method('setOpacity', 1.0))

    icon = 'L.icon({iconUrl: "https://leafletjs.com/examples/custom-icons/leaf-green.png"})'
    ui.button('Change icon', on_click=lambda: marker.run_method(':setIcon', icon))


@doc.demo('Wait for Initialization', '''
    You can wait for the map to be initialized with the `initialized` method.
    This is necessary when you want to run methods like fitting the bounds of the map right after the map is created.
''')
async def wait_for_init() -> None:
    # @ui.page('/')
    async def page():
        m = ui.leaflet(zoom=5)
        central_park = m.generic_layer(name='polygon', args=[[
            (40.767809, -73.981249),
            (40.800273, -73.958291),
            (40.797011, -73.949683),
            (40.764704, -73.973741),
        ]])
        await m.initialized()
        bounds = await central_park.run_method('getBounds')
        m.run_map_method('fitBounds', [[bounds['_southWest'], bounds['_northEast']]])
    ui.timer(0, page, once=True)  # HIDE


@doc.demo('Leaflet Plugins', '''
    You can add plugins to the map by passing the URLs of JS and CSS files to the `additional_resources` parameter.
    This demo shows how to add the [Leaflet.RotatedMarker](https://github.com/bbecquet/Leaflet.RotatedMarker) plugin.
    It allows you to rotate markers by a given `rotationAngle`.

    *Added in version 2.11.0*
''')
def leaflet_plugins() -> None:
    m = ui.leaflet((51.51, -0.09), additional_resources=[
        'https://unpkg.com/leaflet-rotatedmarker@0.2.0/leaflet.rotatedMarker.js',
    ])
    m.marker(latlng=(51.51, -0.091), options={'rotationAngle': -30})
    m.marker(latlng=(51.51, -0.090), options={'rotationAngle': 30})


doc.reference(ui.leaflet)
