extends Sprite2D

## Возможные состояния конечного автомата заданий
enum TASK_STATE {
    DONE,     ## Выполнено
    WAIT,     ## Ожидание плитки
    RUN,      ## Запуск выполнения
    CHECK,    ## Проверка пустого места
    ERROR     ## Ошибка
}


## Координаты для проверок [lng, lat]
const TEST_DATA: = {
    'Калиниград':          [  20.51095,    54.70649   ],
    'Москва':              [  37.6156,     55.7522    ],
    'Нью-Йорк':            [ -74.006,      40.7143    ],
    'Сидней':              [ 151.20732,   -33.8678500 ],
    'Таганрог':            [  38.8969,     47.2362    ],
    'Хельсинки':           [  24.9354,     60.1695    ],
    'Азорские о-ва':       [  28.3996582,  38.5397568 ],
    'Мехико':              [ -99.12766,    19.42847   ],
    'Белое море':          [  68.136606,   42.045546  ],
    'море Беллинсгаузена': [ -72.406587,  -74.857997  ],
    'Фиджи':               [ 178.065032,  -17.713371  ],
    'Ноль':                [   0.0,         0.0       ],
    'Точка1':              [ 170.0,         0.0       ],
    'Точка2':              [-170.0,         0.0       ],
    'Море' :               [  19.4,        54.91      ] }


const MAP_MODES: = {
    0: 'application/config/map_image_size_rto',
    1: 'application/config/map_image_size_rls' }


const MAP_RELATIVE_RADIUSES: = {
    0: 0.39,
    1: 0.466
}


var map_image: Image             ## Изображение с картой
var map_tile_state: TASK_STATE   ## Текущее состояние конечного автомата плитки
var map_bcs: Vector3             ## Яркость, контраст насыщенность
var map_server_addr: String      ## Адрес сервера изображений карт в проекции Меркатора
var map_tile_format: String      ##
var map_tile_stack: Array        ## Стек для хранения плиток карты
var map_request: HTTPRequest     ## Обработчик запросов http
var map_zoom: int                ## Уровень детализации карты
var map_tile: Rect2i             ## Область одной плитки: [x-координата области, y-координата области, x-индекс плитки, y-индекс плитки]
var map_mode: int                ## Режим карты: 0 - приоритет РТО, 1 - приоритет РЛС
var prev_tiles_dic: Dictionary   ##
var lnglat: Array                ## Текущие координаты
var root_rect: Rect2i


func _ready():
    lnglat = TEST_DATA['Таганрог']
    map_tile_state = TASK_STATE.DONE
    map_request = HTTPRequest.new()
    add_child(map_request)
    map_request.request_completed.connect(self._map_request_completed)
    map_bcs = ProjectSettings.get_setting('application/config/map_bcs', Vector3(1.0, 1.0, 1.0))
    map_server_addr = ProjectSettings.get_setting('application/config/map_server_addr')
    map_tile_format = ProjectSettings.get_setting('application/config/map_tile_format', '%s/%d/%d/%d.png')
    var map_image_size = ProjectSettings.get_setting(MAP_MODES[0], Vector2i(512, 512))
    var map_tile_size = ProjectSettings.get_setting('application/config/map_tile_size', 256)
    map_image = Image.create(map_image_size.x, map_image_size.y, true, Image.FORMAT_RGB8)
    map_tile = Rect2i(0, 0, map_tile_size, map_tile_size)
    signaller.connect('zoom_changed', Callable(self, '_on_zoom_changed'))
    signaller.connect('map_mode_changed', Callable(self, '_on_map_mode_changed'))
    log.info('Адрес сервера поставщика карт: \"%s\"' % map_server_addr)
    log.info('Модуль географической карты готов')


func _on_map_mode_changed(mode: int) -> void:
    var map_image_size = ProjectSettings.get_setting(MAP_MODES[mode])
    material.set('shader_parameter/radius', MAP_RELATIVE_RADIUSES[mode])
    map_image.fill(Color(0, 0, 0, 0))
    map_image.resize(map_image_size.x, map_image_size.y)
    map_tile_state = TASK_STATE.DONE
    map_mode = mode
    var tile: = mercator.create_tile(lnglat[0], lnglat[1], map_zoom)
    map_tile_stack.clear()
    map_tile_stack.append(tile)
    prev_tiles_dic.clear()


func mark_image_center(image: Image, color: Color = Color.RED):
    var c = image.get_size() / 2
    map_image.set_pixel(c.x - 1, c.y - 1, color)
    map_image.set_pixel(c.x - 1, c.y,     color)
    map_image.set_pixel(c.x,     c.y - 1, color)
    map_image.set_pixel(c.x,     c.y,     color)


func _on_zoom_changed(zoom: int):
    if map_tile_stack.size() != 0:
        return
    map_zoom = zoom
    var tile: = mercator.create_tile(lnglat[0], lnglat[1], zoom)
    map_tile_stack.clear()
    map_tile_stack.append(tile)
    prev_tiles_dic.clear()


func img_tile_to_map_image(image: Image, dst_pos: Vector2i):
    image.adjust_bcs(map_bcs.x, map_bcs.y, map_bcs.z)
    map_image.blit_rect(image, map_tile, dst_pos)


func _process(_delta: float):
    if (map_tile_state == TASK_STATE.DONE) and map_tile_stack.size():
        var tile: mercator.Tile = map_tile_stack[-1]
        var tile_addr: = map_tile_format % [map_server_addr, tile.z, tile.x , tile.y]
        var rc = map_request.request(tile_addr)
        if rc == OK:
            map_tile_state = TASK_STATE.WAIT
        else:
            log.error('%s: Невозможно сформировать запрос HTTP' % self)
            map_tile_state = TASK_STATE.ERROR
    elif map_tile_state == TASK_STATE.CHECK:
        var root_tile: = mercator.Tile.new(root_rect.size.x, root_rect.size.y, map_zoom)
        var neighbors_tiles = mercator.get_perimetr_tiles(root_tile, (map_image.get_size().x / map_tile.size.x) / 2)
        var neighbors_tiles_dic = {}
        for tile in neighbors_tiles:
            neighbors_tiles_dic[[tile.x, tile.y]] = false
        if neighbors_tiles_dic != prev_tiles_dic:
            for tile in neighbors_tiles:
                tile.is_root = false
                map_tile_stack.push_front(tile)
                prev_tiles_dic[[tile.x, tile.y]] = false
        else:
            if map_tile_stack.size() == 0:
                signaller.emit_signal('zoom_complete', map_zoom)
        map_tile_state = TASK_STATE.DONE


func _map_request_completed(result, response_code, headers, data):
    if result != HTTPRequest.RESULT_SUCCESS:
        log.error('%s: Запрос не обработан. result: \"%s\", response_code: \"%s\", headers: \"%s\"' % [self, result, response_code, headers])
        map_tile_state = TASK_STATE.ERROR
        return

    var img_tile = Image.new()
    var error = img_tile.load_png_from_buffer(data)
    if error != OK:
        log.error('%s: Полученые данные невозможно использовать для формироания изображения. result: \"%s\", response_code: \"%s\", headers: \"%s\"' % [self, result, response_code, headers])
        map_tile_state = TASK_STATE.ERROR
        return

    var tile = map_tile_stack.pop_back()
    map_tile_state = TASK_STATE.CHECK

    if tile.is_root:
        # Отображение корневой плитки
        map_image.fill(Color(0, 0, 0, 0))
        var bounds: = mercator.bounds(tile)
        var lng_c = (bounds.east  + bounds.west)  / 2.0
        var lat_c = (bounds.north + bounds.south) / 2.0
        var sx: float = float(map_tile.size.x) / (bounds.east  - bounds.west)  * (lng_c - tile.lng) + map_tile.size.x / 2.0  # Cмещение в пискселях центра плитки от заданной точки по горизонтали.
        var sy: float = float(map_tile.size.y) / (bounds.north - bounds.south) * (lat_c - tile.lat) - map_tile.size.y / 2.0  # Cмещение в пискселях центра плитки от заданной точки по вертикали.
        var dst_pos: Vector2i = Vector2i(sx, -sy) + map_tile.size * map_mode
        img_tile_to_map_image(img_tile, dst_pos)
        root_rect = Rect2i(dst_pos.x, dst_pos.y, tile.x, tile.y)
    else:
        # Отображение соседних плиток
        var delta: =  Vector2i(tile.x - root_rect.size.x, tile.y - root_rect.size.y)
        var dst_pos: Vector2i = root_rect.position + delta * map_tile.size
        img_tile_to_map_image(img_tile, dst_pos)
    mark_image_center(map_image)
    texture = ImageTexture.create_from_image(map_image)
