scope OrganicConcatenation initializer init
	import Statistics
	import Units
	import SimError
	import Players
	import TerrainData
	import Math
	import SoundControl
	import RegisterPlayerUnitEvent

	native UnitAlive takes unit u returns boolean

	struct DuplicateDat
		real uX
		real uY
		real face
		real f0
		real f1
		real timeLeft
		real axis
		real dist
		unit image0
		unit image1
		unit image2
		unit image3
		unit tU
		unit friend
	end

	constant string IMAGE_SOUND = "Abilities\\Spells\\Orc\\MirrorImage\\MirrorImage.wav"

	constant integer CONCATENATION_ID  = 'A00K'
	constant integer BEAST_ILLUSION_ID = 'h00E'

	constant real DURATION         =    .5
	constant real RANGE            = 300.
	constant real PHASE_OFFSET     =  16.
	constant real CLOCK_PERIOD     =   1. / 30.
	constant real PHASE_MULTIPLIER =  16. * bj_PI

	DuplicateDat array ddStack
	integer ddIndex = -1
	timer clock = CreateTimer()

	function ddPeriodic takes nothing returns nothing
		integer index = 0
		integer id
		player owner
		DuplicateDat tempDat

		loop
			exitwhen index > ddIndex
			tempDat = ddStack[index]

			SetUnitX(tempDat.image0, tempDat.uX + (2. * tempDat.dist / 6. + PHASE_OFFSET) * Cos(tempDat.axis))
			SetUnitX(tempDat.image1, tempDat.uX + (1. * tempDat.dist / 6. + PHASE_OFFSET) * Cos(tempDat.axis))
			SetUnitX(tempDat.image2, tempDat.uX - (1. * tempDat.dist / 6. + PHASE_OFFSET) * Cos(tempDat.axis))
			SetUnitX(tempDat.image3, tempDat.uX - (2. * tempDat.dist / 6. + PHASE_OFFSET) * Cos(tempDat.axis))

			SetUnitY(tempDat.image0, tempDat.uY + (2. * tempDat.dist / 6. + PHASE_OFFSET) * Sin(tempDat.axis))
			SetUnitY(tempDat.image1, tempDat.uY + (1. * tempDat.dist / 6. + PHASE_OFFSET) * Sin(tempDat.axis))
			SetUnitY(tempDat.image2, tempDat.uY - (1. * tempDat.dist / 6. + PHASE_OFFSET) * Sin(tempDat.axis))
			SetUnitY(tempDat.image3, tempDat.uY - (2. * tempDat.dist / 6. + PHASE_OFFSET) * Sin(tempDat.axis))

			tempDat.timeLeft = tempDat.timeLeft - CLOCK_PERIOD
			if tempDat.timeLeft <= 0. then
				RemoveUnit(tempDat.image0)
				RemoveUnit(tempDat.image1)
				RemoveUnit(tempDat.image2)
				RemoveUnit(tempDat.image3)

				Statistics.beastsAlive--
				owner = GetOwningPlayer(tempDat.tU)
				id = GetPlayerId(owner)
				Units.hero[id]    = CreateUnit(owner, Units.BEAST_ID, tempDat.uX, tempDat.uY, tempDat.face)
				Units.subHero[id] = null
				Units.initializeBeast(Units.hero[id])

				SetWidgetLife(Units.hero[id], GetWidgetLife(tempDat.tU) + GetWidgetLife(tempDat.friend))

				if Players.locl == owner then
					SelectUnit(Units.hero[id], true)
				end

				RemoveUnit(tempDat.tU)
				RemoveUnit(tempDat.friend)
				destroy tempDat

				ddStack[index] = ddStack[ddIndex]
				ddIndex--
				index--

				if ddIndex == -1 then
					PauseTimer(clock)
				end
			end

			index++
		end
	end

	function act takes nothing returns nothing
		DuplicateDat dd
		player owner
		integer id
		real x1
		real y1
		real x2
		real y2
		real fDiff

		if GetSpellAbilityId() == CONCATENATION_ID then

			// check range
			id = GetPlayerId(GetOwningPlayer(GetTriggerUnit()))
			if not (IsUnitInRange(Units.hero[id], Units.subHero[id], RANGE) and UnitAlive(Units.hero[id]) and UnitAlive(Units.subHero[id]) and Units.countBalls(Units.hero[id]) > 2 and Units.countBalls(Units.subHero[id]) > 2) then
				simError(Players.fromId[id], "No suitable unit in range for concatenation")
				return
			end

			dd = new DuplicateDat()
			dd.tU     = Units.hero[id]
			dd.friend = Units.subHero[id]
			dd.timeLeft = DURATION
			owner = GetOwningPlayer(dd.tU)

			x1 = GetUnitX(dd.tU)
			x2 = GetUnitX(dd.friend)
			y1 = GetUnitY(dd.tU)
			y2 = GetUnitY(dd.friend)
			dd.dist = TerrainData.distanceBetweenXY(x1, x2, y1, y2)

			dd.uX = (x1 + x2)/2.
			dd.uY = (y1 + y2)/2.
			dd.f0 = GetUnitFacing(dd.tU)     * bj_DEGTORAD
			dd.f1 = GetUnitFacing(dd.friend) * bj_DEGTORAD
			fDiff =      Math.radiansNormalizeHalf(dd.f1 - dd.f0)
			dd.face =    Math.radiansNormalizeHalfModulo(dd.f0 - fDiff/2.)
			dd.axis = TerrainData.radiansBetweenXY(x1, x2, y1, y2)

			dd.image0 = CreateUnit(owner, BEAST_ILLUSION_ID, dd.uX + 2. * dd.dist / 6. * Cos(dd.axis), dd.uY + 2. * dd.dist / 6. * Sin(dd.axis), dd.f0 + 4. * fDiff / 5.)
			dd.image1 = CreateUnit(owner, BEAST_ILLUSION_ID, dd.uX + 1. * dd.dist / 6. * Cos(dd.axis), dd.uY + 1. * dd.dist / 6. * Sin(dd.axis), dd.f0 + 3. * fDiff / 5.)
			dd.image2 = CreateUnit(owner, BEAST_ILLUSION_ID, dd.uX - 1. * dd.dist / 6. * Cos(dd.axis), dd.uY - 1. * dd.dist / 6. * Sin(dd.axis), dd.f0 + 2. * fDiff / 5.)
			dd.image3 = CreateUnit(owner, BEAST_ILLUSION_ID, dd.uX - 2. * dd.dist / 6. * Cos(dd.axis), dd.uY - 2. * dd.dist / 6. * Sin(dd.axis), dd.f0 + 1. * fDiff / 5.)

			SoundControl.play3D(IMAGE_SOUND, 1., dd.uX, dd.uY, 0.)

			SetUnitVertexColor(dd.image0, 255, 255, 255, 128)
			SetUnitVertexColor(dd.image1, 255, 255, 255, 128)
			SetUnitVertexColor(dd.image2, 255, 255, 255, 128)
			SetUnitVertexColor(dd.image3, 255, 255, 255, 128)

			SetUnitTimeScale(dd.image0, 0.)
			SetUnitTimeScale(dd.image1, 0.)
			SetUnitTimeScale(dd.image2, 0.)
			SetUnitTimeScale(dd.image3, 0.)

			Units.dischargeBall(dd.tU)
			Units.dischargeBall(dd.tU)
			Units.dischargeBall(dd.tU)
			Units.dischargeBall(dd.friend)
			Units.dischargeBall(dd.friend)
			Units.dischargeBall(dd.friend)

			SetUnitFlyHeight(  dd.tU,     1000., 0.)
			SetUnitVertexColor(dd.tU,        0 , 0 , 0, 0)
			PauseUnit(dd.tU, true)
			SetUnitFlyHeight(  dd.friend, 1000., 0.)
			SetUnitVertexColor(dd.friend,    0 , 0 , 0, 0)
			PauseUnit(dd.friend, true)

			ddIndex++
			ddStack[ddIndex] = dd
			if ddIndex == 0 then
				TimerStart(clock, CLOCK_PERIOD, true, function ddPeriodic)
			end
		end
	end

	function init takes nothing returns nothing
		registerPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function act)
	end
endscope
