
import time,os

import pygame
from pygame.locals import *
if not pygame.font: print 'Warning, fonts disabled'
if not pygame.mixer: print 'Warning, sound disabled'

from common import *
import factory
import map


class dummy_sprite(pygame.sprite.Sprite):
  def __init__(s):
    s.image=None
    s.x_y(0,0)
  def xy(s,pos):
    x,y=pos
    s.x_y(x,y)
  def x_y(s,x,y):
    s.x_y_w_h(x,y,1,1)
  def x_y_w_h(s,x,y,w,h):
    s.rect=pygame.Rect(x,y,w,h)


class Game:
  def __init__(s):
    # sdl init
    s.SDL_display_rect=pygame.Rect( 0,0,
            SIZE_OF_MAP_IN_TILES*SIZE_OF_TILE_IN_PIXELS+ SIZE_OF_SIDE_FRAME,
            SIZE_OF_MAP_IN_TILES*SIZE_OF_TILE_IN_PIXELS,
            )
    s.SDL_map_rect_zero=pygame.Rect( 0,0,
            SIZE_OF_MAP_IN_TILES*SIZE_OF_TILE_IN_PIXELS,
            SIZE_OF_MAP_IN_TILES*SIZE_OF_TILE_IN_PIXELS,
            )
    s.SDL_map_rect_draw=s.SDL_map_rect_zero.move(
            SIZE_OF_SIDE_FRAME,0
            )
    s.SDL_display= pygame.display.set_mode( s.SDL_display_rect[2:],DOUBLEBUF )
    s.SDL_map = pygame.Surface(s.SDL_map_rect_zero[2:]).convert()

    s.tilesize=(SIZE_OF_TILE_IN_PIXELS,SIZE_OF_TILE_IN_PIXELS)
#    s.SDL_map_black= pygame.Surface(s.SDL_map_rect_zero[2:]).convert()
#    s.SDL_map_black.fill( (0,0,0) )
    pygame.mouse.set_visible(1)
#    s.SDL_minimap_rect=pygame.Rect(0,0,MAPSIZE*5*10,MAPSIZE*5)
#    s.SDL_minimap=pygame.Surface( s.SDL_minimap_rect[2:] ).convert()
    s.SDL_clock=pygame.time.Clock()
    s.SDL_fonts={}
    s.SDL_bgcolor=(0,0,255)
    s.keys=[]
    s.offset=[0,0]
    s.images={}
    s.isize=[0,0]

    # SDL groups
    s.SDLGR_all=pygame.sprite.RenderPlain()

    # tiles
    s.SDLGR_tiles=pygame.sprite.RenderPlain()
    s.SDLGR_display_tiles=pygame.sprite.RenderClear()
    s.SDLGR_dirty_display_tiles=pygame.sprite.RenderClear()

    # objects
    s.SDLGR_objects=pygame.sprite.RenderPlain()
    # just work out if the tile is on display
#    s.SDLGR_objects_on_display=pygame.sprite.RenderPlain()

    # lights
    s.SDLGR_lights=pygame.sprite.RenderPlain()

    # things needing event checking
    s.SDLGR_events=pygame.sprite.RenderPlain()

    # misc groups
    s.SDLGR_marines=pygame.sprite.RenderPlain()

    # a dummy sprite for checking points
    s.SDL_check_xy=dummy_sprite()
    # a dummy sprite for whats on the display
    s.SDL_check_display=dummy_sprite()
    s.SDL_check_display.x_y_w_h(0,0, s.SDL_map_rect_zero[2],
                                     s.SDL_map_rect_zero[3] )

    # game stuff
    s.fact=factory.ThingFactory(s)
    s.map=None
    s.time=0.0

  def force_regen_lighting(s):
    s.SDLGR_tiles.update( UPDATE___LIGHT_LEVELS, s.SDL_map_rect_zero )
  def force_regen_screen(s):
    s.force_regen_lighting()
    s.SDL_map.fill( (0,0,0) )
    #  s.SDL_map.fill( (0,0,0) ) 1.1 seconds/100 blitting takes 2.1/100
    s.SDLGR_display_tiles.empty()
    s.SDLGR_display_tiles.add( pygame.sprite.spritecollide(
                                     s.SDL_check_display,
                                     s.SDLGR_tiles,
                                     0        )
                             )
    s.SDLGR_dirty_display_tiles.empty()
    s.SDLGR_dirty_display_tiles.add( s.SDLGR_display_tiles )


  def display_loop(s):
    s.active=1
    t1=time.time()
    count=last=0
    s.force_regen_screen()
    while s.active:
      s.time=time.time()
#      s.SDL_clock.tick(100)

      # handle events from SDL
      events=pygame.event.get()
      for event in events:
        s.SDL_event(event)
      s.SDL_keys()

      # update objects that need events
      s.SDLGR_events.update( UPDATE___CHECK_FOR_EVENTS, s.time,)
      # update dirty display tiles to screen
      if len(s.SDLGR_dirty_display_tiles):
        print 'dirty tiles',len(s.SDLGR_dirty_display_tiles)
#        s.SDLGR_dirty_display_tiles.clear( s.SDL_map, s.SDL_map_black)
#        s.SDLGR_dirty_display_tiles.clear( s.SDL_map, s.clearimage )
#        s.SDLGR_dirty_display_tiles.update( UPDATE___DUMP_RECT )
#        s.SDLGR_dirty_display_tiles.draw( s.SDL_map )
        s.SDLGR_dirty_display_tiles.update( UPDATE___MY_DIRTY_DRAW, s.SDL_map )
        s.SDLGR_dirty_display_tiles.empty()
      pygame.draw.rect(s.SDL_map,(0,255,0),[0,0,300,300],3)

      # draw map to display
      s.SDL_display.blit( s.SDL_map, s.SDL_map_rect_draw )
      # hud, minimap stuff here.
      pygame.draw.rect(s.SDL_display,(255,0,0),s.SDL_map_rect_draw,1)
      # blink!
      pygame.display.flip()

      count=count+1
      if int(t1)!=int(s.time):
        t1=s.time
        print 'fps:',count-last
        last=count



  AMBIENT=10
  def light_level_to_255(s,light):
    if light<1.:  return 0
    if light<2.:  return 2
    if light<3.:  return 4
    if light<4.:  return 8
    if light<5.:  return 16
    if light<6.:  return 32
    if light<7.:  return 44
    if light<8.:  return 60
    if light<8.:  return 80
    if light<9.:  return 110
    if light<10.: return 150
    return               255


  def image_cache(s,dir,fname,colorkey=TRANSPARENT_COLOR):
    fpath=os.path.join(dir,fname)
    image = pygame.image.load(fpath).convert()
    if colorkey is not None:
      if colorkey is -1:
        colorkey = image.get_at( AT_0_0 )
      image.set_colorkey(colorkey, RLEACCEL)
    s.images[ fname ]= image
    return image


  def tick(s):
    n=time.time()
    o=s.time
    s.time=time.time()
    return n-o

  def level_load(s,name):
    print 'MAP: starting to load'
    s.tick()
    s.map=map.Map(s)
    s.map.load_map(name)
    s.map.load_objects()
    s.map.objects_to_load=None
    return s.map
  def fact_has_type(s,type):
    return s.fact.has_type(type)
  def fact_create_tile(s,pos,blueprint_names):
    tile=s.fact.create_tile(pos,blueprint_names)
    s.SDLGR_all.add( tile )
    s.SDLGR_tiles.add( tile )
    return tile
  def fact_create_objects(s,pos,tile,blueprint_names):
    obj=s.fact.create_objects(pos,tile,blueprint_names)
    s.SDLGR_all.add( obj )
    s.SDLGR_objects.add( obj )
    return obj

  def map_get_tile(s,x,y):
    return s.map.map.get( (int(x),int(y)) , None )
  def map_get_tiles_not_blocked(s,squares):
    rv=[]
    for pos in squares:
      tile=s.map.map[ pos ]
      if not tile.is_blocked: rv.append(tile)
    return rv


  def display_offset_delta(s,dx,dy):
    x,y=s.offset
    s.offset=[x+dx,y+dx]
    pixdelta=( int(dx*SIZE_OF_TILE_IN_PIXELS), int(dy*SIZE_OF_TILE_IN_PIXELS) )
    s.SDLGR_all.update( UPDATE___MOVE_EVERYTHING_BY_DELTA,
                              dx,
                              dy,
                              pixdelta,
                              )
    s.force_regen_screen()




  def ui_click_at(s,pos):
    print 'CLICKED AT pos',pos
    r=s.SDL_check_xy
    x,y=pos
    ox,oy=s.offset
    r.x_y(x+ox-SIZE_OF_SIDE_FRAME,y+oy)
    for t in pygame.sprite.spritecollide(r,s.SDLGR_tiles,0):
      t.dump('  tiles:')
    for o in pygame.sprite.spritecollide(r,s.SDLGR_objects,0):
      o.dump('   objs:')


  def SDL_keys(s):
    if K_ESCAPE in s.keys:
      s.active=0
    if K_q in s.keys:
      s.active=0
    if K_r in s.keys:
      s.force_regen_screen()
    if K_LEFT in s.keys:
      s.display_offset_delta( KEYBOARD_SCROLL_RATE,0)
    if K_RIGHT in s.keys:
      s.display_offset_delta(-KEYBOARD_SCROLL_RATE,0)
    if K_UP in s.keys:
      s.display_offset_delta(0, KEYBOARD_SCROLL_RATE)
    if K_DOWN in s.keys:
      s.display_offset_delta(0,-KEYBOARD_SCROLL_RATE)


  def SDL_event(s,event):
    if event.type == QUIT:
      if DEBUG: print 'SDL_game: Window manager said QUIT!!!'
      s.active=0
    elif event.type == KEYDOWN:
      k=event.key
      if k not in s.keys: s.keys.append( k )
    elif event.type == KEYUP:
      k=event.key
      if k in s.keys: s.keys.remove( k )
    elif event.type == MOUSEBUTTONDOWN:
      if event.button==1: s.ui_click_at(event.pos)
    elif event.type == MOUSEBUTTONUP:
      pass
    elif event.type == ACTIVEEVENT:
      pass
    elif event.type == MOUSEMOTION:
      pass
    else:
      print 'SDL_event: unhandled event',event
