import PlayerUnit from '@/classes/player'
import CTUnit from '@/classes/ct'

import Walls from '@/classes/walls'

import Game from '@/core/game'
import vector from '@/core/pipe'
import { add, dot, unit, intersect } from '@/core/vector2'

const TIME_TO_HIDE_QUESTION_MARK = 2
const VIEW_ANGLE = 60 * Math.PI / 180

export default class Units {

    constructor (map) {

        this.map = map

        this.player = new PlayerUnit(map.playerPosition)

        this.ct = map.ctPositions.map(pos => {

            const unit = new CTUnit(pos)

            return {
                unit,
                hidden: true,
                lastVisible: {
                    time: 0,
                    position: [0, 0],
                    mark: '?'
                }
            }
        })


        const ctArray = this.ct
        Game.func('unitIntersect', (v1, v2) =>  {
            return ctArray.find(item => !item.unit.killed && this.isUnitIntersected(item.unit, v1, v2) ? item.unit : null)
        })

        Game.func('playerIntersect', (v1, v2) =>  {
            return this.isUnitIntersected(this.player, v1, v2) ? this.player : null
        })

        Game.func('getPlayerPosition', () =>  this.player.position)

        const isWallIntersected = Game.func('isIntersected')
        Game.func('isPlayerVisible', (unit) =>  {
            const a = vector(this.player.position).subtract(unit.position).unit().val()
            const angle = Math.acos(dot(unit.direction, a))
            const hidden = (angle > VIEW_ANGLE) || isWallIntersected(unit.position, this.player.position)
            return !hidden
        })

        Game.on('playerfire', () => {
            this.player.health -= 5

            if (this.player.health <= 0) {
                Game.emit('lose')
            }
        })

        Game.on('unitfire', enemy => {
            if (enemy === this.player) return
            enemy.unit.health = Math.max(enemy.unit.health - 30, 0)

            Game.emit('enemyhit')

            if (enemy.unit.health === 0) {
                enemy.unit.killed = true
                enemy.lastVisible = {
                    position: enemy.unit.position,
                    time: TIME_TO_HIDE_QUESTION_MARK,
                    mark: '×'
                }

                Game.emit('enemykilled')

                if (ctArray.filter(enemy => !enemy.unit.killed).length === 0) {
                    Game.emit('userwonround', 'enemykilled')
                }
            }
        })

        this.isWallIntersected = Game.func('isIntersected')
    }

    restart () {
        this.player.health = 100
        this.player.position = this.map.playerPosition
        this.player.weapon.bullets = []

        this.ct.forEach(ct, index => {
            ct.unit.health = 100
            ct.unit.position = this.map.ctPositions[index]
            ct.hidden = true
            ct.lastVisible = {
                time: 0,
                position: [0, 0],
                mark: '?'
            }

            ct.weapon.bullets = []
        })
    }

    isUnitIntersected (unit, v1, v2) {
        const _angle = vector(v1).subtract(v2).angle()
        const a = vector([0, - unit.radius]).rotate(_angle).add(unit.position).val()
        const b = vector([0, unit.radius]).rotate(_angle).add(unit.position).val()
        return intersect(a, b, v1, v2)
    }

    doActions (dt) {
        this.checkVisibility()
        this.player.update(dt)
        this.ct.forEach(enemy => {
            enemy.unit.update(dt)
            enemy.unit.move(dt)
        })
    }

    draw ({ctx, dt}) {
        this.doActions(dt)

        this.player.draw(ctx)

        this.ct.forEach(enemy => {

            if (!enemy.hidden && !enemy.unit.killed) {
                enemy.unit.draw(ctx)
            } else if (enemy.lastVisible.time > 0) {
                enemy.lastVisible.time -= dt
                this.drawSign(ctx, enemy.lastVisible)
            }
        })
    }

    drawSign (ctx, {position, time, mark}) {
        ctx.font = 'bold 12px sans-serif'
        ctx.fillStyle = 'rgba(255, 0, 0, ' + (time / TIME_TO_HIDE_QUESTION_MARK) + ')'
        ctx.textAlign = 'center'
        ctx.textBaseline = 'middle'
        ctx.fillText(mark, position[0], position[1])
    }

    getVisibleUnits (hero, units) {
        const isWallIntersected = Game.func('isIntersected')
        return units.filter(unit => {
            const guard_to_hero = vector(unit.position).subtract(hero.position).unit().val()
            const angle = Math.acos(dot(hero.direction, guard_to_hero))
            const hidden = (angle > VIEW_ANGLE) || isWallIntersected(hero.position, unit.position)
            return !hidden
        })
    }

    checkVisibility () {
        const liveUnits = this.ct.filter(ct => !ct.unit.killed)

        const visibleUnits = this.getVisibleUnits(
            this.player,
            liveUnits.map(ct => ct.unit)
        )

        liveUnits.forEach(enemy => {
            const hidden = !visibleUnits.includes(enemy.unit)

            if (!enemy.hidden && hidden) {
                enemy.lastVisible = {
                    position: enemy.unit.position,
                    time: TIME_TO_HIDE_QUESTION_MARK,
                    mark: '?'
                }
            }

            enemy.hidden = hidden
        })
    }
}
