# Copyright 2020 <NAME>
# SPDX-License-Identifier: Apache-2.0

import logging
import os
import os.path
from concurrent import futures

import yaml

from . import cameras, constants, game_map
from .editor import map_editor

logger = logging.getLogger(__name__)


class AutoSave:
    _MAX_BACKUPS = 10
    _executor = futures.ThreadPoolExecutor(max_workers=1)

    def __init__(
        self,
        editor: map_editor.MapEditor,
        meta_data: dict,
        camera_collection: cameras.Cameras,
        map_path: str,
    ):
        self._editor = editor
        self._meta_data = meta_data
        self._camera_collection = camera_collection
        self._map_path_prefix = map_path[: constants.MAP_EXTENSION_SKIP]
        self._stopped = False

    def perform_save(self, task):
        if self._stopped:
            return task.done

        self._truncate_backups()
        position = self._camera_collection.get_builder_position()
        map_to_save = self._editor.to_game_map(position)

        self._executor.submit(self._do_save, map_to_save)

        path = self._map_path(0)
        self._log_info(f"Auto saving to {path}")

        return task.again

    def stop(self):
        self._stopped = True

    def _truncate_backups(self):
        backup_indices = reversed(range(self._MAX_BACKUPS - 1))
        for index in backup_indices:
            self._move_backup(index)

    def _move_backup(self, index: int):
        old_path = self._map_path(index)
        if not os.path.exists(old_path):
            return

        new_index = index + 1
        old_meta_data_path = self._meta_data_path(index)

        new_path = self._map_path(new_index)
        new_meta_data_path = self._meta_data_path(new_index)

        if os.path.exists(new_path):
            os.remove(new_path)
        os.rename(old_path, new_path)
        if os.path.exists(old_meta_data_path):
            if os.path.exists(new_meta_data_path):
                os.remove(new_meta_data_path)
            os.rename(old_meta_data_path, new_meta_data_path)

    def _map_path(self, index: int):
        return f"{self._map_path_prefix}-BACKUP-{index}.MAP"

    def _meta_data_path(self, index: int):
        return f"{self._map_path_prefix}-BACKUP-{index}.YAML"

    def _do_save(self, map_to_save: game_map.Map):
        try:
            path = self._map_path(0)
            meta_data_path = self._meta_data_path(0)

            map_data, _ = map_to_save.save(path)
            with open(path, "w+b") as map_file:
                map_file.write(map_data)

            with open(meta_data_path, "w+") as meta_file:
                meta_file.write(yaml.dump(self._meta_data))
        except Exception as error:
            self._log_info(f"Failed to save due to: {error}")

    def _log_info(self, message: str):
        logger.info(message)
        self._camera_collection.set_info_text(message)
