New wave always spawns with wrong amount of enemies / also does not delay

Essentially I am trying to make a game in which one enemy of type slow spawns for every round completed so far (# of slow enemies = wave #), and one enemy of type fast spawns for every even round thats been completed (#_of_fast_enemies++ every other wave). However, the game always spawns one slow enemy for the first two consecutive rounds, and then one slow and one fast enemy for every round after that.

    void Update()
    {
        enemyCount = GameObject.FindGameObjectsWithTag("Enemy").Length;
        if ((enemyCount == 0) && waveDone)
        { 
            spawnWave(waveCounter);

        }


        if (Input.GetKeyDown(KeyCode.LeftArrow))
        {
            SpawnLeftCannonBalls();
        }
        if (Input.GetKeyDown(KeyCode.RightArrow))
        {
            SpawnRightCannonBalls();
        }
    }



    private void spawnWave(int waveCount)
    {
        waveDone = false;
        StartCoroutine(WaveCooldown());
 

        int i = 0;
        int j = numOfFastEnemies;
        while (i < waveCounter)
        {
            Instantiate(slowEnemy, new Vector3(-6, 3.5f, 0), gameObject.transform.rotation);

            if (j > 0)
            {
                Instantiate(fastEnemy, new Vector3(-6, 3.5f, 0), gameObject.transform.rotation);
                j--;
            }
            i++;

            StartCoroutine(SpawnCooldown());
        }

        if (waveCounter % 2 == 0)
        {
            numOfFastEnemies++;
        }

        waveCounter++;
        waveDone = true;
    }

I am also trying to make it such that the waves begin spawning enemies after a delay, and that the time between enemies spawning during the wave is delayed so that the enemies will be staggered. I tried to implement this with Coroutines and IEnumerators and this is just simply not working (no delay for either case at run time).

Im not quite sure because some part of the Code is missing.

But to make it clear. The Coroutine will be waited for X Amount you defined as Return value. But only the corutine. All the other code Part which are before, after and the regular update will continue.

You can image it like a Parking. He will start the Coroutine do the stuff inside and then place it somewhere for X Secound.

If you want your code to be blocked, you can await with the await key word for the TIme. But that is a poor design choice.