################################################################
#
#   Bell Runner - Bell
#
################################################################

from __future__ import division
import os
from pygame import Rect
from pygame.transform import rotozoom
from albow.resource import resource_path, get_image, get_sound
from albow.controls import IntField
from level import Item, get_item_image
from notes import NotesField, pitch_to_note, note_to_pitch
from vector_algebra import sub2
from game import game

def get_bell_sounds():
	dir = resource_path("sounds/bells")
	names = os.listdir(dir)
	names.sort()
	sounds = []
	for name in names:
		if name.endswith(".ogg"):
			rsrc = "bells/%s" % name
			sound = get_sound(rsrc)
			sounds.append(sound)
	return sounds

rope_image = get_image("ropes/blue.png")
bell_sounds = get_bell_sounds()

#-----------------------------------------------------------------------------

class Bell(Item):

	editable = True
	resource_prefix = "bells"
	layer_name = 'bells'
	rotation_rate = 7 # pixels of rope per frame

	pitch = 0
	phase = 0 # degrees
	initial_rope_length = 200
	actor_holding_rope = None
	rope_hold_offset = 0 # up from bottom end of rope
	going_up = False

	def __init__(self, rect, image_name):
		Item.__init__(self, rect, image_name)
		base = os.path.splitext(image_name)[0]
		self.pitch = int(base.split("-")[1])
		self.init_limits()
	
	def init_limits(self):
		d = 100 / self.get_rope_per_degree()
		self.min_phase = -d
		self.max_phase = d
	
	def __setstate__(self, state):
		self.__dict__.update(state)
		if 'min_phase' not in state:
			self.init_limits()
	
	def reset(self):
		self.phase = 0
		self.rope_held_by(None, None)
	
	def create_edit_fields(self, fields):
		fields.append(('pitch', "Pitch", NotesField(20)))
		fields.append(('max_phase', "Low Limit", IntField()))
		fields.append(('min_phase', "High Limit", IntField()))
	
	def load_edit_fields(self, fields):
		rpd = self.get_rope_per_degree()
		fields.pitch.set_text(pitch_to_note(self.pitch))
		fields.min_phase.set_value(int(round(-self.min_phase * rpd)))
		fields.max_phase.set_value(int(round(-self.max_phase * rpd)))
	
	def unload_edit_fields(self, fields):
		rpd = self.get_rope_per_degree()
		self.pitch = note_to_pitch(fields.pitch.get_text()[:1])
		lo = fields.min_phase.get_value()
		hi = fields.max_phase.get_value()
		self.min_phase = -fields.min_phase.get_value() / rpd
		self.max_phase = -fields.max_phase.get_value() / rpd

	def draw(self, surf):
		rect = self.rect
		base_image = get_item_image(self.__class__, self.image_name)
		image = rotozoom(base_image, -self.phase, 1)
		r = image.get_rect()
		r.center = self.rect.center
		surf.blit(image, r)
		rope_per_degree = self.get_rope_per_degree()
		rope_length = self.initial_rope_length + self.phase * rope_per_degree
		if rope_length > 0:
			s = rect.width / 128
			x = int(round(rect.centerx + 40 * s))
			y = rect.centery
			r = rope_image.get_rect()
			r.height = rope_length
			surf.blit(rope_image, (x, y), r)
	
	def begin_frame(self, level):
		actor = self.actor_holding_rope
		if actor:
			if self.going_up:
				if self.move_towards_phase(self.min_phase):
					self.going_up = False
			else:
				if self.move_towards_phase(self.max_phase):
					self.ring()
					level.bell_rung(self.pitch)
					self.going_up = True
		else:
			self.move_towards_phase(0)
	
	def move_towards_phase(self, target_phase):
		reached = False
		phase = self.phase
		rpd = self.get_rope_per_degree()
		delta_phase = self.rotation_rate / rpd
		if phase < target_phase:
			phase += delta_phase
			reached = phase >= target_phase
		elif phase > target_phase:
			phase -= delta_phase
			reached = phase <= target_phase
		else:
			reached = False
		if reached:
			phase = target_phase
		self.set_phase(phase)
		return reached
	
	def set_phase(self, phase):
		self.phase = phase
		actor = self.actor_holding_rope
		if actor:
			r = self.get_rope_rect()
			x = r.centerx
			y = r.bottom - self.rope_hold_offset
			if y > r.top:
				actor.set_grab_position((x, y))
			else:
				actor.release_rope()
	
	def ring(self):
		bell_sounds[self.pitch].play()

	def editor_handle_rects(self):
		r = Rect(0, 0, 16, 16)
		r.center = self.get_rope_bottom_point()
		yield r
	
	def begin_handle_drag(self, e):
		p = self.get_rope_bottom_point()
		game.drag_pt = sub2(e.local, p)
	
	def drag_handle(self, e):
		t = self.get_rope_top_point()
		ymin = t[1]
		y = e.local[1]
		dy = game.drag_pt[1]
		self.initial_rope_length = max(y - dy - ymin, 0)
		game.level_changed()

	def get_rope_rect(self):
		p = self.get_rope_top_point()
		r = self.get_rope_source_rect()
		r.topleft = p
		return r
	
	def get_rope_top_point(self):
		rect = self.rect
		s = rect.width / 128
		x = int(round(rect.centerx + 40 * s))
		y = rect.centery
		return (x, y)
	
	def get_rope_bottom_point(self):
		r = self.get_rope_rect()
		return r.midbottom
	
	def get_rope_source_rect(self):
		rope_per_degree = self.get_rope_per_degree()
		rope_length = self.initial_rope_length + self.phase * rope_per_degree
		r = rope_image.get_rect()
		r.height = rope_length
		return r
	
	def get_rope_per_degree(self):
		return 0.006 * self.rect.width

	def rope_held_by(self, actor, grab_pt):
		self.actor_holding_rope = actor
		r = self.get_rope_rect()
		if grab_pt:
			self.rope_hold_offset = r.bottom - grab_pt[1]
		self.going_up = False
