#!/usr/bin/env python
# -*- coding: utf-8 -*-

import curses
import locale
import random
import time
import copy

width, height, offx, offy = 20, 24, 18, 0
colors = ('CYAN', 'BLUE', 'WHITE', # foreground, background, wall
  'RED', 'GREEN', 'RED', 'GREEN', 'MAGENTA', 'YELLOW', 'CYAN') # tetris
blocklist = (
  ((1, 1), ('** ', ' **')), ((1, 1), (' **', '** ')),
  ((1, 2), ('  *', '***')), ((0, 2), ('***', '  *')),
  ((0, 2), ('****')), ((1, 1), (' * ', '***')), ((1, 1), ('**', '**')))
blocks = []
scrmap = [[0 for i in xrange(width)] for j in xrange(height)]

def makecoords(bl):
  r = []
  for k, (o, b) in enumerate(bl):
    if not isinstance(b, tuple): b = (b, )
    p = []
    for j, s in enumerate(b):
      for i, c in enumerate(s):
        if c == '*': p.append([j - o[0], i - o[1]])
    r.append(p)
  return r

def showblock(y, x, b, n):
  for p in b: scrmap[y + p[0]][x + p[1]] = 2 + n

def hideblock(y, x, b):
  showblock(y, x, b, -2)

def checkspace(y, x, b):
  for p in b:
    ny, nx = y + p[0], x + p[1]
    if ny < 0 or ny >= height - 1 or nx <= 0 or nx >= width - 1: return False
    if scrmap[ny][nx]: return False
  return True

def rotateblock(y, x, b, n, r):
  if n == len(blocks) - 1: return
  hideblock(y, x, b)
  for i in xrange(len(b)): b[i] = [r * b[i][1], -r * b[i][0]]
  if not checkspace(y, x, b):
    for i in xrange(len(b)): b[i] = [-r * b[i][1], r * b[i][0]]
  showblock(y, x, b, n)

def moveblock(y, x, b, n, dy, dx):
  hideblock(y, x, b)
  ny, nx = y + dy, x + dx
  if not checkspace(ny, nx, b):
    showblock(y, x, b, n)
    return y, x
  showblock(ny, nx, b, n)
  return ny, nx

def display(v):
  for j in xrange(height):
    for i in xrange(width):
      v.addstr(j, i * 2, '  ', curses.color_pair(scrmap[j][i] + 1))
  t = time.time()
  v.addstr(0, 8,
    '%s.%03d' % (time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(t)),
      int(t * 1000) % 1000),
    curses.color_pair(1))
  v.refresh(0, 0, offy, offx, offy + height, offx + width * 2)

def main(stdscr):
  locale.setlocale(locale.LC_ALL, '')
  enc = locale.getpreferredencoding()
  w = curses.initscr()
  curses.curs_set(0)
  curses.noecho()
  w.keypad(1)
  curses.cbreak()
  curses.start_color()
  for i, c in enumerate(colors):
    if i: curses.init_pair(i, getattr(curses, 'COLOR_%s' % colors[0]) + 8,
      getattr(curses, 'COLOR_%s' % c) + (0 if i == 1 else 8))
  v = curses.newpad(height + 1, (width + 1) * 2) # double buffering
  v.keypad(1)
  global blocks, scrmap
  blocks = makecoords(blocklist)
  for j in xrange(height):
    if j < height - 1:
      scrmap[j][0] = scrmap[j][width - 1] = 1
    else:
      for i in xrange(width): scrmap[j][i] = 1
  random.seed()
  speed, tp, stat, y, x, n, b = 0.5, 0, 0, 3, 10, -1, []
  while True:
    curses.flushinp() # clear key buffer
    if stat == 0:
      stat += 1
      y, x = 3, 10
      n = random.randint(0, len(blocks) - 1)
      b = copy.deepcopy(blocks[n])
      showblock(y, x, b, n)
      tp = time.time()
    elif stat == 1:
      if time.time() - tp >= speed:
        ny, nx = moveblock(y, x, b, n, 1, 0)
        if ny != y: y, x = ny, nx
        else: stat = 2
        tp = time.time()
    elif stat == 2:
      if time.time() - tp >= speed:
        stat = 0
        tp = time.time()
    display(v)
    v.timeout(50)
    c = v.getch()
    if c == -1: pass
    elif c == ord('q'): break
    elif c == ord('z'): rotateblock(y, x, b, n, -1) # counter clockwise
    elif c == ord('x'): rotateblock(y, x, b, n, 1) # clockwise
    elif c == curses.KEY_UP: rotateblock(y, x, b, n, 1) # clockwise
    elif c == curses.KEY_LEFT: y, x = moveblock(y, x, b, n, 0, -1)
    elif c == curses.KEY_RIGHT: y, x = moveblock(y, x, b, n, 0, 1)
    elif c == curses.KEY_DOWN: y, x = moveblock(y, x, b, n, 1, 0)
    elif c == ord(' '): y, x = moveblock(y, x, b, n, 3, 0)
    else: pass
  v.keypad(0)
  curses.nocbreak()
  w.keypad(0)
  curses.echo()
  curses.curs_set(1)
  curses.endwin()

if __name__ == '__main__':
  curses.wrapper(main)