---@diagnostic disable: lowercase-global, undefined-doc-name, undefined-field, undefined-global
---attempts to count the array but will spit out an error if it cannot
---@param array any Array to count
function tryTableLength(array)
    pcall(tableLength,array)
end

---Count a table
---@param Array any
---@return unknown
function tableLength(Array)
    count = 0;
    for index, value in ipairs(Array) do
        count = count + 1
    end
    return count
end

---Check if a ship are in the table
---@param ship ship --Ship to check
---@param arrayTable table --Array to search through
---@return boolean --Return if ship is in table
function shipInArrayTable(ship,arrayTable)
    for index, value in ipairs(arrayTable) do
        for index,value in ipairs(value) do
            if ship == value then
                return true
            end
        end
    end
    return false
end



---Function that spawns ship for everyone to use Although the ship is returned wait 0.05 seconds before giving orders to these ships (this is done as a performance saving measure)
---@param name string -- Name for spawned ship # used to seperate from displayed and non displayed parts of the name
---@param class string -- Class to Spawn
---@param primaryTable table -- Primary weapons to use. Use nil for no weapon changes
---@param secondaryTable table -- Secondary Weapons to use. Use nil for no weapon changes
---@param tertiaryTable table -- Tertiary weapons to use. Use nil for no weapon changes
---@param orientation orientation -- Orientation for ship that is made
---@param vector vector -- Location for ship to spawn
---@param team string -- Team for ship
---@param runOrders boolean -- Remove choosable orders for the player
---@return ship ship -- Ship Created
function createShip(name,class,primaryTable,secondaryTable,tertiaryTable,orientation,vector,team,runOrders)
	newShip = mn.createShip(name,tb.ShipClasses[class],orientation,vector )
	async.run(function()
		if newShip ~= nil then
			newShip.Team = mn.Teams[team]
			local banks = newShip.PrimaryBanks

			if primaryTable ~= nil then
				for i=1, #banks do
					local primary = randomElement(primaryTable)
					local bank = banks[i]
					bank.WeaponClass = tb.WeaponClasses[primary]
					bank.AmmoLeft = 0
				end
			end
			async.await(mn.waitAsync(0.01))
			if secondaryTable ~= nil then
				banks = newShip.SecondaryBanks
				for i=1, #banks do
					local secondary = randomElement(secondaryTable)
					local bank = banks[i]
					bank.WeaponClass = tb.WeaponClasses[secondary]
					bank.AmmoLeft = bank.AmmoMax
				end
			end

			async.await(mn.waitAsync(0.01))
			if tertiary ~= nil then
				banks = newShip.TertiaryBanks
				for i=1, #banks do
					local bank = banks[i]
					local tertiary = randomElement(tertiaryTable)
					bank.WeaponClass = tb.WeaponClasses[tertiary]
					bank.AmmoLeft = bank.AmmoMax
				end
			end

			async.await(mn.waitAsync(0.01))
			if runOrders == false then
				mn.runSEXP("( set-player-orders !"..newShip.Name.."! (false) !Destroy my target! !Disable my target! !Disarm my target! !Protect my target! !Ignore my target! !Form on my wing! !Cover me! !Engage enemy! !Capture my target! !Rearm me! !Abort rearm! !Stay near me! !Stay near my target! !Keep safe! !Depart! !Destroy subsystem!)")
			end

			async.await(mn.waitAsync(0.01))
			newShip:giveOrder(ORDER_ATTACK_ANY)
			if RougeMainSystems.Ships.Passives[newShip.Class.Name] then
				RougeMainSystems.Ships.Passives[newShip.Class.Name](newShip)
			end
		end
	end)
	return newShip
end

---This function creates and handles capital ships (apply orders to them after creation with the return function)
---@param name string --Name of ship in question
---@param class string --Type of ship
---@param orientation orientation --orientation of ship
---@param vector vector --Location for ship spawn
---@param team team --Team for ship to spawn on
---@param Orders bool --Does the ship accept orders?
---@return ship --Ship Spawned
function createCapitalShip(name,class,orientation,vector,team,Orders)
	newShip = mn.createShip(name,tb.ShipClasses[class],orientation,vector )
	if newShip ~= nil then
		newShip.Team = mn.Teams[team]
		if Orders == false then
			mn.runSEXP("( set-player-orders !"..newShip.Name.."! (false) !Destroy my target! !Disable my target! !Disarm my target! !Protect my target! !Ignore my target! !Form on my wing! !Cover me! !Engage enemy! !Capture my target! !Rearm me! !Abort rearm! !Stay near me! !Stay near my target! !Keep safe! !Depart! !Destroy subsystem!)")
		end
	end
	return newShip
end

---This function spawns drones from a parent object (i.e. missiles)
---@param parentObject object -- Object of parent
---@param name string -- Name of ship to spawn
---@param class string -- Class of ship to spawn (i.e. loki)
---@param primary any -- Primary weapon to use. Use nil for no weapon changes
---@param secondary any -- Primary weapon to use. Use nil for no weapon changes
---@param tertiary any -- Primary weapon to use. Use nil for no weapon changes
---@return unknown
function droneSpawn(parentObject,name,class,primary,secondary,tertiary)
	local newShip = mn.createShip(name,tb.ShipClasses[class],parentObject.Orientation,parentObject.Position)
	if newShip ~= nil then
		async.run(function()
			newShip.Team = parentObject.Team
			local banks = newShip.PrimaryBanks

			if primary ~= nil then
				for i=1, #banks do
					local bank = banks[i]
					bank.WeaponClass = tb.WeaponClasses[primary]
					bank.AmmoLeft = 0
				end
			end

			if secondary ~= nil then
				banks = newShip.SecondaryBanks
				for i=1, #banks do
					local bank = banks[i]
					bank.WeaponClass = tb.WeaponClasses[secondary]
					bank.AmmoLeft = bank.AmmoMax
				end
			end

			if tertiary ~= nil then
				banks = newShip.TertiaryBanks
				for i=1, #banks do
					local bank = banks[i]
					bank.WeaponClass = tb.WeaponClasses[tertiary]
					bank.AmmoLeft = bank.AmmoMax
				end
			end

			mn.runSEXP("( set-player-orders !"..newShip.Name.."! (false) !Destroy my target! !Disable my target! !Disarm my target! !Protect my target! !Ignore my target! !Form on my wing! !Cover me! !Engage enemy! !Capture my target! !Rearm me! !Abort rearm! !Stay near me! !Stay near my target! !Keep safe! !Depart! !Destroy subsystem!)")

			newShip:giveOrder(ORDER_ATTACK_ANY)
			end)
		return newShip
	end
	return "Failure"
end

---This function reloads the ammo on secondary banks
---@param ship ship The ship to rearm
function reArmShip(ship)
	if ship:isValid() then
		banks = ship.SecondaryBanks
		for i=1, #banks do
			local bank = banks[i]
			bank.AmmoLeft = bank.AmmoMax
		end
	end
end

---This function returns an element from a table
---@param tb any Table to select random element from
---@return any element element selected
function randomElement(tb)
    local keys = {}
    for k in pairs(tb) do table.insert(keys, k) end
    return tb[keys[math.random(#keys)]]
end

---This function creates a missile that flys away from the ship and is allied to the ship it was launched from
---@param ship ship Ship for missile to spawn from
---@param weapon string Missile type launched
---@param parentShip ship Ship to call Iridescent Parent
function spawnMissile(ship,weapon,parentShip)
    local mat = ba.createRandomVector():getOrientation()
    local newIri = mn.createWeapon(tb.WeaponClasses[weapon], mat, ship.Position + mat:getFvec()*20) -- + (mat:getFvec() * 10))
    newIri.Parent = parentShip
    newIri.Team = parentShip.Team
end

---Mine Function Checks for a class of weapon and scans for detonation if there is something within 1000m of them and creates a number of mine missiles upon detonation
---@param mineClass string Weapon that is the mine
---@param distance number Distance From Target to detonate
---@param mineMissile string Weapon launched from the mine
---@param detonationNumber number Number of missiles to launch from said mine
function mineFunction(mineClass,distance,mineMissile,detonationNumber)
	--Get List of weapons
	local tempShips = {}
	for i=1 , #mn.Ships do
		tempShips[i] = mn.Ships[i]
	end

	for i=1, #mn.Weapons do
		local weapon = mn.Weapons[i]
		if weapon.Class.Name == mineClass then
			for i=1, #tempShips do
				local weaponPosition = weapon.Position
				local weaponParent = weapon.Parent
				if weapon.Team:attacks(tempShips[i].Team) and weaponPosition:getDistance(tempShips[i].Position) < distance then
					weapon.LifeLeft = 0;
					async.run(function()
						for i=1,detonationNumber do
							spawnMissileMine(weaponPosition,mineMissile,weaponParent)
							async.await(mn.waitAsync(0.1))
						end
					end)
					break
				end
			end
		end
	end
end

---This function creates a missile from a weapon (Used in Mines but could theoretically be used in anything involving weapons)
---@param position vector Ship for missile to spawn from
---@param weapon string Missile type launched
---@param parent ship Ship that owns the mine
function spawnMissileMine(position,weapon,parent)
    local mat = ba.createRandomVector():getOrientation()
    local newIri = mn.createWeapon(tb.WeaponClasses[weapon], mat, position + mat:getFvec()*20) -- + (mat:getFvec() * 10))
    newIri.Parent = parent
    newIri.Team = parent.Team
end