from __future__ import division
import collections
import os.path
import glob
import yaml

import libtcodpy as libtcod
import game
import objects
import utilities
import monsters
import random
from main import Game


class Item(object):
	stack_limit = 5
	potency = None
	item_class = None
	distance = None
	probability = 1

	def __init__(self, stackable=False):
		self.mods = collections.defaultdict(set)

	def __new__(*args):
		res = object.__new__(*args)
		return res
	def bind_game(self, game):
		self.game = game
	def bind_user(self, user):
		self.user = user
		return self.owner
	def free_user(self):
		self.user = None
		return self.owner

	def modify(self, mod):
		undo = mod.modify(self)
		self.mods[mod.name].add(mod)
		self.owner.name = self.name

	def unmodify(self, mod):
		if hasattr(mod, 'upper'):
			mods = self.mods[mod]
		else:
			mods = self.mods[mod.name]
		if mods != set():
			mod = mods.pop()
			mod.revert(self)
			self.owner.name = self.name
			if mods == set():
				self.mods.pop(mod.name)
			print 'self.mods', self.mods

class ItemLoader(object):
	def __init__(self, dir):
		self.dir = dir

	def load_items(self):
		for fn in glob.glob(os.path.join(self.dir,'*.yml')):
			print 'fn', fn
			for doc in yaml.safe_load_all(file(fn)):
				self.load_item(doc)

	def load_item(self, doc):
		if doc is None: return

		_color = doc.get('color', None)
		if _color is None:
			_color = libtcod.green
		elif hasattr(_color, 'upper'):
			_color = getattr(libtcod, _color)
		else:
			_color = libtcod.Color(*_color)

		item_class = doc['item_class']
		module, clas = item_class.rsplit('.',1)
		module = __import__(module)
		item_class = getattr(module, clas)
		print 'item class:', item_class

		print 'loading', doc
		@Game.register_item_type(doc['spawn_chance'])
		class LoadedItem(item_class):
			name = doc.get('item_description')
			char = doc.get('char', '!')
			color = _color
			stack_limit = doc.get('stack_limit', Item.stack_limit)
			potency = doc.get('potency')
			distance = doc.get('distance')


@Game.register_item_type(10)
class HealingPotion(Item):
	name = 'Healing potion'
	char = '\x03'
	color = libtcod.violet
	potency = 15
	item_class = 'healing'
	def use(self):
		fighter = self.user.fighter

		result = True
		if fighter.hp == fighter.max_hp:
			self.game.message('Full health, can\'t heal', libtcod.red)
			result = False
		else:
			self.game.message('Healing...')
			fighter.heal(self.potency)

		return result

@Game.register_item_type(7)
class SuperHealingPotion(Item):
	name = 'Super healing potion'
	char = '\x03'
	color = libtcod.yellow
	probability = .5
	potency = 10
	item_class = 'healing'
	def use(self):
		fighter = self.user.fighter
		if random.random() < self.probability:
			fighter.max_hp += self.potency
		fighter.heal(self.potency)
		return True

@Game.register_item_type(1)
class Confusion(Item):
	name = 'Confusion'
	char = 'c'
	color=libtcod.dark_chartreuse
	item_class = 'monster defense'
	def use(self):
		monster = monsters.get_closest_monster(self.user)

		result = False
		if monster is not None:
			self.game.message('%s becomes confused' % monster.name)
			monsters.ConfusedMonster(random.randrange(10,18)).attach(
				monster
			)
			result = True

		return result

@Game.register_item_type(4)
class Strengthen(Item):
	name = 'Strengthen'
	char = 's'
	color = libtcod.chartreuse
	item_class = 'attack'
	potency = 20
	def use(self):
		if self.user.fighter:
			self.game.message('%s feels a surge of strength' % self.user.name)
			self.user.fighter.stat_adjust(self.potency, self.adj)
		return True

	def adj(self, owner):
		return (
			lambda _: self.game.message('The surge of strength has subsided'),
			owner.fighter.defense,
			owner.fighter.power+3
		)

@Game.register_item_type(4)
class Protect(Item):
	name = 'Protect'
	char = 'p'
	color = libtcod.chartreuse
	item_class = 'defense'
	potency = 15
	def use(self):
		if self.user.fighter:
			self.game.message('%s is surrounded by a protecting aura' % self.user.name)
			self.user.fighter.stat_adjust(self.potency, self.adj)
		return True

	def adj(self, owner):
		return (
			lambda _: self.game.message('The protecting aura dissipates'),
			owner.fighter.defense+6,
			owner.fighter.power
		)

@Game.register_item_type(2)
class LightningBolt(Item):
	name = 'Lightning Bolt'
	char = 'z'
	color = libtcod.darkest_red
	item_class = 'attack'
	potency = 13
	def use(self):
		monster = monsters.get_closest_monster(self.user)
		result = False
		if monster and self.user.can_see(monster.x, monster.y):
			self.game.message('Monster %s has been struck by lightning' % monster.name)
			monster.fighter.take_damage(self.potency)
			result = True
		else:
			self.game.message('No target')
		return result

@Game.register_item_type(5)
class Jump(Item):
	name = 'Jump'
	char = 'j'
	color= libtcod.dark_green
	distance = 3
	item_class = 'movement'
	def use(self):
		self.game.select(self.jump)
		return True
	def jump(self, x,y):
		dist = self.user.distance(x,y)

		if dist <= self.distance:
			self.user.x, self.user.y = x,y
			self.game.message('you are transported to a new place')
		elif random.random() < self.distance/dist:
			self.user.x, self.user.y = x,y
			self.game.message('you strain all your power to move %d squares' % int(dist))
		else:
			self.game.message('you didn\'t make it')
			self.user.fighter.take_damage( int(round(2 * dist/self.distance)) )

@Game.register_item_type(3)
class Acquire(Item):
	name = 'Acquire'
	char = 'a'
	color= libtcod.dark_green
	effect_distance = 5
	item_class = 'pickup'
	def use(self):
		self.game.message('what do you want?')
		self.game.select(self.get)
		return True
	def get(self, x,y):
		if self.user.distance(x,y) < self.effect_distance:
			self.user.pick_up(self.user.object_at(x,y))


@Game.register_item_type(1)
class Smite(Item):
	name = 'Smite'
	char = '\x0f'
	color = libtcod.red
	item_class = 'attack'
	potency = 10
	def use(self):
		self.game.select(self.smite)
		return True

	def smite(self, x,y):
		monster = monsters.monster_at(x,y)
		if monster:
			monster.fighter.take_damage(self.potency)
			if monster.fighter:
				self.game.message('%s is smitten, he only retains %s hp' % (monster.name, monster.fighter.hp))
			else:
				self.game.message('%s thought it better to go elsewhere' % monster.name)


@Game.register_item_type(2)
class Fireball(Item):
	name = 'Fireball'
	char = '*'
	color = libtcod.darker_red
	effect_radius = 5
	potency = (20,6)
	item_class = 'splash attack'

	def use(self):
		self.game.select(self.smite)
		return True

	def smite(self, x,y):
		if random.random() < .1:
			self.game.message('the fireball is amazingly effective', libtcod.green)
			self.effect_radius *= 2

		direct_damage, splash_damage = self.potency
		strikes = []
		for obj in self.owner.level.objects:
			if obj.fighter and obj is not self.user:
				if (obj.x, obj.y) == (x,y):
					self.game.message('%s takes a direct hit from the fireball' % obj.name)
					obj.fighter.take_damage(direct_damage)
				elif obj.distance(x,y) < self.effect_radius:
					obj.fighter.take_damage(splash_damage)
					if obj.fighter:
						strikes.append('%s %s' % (obj.name, obj.fighter.hp))
					else:
						strikes.append('%s dead' % obj.name)
		self.game.message('The names of those who were to close for comfort: %s' % ', '.join(strikes))

