#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#include <math.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>


#ifndef __nullptr__
#define Nullptr(type) (type *)0
#endif // __nullptr__

#include "vector.h"
#include "asteroids.h"

// TODO: Export all this to a scenery (For later implementation of enemy waves)

// Properties
const double BULLET_Speed_Ally = 12.0f;
const double BULLET_Speed_Enemy = 6.0f;
const int BULLET_DecayTime_Ally = 60;
const int BULLET_DecayTime_Enemy = 100;
const double SHIP_RotationValue = 4.0f;
const int SHIP_DeathAnimSpeed = 6;
const int SHIP_DeathAnimFrameCount = 5;
const double SHIP_MaxSpeed = 8.0f;
const double SHIP_Acceleration = 0.15f;
const double SHIP_ShootIntevale = 12;  // Based on frame count
const double ASTEROID_MinimumSize = 50.0f; // No asteroid under this size if split
// const int ENEMY_DeathAnimSpeed = 6;
// const int ENEMY_DeathAnimFrameCount = 5;
const double ENEMY_MaxSpeed = 5.0f;
const int ENEMY_MaxTurnIntervale = 300;
const double ENEMY_Acceleration = 0.15f;
const double ENEMY_ShootIntevale = 120; // Based on frame count
const SDL_Scancode SHIP_LeftButton = SDL_SCANCODE_A;
const SDL_Scancode SHIP_RightButton = SDL_SCANCODE_D;
const SDL_Scancode SHIP_ForwardButton = SDL_SCANCODE_W;
const SDL_Scancode SHIP_ShootButton = SDL_SCANCODE_SPACE;
// End Properties

// Vars
Bullet NULLET;
int GAME_BoxWidth, GAME_BoxHeight, SHIP_MaxBulletCount, ENEMY_MaxBulletCount; // TODO: Explosion Texture
SDL_Rect SHIP_SourceRect_Gliding, SHIP_SourceRect_Accelerating, * SHIP_SourceRect_Explosion, * ASTEROID_SourceRect_Size1, * ASTEROID_SourceRect_Size2, * ASTEROID_SourceRect_Size3;
SDL_Texture * SHIP_Texture_Gliding, * SHIP_Texture_Accelerating, * SHIP_Texture_Explosion, * ASTEROID_Texture, * BULLET_Texture_Ally, * BULLET_Texture_Enemy, * ENEMY_Texture;
SDL_Point SHIP_RotationCenter_Flight, SHIP_RotationCenter_Explosion;
Asteroid ASTEROIDULLET;
bool ASTEROID_IsDestroyed = false, BULLET_IsInit = false,  ENEMY_IsInit = false,  SHIP_IsInit = false,  ASTEROID_IsInit = false; // TODO: check if init successful
// End Vars

void ASTEROIDS_InitializeGame(SDL_Renderer * renderer, int width, int height){
    printf("Initializing Game...\n");
    GAME_BoxWidth = width;
    GAME_BoxHeight = height;
    SHIP_Initialize(renderer);
    BULLET_Initialize(renderer); // Call may be redundant
    ENEMY_Initialize(renderer);
    ASTEROID_Initialize(renderer);
    printf("Game initialized!\n");
}

void ASTEROIDS_DeinitializeGame(){
    printf("Make sure all resources are destroyed (not fatal)!\nDe-initializing Game...\n");
    SHIP_Deinitialize();
    BULLET_Deinitialize(); // Call may be redundant
    ENEMY_Deinitialize();
    ASTEROID_Deinitialize();
    printf("Game de-initialized!\n");
}

void BULLET_Initialize(SDL_Renderer * renderer){
    if (BULLET_IsInit) return;
    printf("Initializing Bullet...\n");
    NULLET = (Bullet) {
        .Rotation = 0.0f,
        .Location = (Vector) {.x = 0.0f, .y = 0.0f },
        .Momentum = (Vector) {.x = 0.0f, .y = 0.0f },
        .TargetRect = (SDL_Rect) {.x = 0, .y = 0, .w = 0, .h = 0 },
        .Lifetime = 0,
        .IsDestroyed = true,
        .Type = AllyBullet
    }; // Default or null bullet
    BULLET_Texture_Ally = IMG_LoadTexture(renderer, "assets/images/bullet.png");
    if (!BULLET_Texture_Ally) printf("Ally bullet texture cannot be loaded!\n");
    BULLET_Texture_Enemy = IMG_LoadTexture(renderer, "assets/images/enemyBullet.png");
    if (!BULLET_Texture_Enemy) printf("Enemy bullet texture cannot be loaded!\n");
    printf("Bullet initialized!\n");
    BULLET_IsInit = true;
}

void ENEMY_Initialize(SDL_Renderer * renderer){
    if (ENEMY_IsInit) return;
    printf("Initializing Enemy...\n");
    BULLET_Initialize(renderer);
    ENEMY_Texture = IMG_LoadTexture(renderer, "assets/images/enemy.png");
    if (!ENEMY_Texture) printf("Enemy texture cannot be loaded!\n");
    ENEMY_MaxBulletCount = ((BULLET_DecayTime_Ally / ENEMY_ShootIntevale) + 10);
    printf("Enemy initialized!\n");
    ENEMY_IsInit = true;
} // ENEMY_Initialize

void SHIP_Initialize(SDL_Renderer * renderer){
    if (SHIP_IsInit) return;
    printf("Initializing Ship...\n");
    BULLET_Initialize(renderer);
    SHIP_Texture_Gliding = IMG_LoadTexture(renderer, "assets/images/ship.png");
    if (!SHIP_Texture_Gliding) printf("Ship texture 1 cannot be loaded!\n");
    SHIP_Texture_Accelerating = IMG_LoadTexture(renderer, "assets/images/shipAcc.png");
    if (!SHIP_Texture_Accelerating) printf("Ship texture 2 cannot be loaded!\n");
    SHIP_Texture_Explosion = IMG_LoadTexture(renderer, "assets/images/shipExpl.png");
    if (!SHIP_Texture_Explosion) printf("Ship texture explosion cannot be loaded!\n");
    SHIP_SourceRect_Explosion = calloc(5, sizeof(SDL_Rect));
    if (!SHIP_SourceRect_Explosion) printf("Memory for SDL_Rect (SHIP) cannot be allocated!\n");
    int i;
    for (i = 0; i < SHIP_DeathAnimFrameCount; i++) {
        SHIP_SourceRect_Explosion[i] = (SDL_Rect) {.x = 0, .y = (i * 1632), .w = 1280, .h = 1632 };
    }
    SHIP_SourceRect_Gliding = (SDL_Rect) {.x = 0, .y = 0, .w = 320, .h = 408 }; // TODO: Sourcerects are currently redundant considering the usage of SDL_RenderCopyEx
    SHIP_SourceRect_Accelerating = (SDL_Rect) {.x = 0, .y = 0, .w = 320, .h = 576 };
    SHIP_RotationCenter_Flight = (SDL_Point) {.x = 20, .y = 25 };
    SHIP_MaxBulletCount = ((BULLET_DecayTime_Ally / SHIP_ShootIntevale) + 10);
    printf("Ship initialized!\n");
    SHIP_IsInit = true;
} // SHIP_Initialize

void ASTEROID_Initialize(SDL_Renderer * renderer){
    if (ASTEROID_IsInit) return;
    printf("Initializing Asteroid...\n");
    ASTEROID_Texture = IMG_LoadTexture(renderer, "assets/images/asteroid.png");
    ASTEROIDULLET = (Asteroid) {
        .Rotation = 0.0f,
        .Size = 0.0f,
        .RotationValue = 0.0f,
        .Location = (Vector) {.x = 0.0f, .y = 0.0f },
        .Momentum = (Vector) {.x = 0.0f, .y = 0.0f },
        .TargetRect = (SDL_Rect) {.x = 0, .y = 0, .w = 0, .h = 0 },
        .IsDestroyed = true,
        .Health = 0
    };
    if (!ASTEROID_Texture) printf("Asteroid texture cannot be loaded!\n");
    ASTEROID_SourceRect_Size1 = (SDL_Rect *)malloc(3 * sizeof(SDL_Rect));
    ASTEROID_SourceRect_Size1[0] = (SDL_Rect) {.x = 0, .y = 0, .w = 800, .h = 800 };
    ASTEROID_SourceRect_Size1[1] = (SDL_Rect) {.x = 0, .y = 800, .w = 800, .h = 800 };
    ASTEROID_SourceRect_Size1[2] = (SDL_Rect) {.x = 800, .y = 0, .w = 800, .h = 800 };
    ASTEROID_SourceRect_Size2 = (SDL_Rect *)malloc(3 * sizeof(SDL_Rect));
    ASTEROID_SourceRect_Size2[0] = (SDL_Rect) {.x = 800, .y = 800, .w = 400, .h = 400 };
    ASTEROID_SourceRect_Size2[1] = (SDL_Rect) {.x = 800, .y = 1200, .w = 400, .h = 400 };
    ASTEROID_SourceRect_Size2[2] = (SDL_Rect) {.x = 1200, .y = 800, .w = 400, .h = 400 };
    ASTEROID_SourceRect_Size3 = (SDL_Rect *)malloc(4 * sizeof(SDL_Rect));
    ASTEROID_SourceRect_Size3[0] = (SDL_Rect) {.x = 1200, .y = 1200, .w = 200, .h = 200 };
    ASTEROID_SourceRect_Size3[1] = (SDL_Rect) {.x = 1200, .y = 1400, .w = 200, .h = 200 };
    ASTEROID_SourceRect_Size3[2] = (SDL_Rect) {.x = 1400, .y = 1200, .w = 200, .h = 200 };
    ASTEROID_SourceRect_Size3[3] = (SDL_Rect) {.x = 1400, .y = 1400, .w = 200, .h = 200 };
    printf("Asteroid initialized!\n");
    ASTEROID_IsInit = true;
} // ASTEROID_Initialize

void BULLET_Deinitialize(){
    if (!BULLET_IsInit) return;
    printf("De-initializing Bullet...\n");
    printf("De-initializing ally bullet texture...\n");
    SDL_DestroyTexture(BULLET_Texture_Ally);
    printf("De-initializing enemy bullet texture...\n");
    SDL_DestroyTexture(BULLET_Texture_Enemy);
    printf("De-initializing Bullet finished!\n");
    BULLET_IsInit = false;
}

void ENEMY_Deinitialize(){
    if (!ENEMY_IsInit) return;
    printf("De-initializing Enemy class...\n");
    BULLET_Deinitialize();
    printf("De-initializing Enemy texture 1...\n");
    SDL_DestroyTexture(ENEMY_Texture);
    printf("De-initializing Enemy class finished!\n");
    ENEMY_IsInit = false;
}

void ASTEROID_Deinitialize(){
    if (!ASTEROID_IsInit) return;
    printf("De-initializing Asteroid...\n");
    printf("De-initializing Asteroid texture...\n");
    SDL_DestroyTexture(ASTEROID_Texture);
    free(ASTEROID_SourceRect_Size1);
    free(ASTEROID_SourceRect_Size2);
    free(ASTEROID_SourceRect_Size3);
    printf("De-initializing Asteroid finished!\n");
    ASTEROID_IsInit = false;
}

void SHIP_Deinitialize(){
    if (!SHIP_IsInit) return;
    printf("De-initializing Ship class...\n");
    BULLET_Deinitialize();
    free(SHIP_SourceRect_Explosion);
    printf("De-initializing Ship texture 1...\n");
    SDL_DestroyTexture(SHIP_Texture_Gliding);
    printf("De-initializing Ship texture 2...\n");
    SDL_DestroyTexture(SHIP_Texture_Accelerating);
    printf("De-initializing Ship texture 2...\n");
    SDL_DestroyTexture(SHIP_Texture_Explosion);
    printf("De-initializing Ship class finished!\n");
    SHIP_IsInit = false;
}

Bullet BULLET_Fire(Vector location, double direction, BulletType type){
    return (Bullet) {
               .Rotation = direction,
               .Location = location,
               .Momentum = getScaledDirectionalUnitVector(direction, (type == AllyBullet ? BULLET_Speed_Ally : BULLET_Speed_Enemy)),
               .TargetRect = (type == AllyBullet ? (SDL_Rect) {.x = 0, .y = 0, .w = 12, .h = 35 } : (SDL_Rect) {.x = 0, .y = 0, .w = 30, .h = 30 }),
               .Lifetime = (type == AllyBullet ? BULLET_DecayTime_Ally : BULLET_DecayTime_Enemy),
               .IsDestroyed = false,
               .Type = type
    };
}

void BULLET_UpdateAlly(Bullet * bul, Asteroid * asts, int * astCount, Enemy * enemies, int * enemyCount) {
    if (!(bul->IsDestroyed)) {
        if ((bul->Lifetime)-- <= 0) { // Decay
            (bul->IsDestroyed) = true;
            return;
        }
        (bul->Location) = vectorAdd((bul->Location), (bul->Momentum)); // Move
        RECT_CheckBoundaries(&(bul->Location), &(bul->TargetRect)); // Check Boundaries
        int i;
        for (i = 0; i < (*astCount); i++) { // Collide with Asteroids
            if (!(asts[i].IsDestroyed)) {
                if (RECT_CircleCollide(&(asts[i].TargetRect), &(bul->TargetRect))) {
                    printf("Bullet is destroyed by asteroid no. %d!\n", i + 1);
                    ASTEROID_TryDestroy(asts, astCount, i);
                    if ((asts[i].IsDestroyed)) printf("Bullet destroyed asteroid no. %d!\n", i + 1);
                    (bul->IsDestroyed) = true;
                    break;
                }
            }
        }
    }
} // BULLET_Update

void BULLET_UpdateEnemy(Bullet * bul, Ship * shp, int * shpCount) {
    if (!(bul->IsDestroyed)) {
        if ((bul->Lifetime)-- <= 0) { // Decay
            (bul->IsDestroyed) = true;
            return;
        }
        // TODO: Export triple use of boundary check to method using pointers
        (bul->Location) = vectorAdd((bul->Location), (bul->Momentum)); // Move
        if ((bul->Location).x < 0.0f) (bul->Location).x = GAME_BoxWidth; // Check Boundaries
        else if ((bul->Location).x > GAME_BoxWidth) (bul->Location).x = 0.0f;
        if ((bul->Location).y < 0.0f) (bul->Location).y = GAME_BoxHeight;
        else if ((bul->Location).y > GAME_BoxHeight) (bul->Location).y = 0.0f;
        (bul->TargetRect).x = (int)round((bul->Location).x); // Update render Target
        (bul->TargetRect).y = (int)round((bul->Location).y);
        int i;
        for (i = 0; i < (*shpCount); i++) { // Collide with Asteroids
            if (!(shp->IsDead)) {
                if (RECT_CircleCollide(&(shp[i].TargetRect_Gliding), &(bul->TargetRect))) {
                    printf("Player %d is destroyed by bullet!\n", i + 1);
                    (bul->IsDestroyed) = true;
                    (shp[i].IsDead) = true;
                    break;
                }
            }
        }
    }
} // BULLET_Update

void BULLET_Draw(Bullet * bul, SDL_Renderer * renderer) {
    if (!(bul->IsDestroyed)) {
        if ((bul->Type) == AllyBullet) {
            SDL_RenderCopyEx(renderer, BULLET_Texture_Ally, NULL, &(bul->TargetRect), (bul->Rotation), NULL, SDL_FLIP_NONE);
        } else {
            SDL_RenderCopyEx(renderer, BULLET_Texture_Enemy, NULL, &(bul->TargetRect), (bul->Rotation), NULL, SDL_FLIP_NONE);
        }
    }
}

Asteroid ASTEROID_CreateRandom(){
    return (Asteroid) {
               .Rotation = (double)(rand() % 360),
               .Size = 200,
               .RotationValue = fmod((double)(rand()), 4.0f) - 2.0f,
               // TODO: Reimplement random start pos here later
               // .Location = (_vector) {.x = (double)(rand() % GAME_BoxWidth), .y = (double)(rand() % GAME_BoxHeight) },
               .Location = (Vector) {.x = 0, .y = 0 },
               .Momentum = getScaledDirectionalUnitVector((double)(rand() % 360), fmod((double)rand(), 2.0f) + 1.0f),
               .TargetRect = (SDL_Rect) {.x = 0, .y = 0, .w = 200, .h = 200 },
               .IsDestroyed = false,
               .SourceRect = ASTEROID_SourceRect_Size1[(rand() % 3)],
               .Health = 4
    };
}

bool ASTEROID_TryDestroy(Asteroid * asts, int * astCount, int index){
    asts[index].Health--;
    if (asts[index].Health <= 0) {
        if (ASTEROID_CanSplit(&(asts[index]))) {
            printf("Asteroid %d was split...\n", index);
            asts[(*astCount)++] = ASTEROID_CreateFromSplit(&(asts[index]));
            Asteroid tmp =  ASTEROID_CreateFromSplit(&(asts[index]));
            asts[index] = tmp;
        } else {
            printf("Asteroid %d was destroyed...\n", index);
            asts[index].IsDestroyed = true;
        }
        return true;
    } else {
        printf("Asteroid %d was hit...\n", index);
    }
    return false;
}

Asteroid ASTEROID_CreateFromSplit(Asteroid * creator){
    Asteroid aster = (Asteroid) {
        .Rotation = (double)(rand() % 360),
        .Size = (creator->Size) / 2.0f,
        .RotationValue = fmod((double)(rand()), 4.0f) - 2.0f,
        .Location = creator->Location,
        .Momentum = getScaledDirectionalUnitVector((double)(rand() % 360), fmod((double)rand(), 2.0f) + 1.0f),
        .TargetRect = (SDL_Rect) {.x = 0, .y = 0, .w = (int)round((creator->Size) / 2.0f), .h = (int)round((creator->Size) / 2.0f) },
        .IsDestroyed = false,
        .SourceRect = ASTEROID_SourceRect_Size1[0],
        .Health = 0
    };

    aster.Health = (int)round(aster.Size / 50.0f) + ((rand() % 2) ? (1) : (-1)); // Bei 200 3 - 5 Hits
    if (aster.Health <= 1) aster.Health = 1;
    if (aster.Size > 150.0f) aster.SourceRect = ASTEROID_SourceRect_Size1[(rand() % 3)];
    else if (aster.Size > 75.0f) aster.SourceRect = ASTEROID_SourceRect_Size2[(rand() % 3)];
    else aster.SourceRect = ASTEROID_SourceRect_Size3[(rand() % 4)];
    return aster;
}

bool ASTEROID_CanSplit(Asteroid * ast){
    return ((ast->Size) >= (2 * ASTEROID_MinimumSize));
}

bool RECT_CircleCollide(SDL_Rect * rect1, SDL_Rect * rect2){
    return (distance(rect1, rect2) < round(((double)(rect1->w + rect2->w)) / 2.0f));
}

void RECT_CheckBoundaries(Vector * location, SDL_Rect * Target){
    if ((location->x) < -(Target->w)) (location->x) = GAME_BoxWidth; // Check Boundaries
    else if ((location->x) > GAME_BoxWidth) (location->x) = -(Target->w);
    if ((location->y) < -(Target->h)) (location->x) = GAME_BoxHeight;
    else if ((location->y) > GAME_BoxHeight) (location->y) = -(Target->h);
    (Target->x) = (int)round((location->x));
    (Target->y) = (int)round((location->y));
}

double distance(SDL_Rect * r1, SDL_Rect * r2){
    double dx = (((double)(r2->x) + ((double)(r2->w) / 2.0f)) - ((double)(r1->x) + ((double)(r1->w) / 2.0f)));
    double dy = (((double)(r2->y) + ((double)(r2->h) / 2.0f)) - ((double)(r1->y) + ((double)(r1->h) / 2.0f)));

    return round(sqrt((dx * dx ) + (dy * dy)));
}

void ASTEROID_Update(Asteroid * ast){
    if (!(ast->IsDestroyed)) {
        (ast->Rotation) += (ast->RotationValue); // Rotate
        (ast->Location) = vectorAdd(ast->Location, ast->Momentum); // Update Position
        RECT_CheckBoundaries(&(ast->Location), &(ast->TargetRect)); // Reset position if object exits window
    }
} // ASTEROID_Update

void ASTEROID_Draw(Asteroid * ast, SDL_Renderer * renderer){
    if (!(ast->IsDestroyed)) {
        SDL_RenderCopyEx(renderer, ASTEROID_Texture, &(ast->SourceRect), &(ast->TargetRect), ast->Rotation, NULL, SDL_FLIP_NONE); // Draw Image
    }
}

Enemy ENEMY_GetDefault(){
    Enemy temp;

    temp.Bullets = (Bullet *)malloc(ENEMY_MaxBulletCount * sizeof(Bullet));
    int i;
    for (i = 0; i < ENEMY_MaxBulletCount; i++) {
        temp.Bullets[i] = NULLET; // Nullet is set only if BULLET_Initialize() was called!
    }
    temp.Location = (Vector) {.x = (double)(rand() % GAME_BoxWidth), .y = (double)(rand() % GAME_BoxHeight) };
    temp.Momentum = getScaledDirectionalUnitVector((rand() % 360), fmod((double)rand(), ENEMY_MaxSpeed / 2.0f) + (ENEMY_MaxSpeed / 2.0f));
    temp.TargetRect = (SDL_Rect) {.x = 0, .y = 0, .w = 80, .h = 40 };
    temp.WeaponCooldown = ENEMY_ShootIntevale;
    temp.lastBulletIndex = 0;
    temp.IsDead = false;
    temp.TurnCooldown = (rand() % ENEMY_MaxTurnIntervale);

    return temp;
} // ENEMY_CreateDefault

void ENEMY_DestroyObject(Enemy * enm){
    printf("De-initializing Enemy...\n");
    printf("De-initializing Bullet-array...\n");
    free((enm->Bullets));
    printf("De-initializing Enemy...\n");
}

void ENEMY_Update(Enemy * enm, Ship * targets, int targetCount){
    if (!(enm->IsDead)) {
        int i;
        for (i = 0; i < ENEMY_MaxBulletCount; i++) { // Update Bullets
            BULLET_UpdateEnemy(&((enm->Bullets)[i]), targets, &targetCount);
        }

        if ((enm->TurnCooldown)-- <= 0) {
            (enm->TurnCooldown) = (rand() % ENEMY_MaxTurnIntervale);
            (enm->Momentum) = getScaledDirectionalUnitVector((rand() % 360), fmod((double)rand(), ENEMY_MaxSpeed / 2.0f) + (ENEMY_MaxSpeed / 2.0f));
        }

        (enm->Location) = vectorAdd((enm->Location), (enm->Momentum)); // Update Position
        RECT_CheckBoundaries(&(enm->Location), &(enm->TargetRect)); // Check Boundaries

        (enm->TargetRect).x = (int)round((enm->Location).x);
        (enm->TargetRect).y = (int)round((enm->Location).y);

        if ((enm->WeaponCooldown) <= 0) { // Shoot instantly if cooldown ends
            (enm->WeaponCooldown) = ENEMY_ShootIntevale;
            (enm->lastBulletIndex)++;
            if ((enm->lastBulletIndex) >= ENEMY_MaxBulletCount) (enm->lastBulletIndex) = 0;
            Vector startPoint = vectorAdd((enm->Location), (Vector) {.x = (double)((enm->TargetRect).w) / 2.0f, .y = (double)((enm->TargetRect).h) / 2.0f });
            (enm->Bullets)[(enm->lastBulletIndex)] = BULLET_Fire(startPoint, vectorRotation(vectorSub((targets[rand() % targetCount].Location), (enm->Location))), EnemyBullet); // Maybe check if bullet space is already taken from another bullet being fired...
        }
        (enm->WeaponCooldown)--;
    } else {
        (enm->DeadTime)++;
    }
} // ENEMY_Update

void ENEMY_Draw(Enemy * enm, SDL_Renderer * renderer){
    if (!(enm->IsDead)) {
        int i;
        for (i = 0; i < ENEMY_MaxBulletCount; i++) { // Draw Bullets
            BULLET_Draw(&((enm->Bullets)[i]), renderer);
        }

        SDL_RenderCopyEx(renderer, ENEMY_Texture, NULL, &(enm->TargetRect), 0.0f, NULL, SDL_FLIP_NONE); // Draw Image
    }
}

Ship SHIP_CreateDefault(){
    Ship temp;

    temp.Bullets = (Bullet *)malloc(SHIP_MaxBulletCount * sizeof(Bullet));
    int i;
    for (i = 0; i < SHIP_MaxBulletCount; i++) {
        temp.Bullets[i] = NULLET; // Nullet is set only if BULLET_Initialize() was called!
    }
    temp.Rotation = 0.0f;
    temp.FacingDirection = (Vector) {.x = 0.0f, .y = 0.0f };
    temp.Location = (Vector) {.x = 800.0f, .y = 450.0f };
    temp.Momentum = (Vector) {.x = 0.0f, .y = 0.0f };
    temp.TargetRect_Gliding = (SDL_Rect) {.x = 0, .y = 0, .w = 40, .h = 51 };
    temp.TargetRect_Accelerating = (SDL_Rect) {.x = 0, .y = 0, .w = 40, .h = 74 };
    temp.TargetRect_Explosion = (SDL_Rect) {.x = 0, .y = 0, .w = 160, .h = 204 };
    temp.WeaponCooldown = 0;
    temp.DeadTime = 0;
    temp.lastBulletIndex = 0;
    temp.IsDead = false;

    return temp;
} // SHIP_CreateDefault

void SHIP_DestroyObject(Ship * shp){
    printf("De-initializing Ship...\n");
    printf("De-initializing Bullet-array...\n");
    free((shp->Bullets));
    printf("De-initializing Ship...\n");
}

void SHIP_Update(Ship * shp, Uint8 * keystate, Asteroid * asts, int * astCount, Enemy * enemies, int * enemyCount){
    if (!(shp->IsDead)) {
        (shp->TurnLeftButtonPressed) = keystate[SHIP_LeftButton];
        (shp->TurnRightButtonPressed) = keystate[SHIP_RightButton];
        (shp->ForwardButtonPressed) = keystate[SHIP_ForwardButton];
        (shp->ShootButtonPressed) = keystate[SHIP_ShootButton];

        int i;
        for (i = 0; i < SHIP_MaxBulletCount; i++) { // Update Bullets
            BULLET_UpdateAlly(&((shp->Bullets)[i]), asts, astCount, enemies, enemyCount);
        }

        if (((shp->TurnLeftButtonPressed) + (shp->TurnRightButtonPressed)) == 1) { // Handle rotation
            if ((shp->TurnLeftButtonPressed)) { // Rotate to the left
                (shp->Rotation) -= SHIP_RotationValue;
            } else if ((shp->TurnRightButtonPressed)) { // Rotate to the right
                (shp->Rotation) += SHIP_RotationValue;
            }
            (shp->FacingDirection) = getScaledDirectionalUnitVector((shp->Rotation), SHIP_Acceleration);
        }

        if ((shp->ForwardButtonPressed)) { // Handle forward acceleration
            (shp->Momentum) = vectorAdd((shp->FacingDirection), (shp->Momentum));
            if (vectorMagnitude((shp->Momentum)) > SHIP_MaxSpeed) {
                (shp->Momentum) = vectorScaleTo((shp->Momentum), SHIP_MaxSpeed);
            }
        }

        (shp->Location) = vectorAdd((shp->Location), (shp->Momentum)); // Update Position
        RECT_CheckBoundaries(&(shp->Location), &(shp->TargetRect_Gliding)); // Check Boundaries
        (shp->TargetRect_Accelerating).x = (shp->TargetRect_Gliding).x;
        (shp->TargetRect_Accelerating).y = (shp->TargetRect_Gliding).y;

        if ((shp->ShootButtonPressed)) { // Fire bullet
            if ((shp->WeaponCooldown) <= 0) {
                (shp->WeaponCooldown) = SHIP_ShootIntevale;
                (shp->lastBulletIndex)++;
                if ((shp->lastBulletIndex) >= SHIP_MaxBulletCount) (shp->lastBulletIndex) = 0;
                (shp->Bullets)[(shp->lastBulletIndex)] = BULLET_Fire(vectorAdd((shp->Location), (Vector) {.x = (double)(SHIP_RotationCenter_Flight.x) / 2.0f, .y = (double)(SHIP_RotationCenter_Flight.y) / 2.0f }), (shp->Rotation), AllyBullet); // Maybe check if bullet space is already taken from another bullet being fired...
            }
        }
        (shp->WeaponCooldown)--;

        for (i = 0; i < (*astCount); i++) {
            if (RECT_CircleCollide(&(asts[i].TargetRect), &(shp->TargetRect_Gliding))) {
                printf("Ship is destroyed by asteroid no. %d!\n", i + 1);
                (shp->IsDead) = true;
                (shp->TargetRect_Explosion).x = ((int)round((shp->Location).x) - ((shp->TargetRect_Explosion).w / 4));
                (shp->TargetRect_Explosion).y = ((int)round((shp->Location).y) - ((shp->TargetRect_Explosion).h / 4));
                break;
            }
        }
    } else {
        (shp->DeadTime)++;
    }
} // SHIP_Update

void SHIP_Draw(Ship * shp, SDL_Renderer * renderer){
    if (!(shp->IsDead)) {
        int i;
        for (i = 0; i < SHIP_MaxBulletCount; i++) { // Draw Bullets
            BULLET_Draw(&((shp->Bullets)[i]), renderer);
        }

        if ((shp->ForwardButtonPressed))
            SDL_RenderCopyEx(renderer, SHIP_Texture_Accelerating, &SHIP_SourceRect_Accelerating, &(shp->TargetRect_Accelerating), (shp->Rotation), &SHIP_RotationCenter_Flight, SDL_FLIP_NONE);       // Draw Image
        else
            SDL_RenderCopyEx(renderer, SHIP_Texture_Gliding, &SHIP_SourceRect_Gliding, &(shp->TargetRect_Gliding), (shp->Rotation), NULL, SDL_FLIP_NONE);                                             // Draw Image
    } else if ((shp->DeadTime) < SHIP_DeathAnimSpeed * SHIP_DeathAnimFrameCount) {
        SDL_RenderCopyEx(renderer, SHIP_Texture_Explosion, &(SHIP_SourceRect_Explosion[((shp->DeadTime) / SHIP_DeathAnimSpeed)]), &(shp->TargetRect_Explosion), (shp->Rotation), NULL, SDL_FLIP_NONE); // Draw Image
    }
}
