# Concave
# Copyright (C) 2012 Daniel Malmqvist
# 
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


import random
import pygame
import helperfunctions
import constants
import g
from item import Item

class Enemy():
    def __init__(self, startX, startY, level, gameMap):
        self.updateindex = 0
        self.level = level
        self.buffs = []
        self.dots = []
        
        if self.level % 5 == 0:
            self.scale = 2
            self.str = 15 + (self.level * 3) 
            self.mag = 5 + (self.level * 10)
            self.vit = 10 + (self.level * 5)
        else:
            self.scale = 0
            self.str = 15 + (self.level * 2)
            self.mag = 5 + (self.level * 1)
            self.vit = 10 + (self.level * 2)
        
        self.str += random.randint(0,level*2)
        self.mag += random.randint(0,level*2)
        self.vit += random.randint(0,level*2)
        
        if self.level <= 10:
            self.maxHp = self.hp = (self.vit*10) / 2
            self.maxMp = self.mp = (self.mag*10) / 2
        elif self.level <= 20:
            self.maxHp = self.hp = (self.vit*10) / 1.5
            self.maxMp = self.mp = (self.mag*10) / 1.5
        else:
            self.maxHp = self.hp = (self.vit*10)
            self.maxMp = self.mp = (self.mag*10)
        
        
            
        
        
        self.gameMap = gameMap
        
        self.headlevel = 0
        self.bodylevel = 0
        self.legslevel = 0
        self.feetlevel = 0
        self.beltlevel = 0
        
        self.spreadLevelPoints(level+5)
        self.body = random.randint(0,1)
        if self.body == 1:
            self.type = random.choice([constants.DAGGER(), constants.LONGSWORD(), constants.RAPIER(), constants.SPEAR(), constants.STAFF(), constants.LONGSPEAR()])
        else:
            self.type = random.choice([constants.DAGGER(), constants.LONGSWORD(), constants.RAPIER()])
        
        self.path = []
        self.pathIndex = 0
        
        self.fixHitBox()
        
        self.x = startX-self.hitStopX
        self.y = startY-self.hitStopY
        
        self.clearImages()
        
        self.moving = True
        self.attacking = False
        self.direction = constants.SOUTH()
        
        self.imageIndex = 0
        self.imageWait = 5
        self.imageCurrentWait = 0
        
        self.dieIndex = 0
        self.alive = True
    
        self.removeThis = False
    
    
    def clearImages(self):
        self.walkSouth = []
        self.walkNorth = []
        self.walkEast = []
        self.walkWest = []
        self.attackSouth = []
        self.attackNorth = []
        self.attackEast = []
        self.attackWest = []
        self.hurt = []
    
    def takeDamage(self, hp, damageDealer):
        self.hp -= hp
        if self.hp <= 0:
            self.hp = 0
            self.die(damageDealer)
            
            
    
    def getHpLineWidth(self):
        lineWidth = self.hitStopX - self.hitStartX
        lineWidth = float(lineWidth)
        lineWidth *= (float(self.hp)/float(self.maxHp))
        
        return int (lineWidth)
    def getHitRect(self):
        if self.hp == 0:
            return pygame.Rect(-1,-1,0,0)
        return pygame.Rect(self.x+self.hitStartX, self.y+self.hitStartY, self.hitStopX-self.hitStartX, self.hitStopY-self.hitStartY)
        
    def fixHitBox(self):
        self.hitStartX = 16
        self.hitStopX = 48
        self.hitStartY = 48
        self.hitStopY = 64
        
        if self.scale <= -1:
            self.hitStartX /= 2
            self.hitStopX /= 2
            self.hitStartY /= 2
            self.hitStopY /= 2
        if self.scale <= -2:
            self.hitStartX /= 2
            self.hitStopX /= 2
            self.hitStartY /= 2
            self.hitStopY /= 2
        if self.scale >= 1:
            self.hitStartX *= 2
            self.hitStopX *= 2
            self.hitStartY *= 2
            self.hitStopY *= 2
        if self.scale >= 2:
            self.hitStartX *= 2
            self.hitStopX *= 2
            self.hitStartY *= 2
            self.hitStopY *= 2
            
    def die(self, damageDealer):
        self.imageIndex = 0
        self.attackEast = self.attackWest = self.attackSouth = self.attackNorth = [] 
        self.walkEast = self.walkNorth = self.walkSouth = self.walkWest = []
        
        damageDealer.getExp(self.level*self.level*3)
        
        # Change status to dead
        self.alive = False
        
        
        itemClassToDrop = [constants.BELT(), constants.FEET(), constants.HANDS(), constants.HEAD(), constants.LEGS(), constants.TORSO(), constants.WEAPON()]
        itemClassToDrop = random.choice(itemClassToDrop)
        itemDroped = 0
        
        levelPoints = random.randint(0,max(2*self.level,1))
        
        if g.getCurrentLevel() % 5 == 0:
            levelPoints *= 2
        
        if itemClassToDrop == constants.BELT() and self.beltlevel > 0:
            itemDroped = Item(self.x + self.hitStartX, self.y + self.hitStartY, constants.BELT(), self.beltlevel, levelPoints)
        elif itemClassToDrop == constants.FEET() and self.feetlevel > 0:
            itemDroped = Item(self.x + self.hitStartX, self.y + self.hitStartY, constants.FEET(), self.feetlevel, levelPoints)
        elif itemClassToDrop == constants.HEAD() and self.headlevel > 0:
            itemDroped = Item(self.x + self.hitStartX, self.y + self.hitStartY, constants.HEAD(), self.headlevel, levelPoints)
        elif itemClassToDrop == constants.LEGS() and self.legslevel > 0:
            itemDroped = Item(self.x + self.hitStartX, self.y + self.hitStartY, constants.LEGS(), self.legslevel, levelPoints)
        elif itemClassToDrop == constants.TORSO() and self.bodylevel > 0:
            itemDroped = Item(self.x + self.hitStartX, self.y + self.hitStartY, constants.TORSO(), self.bodylevel, levelPoints)
        elif itemClassToDrop == constants.WEAPON():
            itemDroped = Item(self.x + self.hitStartX, self.y + self.hitStartY, self.type, 0, levelPoints)
        
        
        if itemDroped != 0:
            g.addItem(itemDroped)
        
        # append blood to map
        
    def getDmg(self):
        return self.level * self.str
    
    def update(self, inRange, enemiesInRange = [], playerX = -1, playerY = -1):
        self.updateindex += 1
        

        if self.hp <= 0:
            return

        for d in self.dots:
            if not d.update():
                self.dots.remove(d)
        
        if self.hp <= 0:
            self.die(g.getPlayer())
        
        if self.attacking and self.updateindex%10 == 0:
            g.getPlayer().takeDamage(self.getDmg() / len(enemiesInRange))
        
        if inRange and self.inAttackRange():
            attackDirection = self.canAttack()
            if attackDirection:
                self.direction = attackDirection
                self.attacking = True
                self.moving = False
                self.path = []
                self.pathIndex = 0
                
                #print "should attack"
                return
            else:
                if self.x > playerX:
                    self.x -= 4
                else:
                    self.x += 4
                return
        if inRange and len(self.path) == self.pathIndex and not self.inAttackRange():
            self.y += 4
            

        self.attacking = False
        
        if inRange and self.pathIndex >= 5:
            self.findPath()
        elif inRange and self.pathIndex == 0:
            self.findPath()
        elif inRange and self.pathIndex == len(self.path):
            self.findPath()
        
        if len(self.path) > 0 and self.pathIndex != len(self.path):
            tileX = (self.x + self.hitStartX)/32
            tileY = (self.y + self.hitStartY)/32
            
            if len(self.path) <= self.pathIndex:
                self.path = []
                self.pathIndex = 0
            elif self.path[self.pathIndex] == (tileX, tileY):
                self.pathIndex += 1
            else:
                oldx = self.x
                oldy = self.y
                
                directions = []
                stepLength = 2
                
                (targetTileX, targetTileY) = self.path[self.pathIndex]
                
                if targetTileX > tileX: 
                    directions.append(constants.EAST())
                elif targetTileX < tileX:
                    directions.append(constants.WEST())
                
                if targetTileY > tileY: 
                    directions.append(constants.SOUTH())
                elif targetTileY < tileY:
                    directions.append(constants.NORTH())
                
                if len(directions) == 2:
                    self.direction = directions[0]
                    
                    if constants.EAST() in directions and constants.NORTH() in directions:
                        self.y -= stepLength
                        self.x += stepLength
                        
                        if self.canMoveTo(enemiesInRange):
                            return True
                        
                        self.y = oldy
                        self.x = oldx + stepLength
                        
                        if self.canMoveTo(enemiesInRange):
                            if not self.attacking:
                                self.direction = constants.EAST()
                            return True
                        
                        self.y = oldy - stepLength
                        self.x = oldx
                        
                        if self.canMoveTo(enemiesInRange):
                            if not self.attacking:
                                self.direction = constants.NORTH()
                            return True
                        
                        self.y = oldy
                        self.x = oldx
                        return False
                    elif constants.NORTH() in directions and constants.WEST() in directions:
                        self.y -= stepLength
                        self.x -= stepLength
                        if self.canMoveTo(enemiesInRange):
                            return True
                        
                        self.y = oldy
                        self.x = oldx - stepLength
                        
                        if self.canMoveTo(enemiesInRange):
                            if not self.attacking:
                                self.direction = constants.WEST()
                            return True
                        
                        self.y = oldy - stepLength
                        self.x = oldx
                        
                        if self.canMoveTo(enemiesInRange):
                            if not self.attacking:
                                self.direction = constants.NORTH()
                            return True
                        
                        self.y = oldy
                        self.x = oldx
                        return False
                    elif constants.SOUTH() in directions and constants.WEST() in directions:
                        self.y += stepLength
                        self.x -= stepLength
                        if self.canMoveTo(enemiesInRange):
                            return True
                        
                        self.y = oldy
                        self.x = oldx - stepLength
                        
                        if self.canMoveTo(enemiesInRange):
                            if not self.attacking:
                                self.direction = constants.WEST()
                            return True
                        
                        self.y = oldy + stepLength
                        self.x = oldx
                        
                        if self.canMoveTo(enemiesInRange):
                            if not self.attacking:
                                self.direction = constants.SOUTH()
                            return True
                        
                        self.y = oldy
                        self.x = oldx
                        return False
                    elif constants.SOUTH() in directions and constants.EAST() in directions:
                        self.y += stepLength
                        self.x += stepLength
                        if self.canMoveTo(enemiesInRange):
                            return True
                        
                        self.y = oldy
                        self.x = oldx + stepLength
                        
                        if self.canMoveTo(enemiesInRange):
                            if not self.attacking:
                                self.direction = constants.EAST()
                            return True
                        
                        self.y = oldy + stepLength
                        self.x = oldx
                        
                        if self.canMoveTo(enemiesInRange):
                            if not self.attacking:
                                self.direction = constants.SOUTH()
                            return True
                        
                        self.y = oldy
                        self.x = oldx
                        return False
                    
                elif len(directions) == 1:
                    self.moving = True
                    if not self.attacking:
                        self.direction = directions[0]
                    
                    if (directions[0] == constants.NORTH()):
                        self.y -= stepLength
                        if self.canMoveTo(enemiesInRange):
                            return True
                        else:
                            self.y += stepLength
                            return False
                        
                    elif (directions[0] == constants.SOUTH()):
                        self.y += stepLength
                        if self.canMoveTo(enemiesInRange):
                            return True
                        else:
                            self.y -= stepLength
                            return False
                    elif (directions[0] == constants.WEST()):
                        self.x -= stepLength
                        if self.canMoveTo(enemiesInRange):
                            return True
                        else:
                            self.x += stepLength
                            return False
                    elif (directions[0] == constants.EAST()):
                        self.x += stepLength
                        if self.canMoveTo(enemiesInRange):
                            return True
                        else:
                            self.x -= stepLength
                            return False
                    
                else:
                    print "jaha?"
                
        else:
            self.moving = False        
                
                
                
            
    def findPath(self):
        playerX = g.getPlayer().x + 4
        playerY = g.getPlayer().y - 12
        self.path = self.gameMap.astar(((self.x+self.hitStartX)/32, (self.y+self.hitStartY)/32), (playerX/32, playerY/32))
        self.pathIndex = 0
        
    def canMoveTo(self, bepa = 1):
        return True
        if self.getHitRect().colliderect(g.getPlayer().getHitRect()):
            return False
        
        return True
    
    def spreadLevelPoints(self, level):
        while (level > 0):
            if self.headlevel == 5 and self.bodylevel == 8 and self.legslevel == 3 and self.feetlevel == 2 and self.beltlevel == 2:
                return
            level -= 1
            rand = random.randint(0,4)
            if rand == 0:
                self.headlevel += 1
                if self.headlevel > 5:
                    self.headlevel = 5
                    level += 1
            elif rand == 1:
                self.bodylevel += 1
                if self.bodylevel > 8:
                    self.bodylevel = 8
                    level += 1

            elif rand == 2:
                self.legslevel += 1
                if self.legslevel > 3:
                    self.legslevel = 3
                    level += 1
            elif rand == 3:
                self.feetlevel += 1
                if self.feetlevel > 2:
                    self.feetlevel = 2
                    level += 1
            elif rand == 4:
                self.beltlevel += 1
                if self.beltlevel > 2:
                    self.beltlevel = 2
                    level += 1
            
    
    def loadImages(self):
        self.gridX = 64
        self.gridY = 64
        
        walkpath = "graphic/walkcycle/"
        
        if self.type == constants.DAGGER() or self.type == constants.LONGSWORD() or self.type == constants.RAPIER():
            attackpath = "graphic/slash/"
        #elif self.type == constants.LONGSPEAR() or self.type == constants.SPEAR() or self.type == constants.STAFF():
        else:
            attackpath = "graphic/thrust/"
        
        hurtPath = "graphic/hurt/"
        
        walksheet = self.scaleImage(self.generateImage(walkpath))
        
        attackSheet = self.generateImage(attackpath) 
        
        #attackSheet.blit(pygame.image.load(attackpath + "WEAPON_dagger.png"), (0,0))
        attackSheet = self.scaleImage(attackSheet)
        
        hurtSheet = self.scaleImage(self.generateImage(hurtPath))
        
        if self.scale == -2:
            self.gridX /= 4
            self.gridY /= 4
        elif self.scale == -1:
            self.gridX /= 2
            self.gridY /= 2
        elif self.scale == 1:
            self.gridX *= 2
            self.gridY *= 2
        elif self.scale == 2:
            self.gridX *= 4
            self.gridY *= 4
        
        for i in range(1, walksheet.get_width()/self.gridX):
            self.walkNorth.append(helperfunctions.loadImage(walksheet, i, 0, self.gridX, self.gridY))
            self.walkWest.append(helperfunctions.loadImage(walksheet, i, 1, self.gridX, self.gridY))
            self.walkSouth.append(helperfunctions.loadImage(walksheet, i, 2, self.gridX, self.gridY))
            self.walkEast.append(helperfunctions.loadImage(walksheet, i, 3, self.gridX, self.gridY))
        self.standSouth = helperfunctions.loadImage(walksheet, 0, 0, self.gridX, self.gridY)
        self.standNorth = helperfunctions.loadImage(walksheet, 0, 0, self.gridX, self.gridY)
        self.standEast = helperfunctions.loadImage(walksheet, 0, 0, self.gridX, self.gridY)
        self.standWest = helperfunctions.loadImage(walksheet, 0, 0, self.gridX, self.gridY)

        for i in range(1, attackSheet.get_width()/self.gridX):
            self.attackNorth.append(helperfunctions.loadImage(attackSheet, i, 0, self.gridX, self.gridY))
            self.attackWest.append(helperfunctions.loadImage(attackSheet, i, 1, self.gridX, self.gridY))
            self.attackSouth.append(helperfunctions.loadImage(attackSheet, i, 2, self.gridX, self.gridY))
            self.attackEast.append(helperfunctions.loadImage(attackSheet, i, 3, self.gridX, self.gridY))

        for i in range(1, hurtSheet.get_width()/self.gridX):
            self.hurt.append(helperfunctions.loadImage(hurtSheet, i, 0, self.gridX, self.gridY))
        
        
        attackSheet = 0
        walksheet = 0
        hurtSheet = 0
        
    def generateImage(self, path):
        sheet = 0
        if self.body == 0:
            sheet = pygame.image.load(path + "BODY_skeleton.png")
        else:
            sheet = pygame.image.load(path + "BODY_male.png")
        
        if self.bodylevel == 1:
            sheet.blit(pygame.image.load(path + "TORSO_leather_armor_shirt_white.png"), (0,0))
        elif self.bodylevel == 2:
            sheet.blit(pygame.image.load(path + "TORSO_robe_shirt_brown.png"), (0,0))
        elif self.bodylevel == 3:
            sheet.blit(pygame.image.load(path + "TORSO_leather_armor_torso.png"), (0,0))
        elif self.bodylevel == 4:
            sheet.blit(pygame.image.load(path + "TORSO_leather_armor_torso.png"), (0,0))
            sheet.blit(pygame.image.load(path + "TORSO_leather_armor_shoulders.png"), (0,0))
        elif self.bodylevel == 5:
            sheet.blit(pygame.image.load(path + "TORSO_chain_armor_jacket_purple.png"), (0,0))
        elif self.bodylevel == 6:
            sheet.blit(pygame.image.load(path + "TORSO_chain_armor_torso.png"), (0,0))
        elif self.bodylevel == 7:
            sheet.blit(pygame.image.load(path + "TORSO_plate_armor_torso.png"), (0,0))
        elif self.bodylevel == 8:
            sheet.blit(pygame.image.load(path + "TORSO_plate_armor_torso.png"), (0,0))
            sheet.blit(pygame.image.load(path + "TORSO_plate_armor_arms_shoulders.png"), (0,0))
        
        
        if self.legslevel == 1:
            sheet.blit(pygame.image.load(path + "LEGS_pants_greenish.png"), (0,0))
        elif self.legslevel == 2:
            sheet.blit(pygame.image.load(path + "LEGS_robe_skirt.png"), (0,0))
        elif self.legslevel == 3:
            sheet.blit(pygame.image.load(path + "LEGS_plate_armor_pants.png"), (0,0))
        
        if self.beltlevel == 1:
            sheet.blit(pygame.image.load(path + "BELT_rope.png"), (0,0))
        elif self.beltlevel == 2:
            sheet.blit(pygame.image.load(path + "BELT_leather.png"), (0,0))
        
        
        if self.feetlevel == 1:
            sheet.blit(pygame.image.load(path + "FEET_shoes_brown.png"), (0,0))
        elif self.feetlevel == 2:
            sheet.blit(pygame.image.load(path + "FEET_plate_armor_shoes.png"), (0,0))
        
        if self.headlevel == 1:
            sheet.blit(pygame.image.load(path + "HEAD_robe_hood.png"), (0,0))
        elif self.headlevel == 2:
            sheet.blit(pygame.image.load(path + "HEAD_leather_armor_hat.png"), (0,0))
        elif self.headlevel == 3:
            sheet.blit(pygame.image.load(path + "HEAD_chain_armor_helmet.png"), (0,0))
        elif self.headlevel == 4:
            sheet.blit(pygame.image.load(path + "HEAD_chain_armor_hood.png"), (0,0))
        elif self.headlevel == 5:
            sheet.blit(pygame.image.load(path + "HEAD_plate_armor_helmet.png"), (0,0))
        
        
        return sheet
    
    
    def scaleImage(self, image):
        if self.scale == -1:
            image = pygame.transform.scale(image, (image.get_width()/2, image.get_height()/2))
        elif self.scale == -2:
            image = pygame.transform.scale(image, (image.get_width()/4, image.get_height()/4))
        elif self.scale == 1:
            image = pygame.transform.scale2x(image)
        elif self.scale == 2:
            image = pygame.transform.scale2x(image)
            image = pygame.transform.scale2x(image)
        return image
    
    def getCurrentImage(self):
        if not self.alive:
            if self.dieIndex == len(self.hurt) -1:
                self.imageCurrentWait += 1
                if self.imageCurrentWait >= 30 and g.getCurrentLevel() != 5:
                    self.removeThis = True
                return self.hurt[len(self.hurt) - 1]

            self.imageCurrentWait += 1
            if self.imageCurrentWait == self.imageWait*2:
                self.imageCurrentWait = 0
                self.dieIndex += 1
            
            return self.hurt[self.dieIndex]
        
        if self.attacking:
            self.imageCurrentWait += 1
            if self.imageCurrentWait == self.imageWait:
                self.imageCurrentWait = 0
                self.imageIndex += 1

            if self.imageIndex >= len(self.attackNorth):
                self.imageIndex = 0
            
            if self.direction == constants.NORTH():
                return self.attackNorth[self.imageIndex]
            elif self.direction == constants.EAST():
                return self.attackEast[self.imageIndex]
            elif self.direction == constants.SOUTH():
                return self.attackSouth[self.imageIndex]
            elif self.direction == constants.WEST():
                return self.attackWest[self.imageIndex]

        if self.moving:
            self.imageCurrentWait += 1
            if self.imageCurrentWait == self.imageWait:
                self.imageCurrentWait = 0
                self.imageIndex += 1

            if self.imageIndex == len(self.walkNorth):
                self.imageIndex = 0
            
            if self.direction == constants.NORTH():
                return self.walkNorth[self.imageIndex]
            elif self.direction == constants.EAST():
                return self.walkEast[self.imageIndex]
            elif self.direction == constants.SOUTH():
                return self.walkSouth[self.imageIndex]
            elif self.direction == constants.WEST():
                return self.walkWest[self.imageIndex]
            self.moving = False
        else:
            if self.direction == constants.NORTH():
                return self.standNorth
            elif self.direction == constants.EAST():
                return self.standEast
            elif self.direction == constants.SOUTH():
                return self.standSouth
            elif self.direction == constants.WEST():
                return self.standWest

    def canAttack(self):
        player = g.getPlayer().getHitRect().move(16,0)
        
        if self.getHitRect().move(-32,0).inflate(8,8).colliderect(player):
            return constants.WEST()
        elif self.getHitRect().move(32,0).inflate(8,8).colliderect(player):
            return constants.EAST()
        elif self.getHitRect().move(0,-16).inflate(8,8).colliderect(player):
            return constants.NORTH()
        elif self.getHitRect().move(0,16).inflate(8,8).colliderect(player):
            return constants.SOUTH()
        
        return None
    def inAttackRange(self):
        if (self.type == constants.DAGGER() or self.type == constants.SPEAR() or self.type == constants.STAFF()):
            player = g.getPlayer().getHitRect().move(16,0)
            return self.getHitRect().inflate(32,16).colliderect(player)
        else:
            player = g.getPlayer().getHitRect().move(16,0)
            return self.getHitRect().inflate(64,32).colliderect(player)
        
        
        




