# Copyright 2019 DeepMind Technologies Limited.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================

"""Randomly generated text mazes."""

from labmaze import base
from labmaze import defaults
from labmaze import text_grid
from labmaze.cc.python import _random_maze
import numpy as np


class RandomMaze(base.BaseMaze):
  """A random text maze generated by DeepMind Lab's maze generator."""

  def __init__(
      self, height=11, width=11,
      max_rooms=defaults.MAX_ROOMS,
      room_min_size=defaults.ROOM_MIN_SIZE,
      room_max_size=defaults.ROOM_MAX_SIZE,
      retry_count=defaults.RETRY_COUNT,
      extra_connection_probability=defaults.EXTRA_CONNECTION_PROBABILITY,
      max_variations=defaults.MAX_VARIATIONS,
      has_doors=defaults.HAS_DOORS,
      simplify=defaults.SIMPLIFY,
      spawns_per_room=defaults.SPAWN_COUNT,
      spawn_token=defaults.SPAWN_TOKEN,
      objects_per_room=defaults.OBJECT_COUNT,
      object_token=defaults.OBJECT_TOKEN, random_seed=None):

    if height != int(height) or height < 0 or height % 2 == 0:
      raise ValueError(
          '`height` should be a positive odd integer: got {!r}'.format(height))

    if width != int(width) or width < 0 or width % 2 == 0:
      raise ValueError(
          '`width` should be a positive odd integer: got {!r}'.format(width))

    if room_min_size != int(room_min_size) or room_min_size < 0:
      raise ValueError('`room_min_size` should be a positive integer: '
                       'got {!r}'.format(room_min_size))

    if room_max_size != int(room_max_size) or room_max_size < 0:
      raise ValueError('`room_max_size` should be a positive integer: '
                       'got {!r}'.format(room_max_size))

    if room_min_size > room_max_size:
      raise ValueError(
          '`room_min_size` should be less than or equal to `room_max_size`: '
          'got room_min_size={!r} and room_max_size={!r}'
          .format(room_min_size, room_max_size))

    if retry_count != int(retry_count) or retry_count < 0:
      raise ValueError('`retry_count` should be a positive integer: '
                       'got {!r}'.format(retry_count))

    if extra_connection_probability < 0 or extra_connection_probability > 1:
      raise ValueError(
          '`extra_connection_probability` should be between 0.0 and 1.0: '
          'got {!r}'.format(extra_connection_probability))

    if (max_variations != int(max_variations)
        or max_variations < 0 or max_variations > 26):
      raise ValueError(
          '`max_variations` should be an integer between 0 and 26: '
          'got {!r}'.format(max_variations))

    spawn_token = str(spawn_token)
    if len(spawn_token) != 1:
      raise ValueError('`spawn_token` should be a single character: '
                       'got {!r}'.format(spawn_token))

    object_token = str(object_token)
    if len(object_token) != 1:
      raise ValueError('`object_token` should be a single character: '
                       'got {!r}'.format(object_token))

    if random_seed is None:
      random_seed = np.random.randint(2147483648)  # 2**31

    self._height = height
    self._width = width
    self._max_rooms = max_rooms
    self._room_min_size = room_min_size
    self._room_max_size = room_max_size
    self._max_variations = max_variations
    self._spawns_per_room = spawns_per_room
    self._spawn_token = spawn_token
    self._objects_per_room = objects_per_room
    self._object_token = object_token

    self._native_maze = _random_maze.RandomMaze(
        height=height, width=width, max_rooms=max_rooms,
        room_min_size=room_min_size, room_max_size=room_max_size,
        retry_count=retry_count,
        extra_connection_probability=extra_connection_probability,
        max_variations=max_variations,
        has_doors=has_doors, simplify=simplify,
        spawns_per_room=spawns_per_room, spawn_token=spawn_token,
        objects_per_room=objects_per_room, object_token=object_token,
        random_seed=random_seed)
    self._entity_layer = text_grid.TextGrid(self._native_maze.entity_layer)
    self._variations_layer = (
        text_grid.TextGrid(self._native_maze.variations_layer))

  def regenerate(self):
    self._native_maze.regenerate()
    self._entity_layer = text_grid.TextGrid(self._native_maze.entity_layer)
    self._variations_layer = (
        text_grid.TextGrid(self._native_maze.variations_layer))

  @property
  def entity_layer(self):
    return self._entity_layer

  @property
  def variations_layer(self):
    return self._variations_layer

  @property
  def height(self):
    return self._height

  @property
  def width(self):
    return self._width

  @property
  def max_rooms(self):
    return self._max_rooms

  @property
  def room_min_size(self):
    return self._room_min_size

  @property
  def room_max_size(self):
    return self._room_max_size

  @property
  def max_variations(self):
    return self._max_variations

  @property
  def spawns_per_room(self):
    return self._spawns_per_room

  @property
  def spawn_token(self):
    return self._spawn_token

  @property
  def objects_per_room(self):
    return self._objects_per_room

  @property
  def object_token(self):
    return self._object_token
