#!/usr/bin/env python3

import pygame, time, os

pygame.init()


def load_maps():
  with open("maps.txt") as fh:
    maps = fh.read().split("*************************************")
    return [x.rstrip() for x in maps if x.rstrip()]


class Game:

  def __init__(self):
    self.rotated_screen = pygame.display.set_mode((480, 640), pygame.DOUBLEBUF | pygame.HWSURFACE)
    self.screen = pygame.Surface((640, 480))
    self.clock = pygame.time.Clock()
    self.pics = {}
    for image in os.listdir("pics"):
      self.pics[os.path.splitext(image)[0]] = pygame.image.load(os.path.join("pics", image)).convert_alpha()
    self.grid = {}
    self.targets = {}
    self.cellrect = self.pics["ground"].get_rect()
    self.sammy = (0, 0)
    self.won = False
    self.maps = load_maps()
    self.level = 0
    self.reload_map()
    self.draw()

  def reload_map(self):
    if self.level < 0:
      self.level = 0
    if self.level >= len(self.maps):
      self.level = len(self.maps) - 1

    rows = [row.rstrip() for row in self.maps[self.level].rstrip().split("\n")
            if row.rstrip()]
    while rows:
      if ":" not in rows[0].rstrip(): break
      rows.pop(0)

    rowcount = len(rows)
    colcount = max([0] + [len(row) for row in rows])
    if rowcount > 480/32 or colcount > 640/32:
      # TODO: transpose if possible
      self.maps.pop(self.level)
      return self.reload_map()

    row_offset = 0
    if rowcount < 480//32:
      row_offset = (480//32 - rowcount)//2
    col_offset = 0
    if colcount < 640//32:
      col_offset = (640//32 - colcount)//2

    self.sammy = (0, 0)
    self.grid = {}
    self.targets = {}
    self.won = False

    for row, line in enumerate(rows):
      line = line.rstrip()
      row += row_offset
      for col, char in enumerate(line):
        col += col_offset
        if char == "@":
          self.sammy = (row, col)
        elif char in ("*", "&"):
          self.grid[(row, col)] = self.pics["object"]
        elif char == "X":
          self.grid[(row, col)] = self.pics["wall"]
        if char in ("&", "."):
          self.targets[(row, col)] = True

  def move(self, change):
    new_spot = (self.sammy[0] + change[0], self.sammy[1] + change[1])
    if new_spot not in self.grid:
      self.sammy = new_spot
      return
    if self.grid[new_spot] != self.pics["object"]:
      return
    object_new_spot = (new_spot[0] + change[0], new_spot[1] + change[1])
    if object_new_spot in self.grid:
      return
    self.grid[object_new_spot] = self.grid[new_spot]
    del self.grid[new_spot]
    self.sammy = new_spot
    self.check_for_win()

  def check_for_win(self):
    for (row, col) in self.targets:
      if self.grid.get((row, col)) != self.pics["object"]:
        return
    if self.level + 1 == len(self.maps):
      self.won = True
      return
    self.level += 1
    self.reload_map()

  def run(self):
    total = 0.0
    while True:
      event = pygame.event.wait()
      if event.type == pygame.QUIT:
        return
      if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_LEFT:
          self.move((0, -1))
        elif event.key == pygame.K_RIGHT:
          self.move((0, 1))
        elif event.key == pygame.K_UP:
          self.move((-1, 0))
        elif event.key == pygame.K_DOWN:
          self.move((1, 0))
        elif event.key == pygame.K_e:
          self.level -= 1
          self.reload_map()
        elif event.key == pygame.K_t:
          self.level += 1
          self.reload_map()
        elif event.key in (pygame.K_ESCAPE, pygame.K_RCTRL):
          return
      self.draw()

  def draw(self):
    self.screen.fill((0, 0, 0))

    for row in range(480//32):
      for col in range(640//32):
        rect = self.cellrect.move(col * self.cellrect.width, row * self.cellrect.height)
        self.screen.blit(self.pics["ground"], rect)
        if (row, col) in self.grid:
          self.screen.blit(self.grid[(row, col)], rect)

    rect = self.cellrect.move(self.sammy[1] * self.cellrect.width, self.sammy[0] * self.cellrect.height)
    self.screen.blit(self.pics["sammy"], rect)

    for (row, col) in self.targets:
      rect = self.cellrect.move(col * self.cellrect.width, row * self.cellrect.height)
      if self.grid.get((row, col)) == self.pics["object"]:
        self.screen.blit(self.pics["highlight-full"], rect)
      else:
        self.screen.blit(self.pics["highlight-empty"], rect)

    if self.won:
      fonts = pygame.font.SysFont(None, 48)
      surface = fonts.render("You won!", False, pygame.Color(255, 255, 255), pygame.Color(0, 0, 0))
      rect = surface.get_rect()
      rect.centerx = self.screen.get_rect().centerx
      rect.centery = self.screen.get_rect().centery
      self.screen.blit(surface, rect)

    self.rotated_screen.blit(pygame.transform.rotate(self.screen, 90), (0, 0))
    pygame.display.flip()


Game().run()
