module game_core.basic;

import bubel.ecs.core;
import bubel.ecs.attributes;

import ecs_utils.math.vector;

import gui.attributes;

import ecs_utils.utils;

import app : launcher;

import bindbc.sdl;

struct CLocation
{
    mixin ECS.Component;

    alias value this;

    vec2 value = vec2(0);
}

struct CScale
{
    mixin ECS.Component;

    alias value this;///use component as it value

    vec2 value = vec2(16,16);
}

struct CRotation
{
    mixin ECS.Component;

    alias value this;///use component as it value

    float value = 0;
}

struct CDepth
{
    mixin ECS.Component;

    alias value this;

    short value;
}

struct CColor
{
    mixin ECS.Component;

    alias value this;

    @GUIColor uint value;
}

struct CSelected
{
    mixin ECS.Component;

    bool value = false;
}

struct CInput
{
    mixin ECS.Component;
}

struct CDamping
{
    mixin ECS.Component;

    alias value this;

    @GUIRange(0,9) byte value = 1;
}

struct CVelocity
{
    mixin ECS.Component;

    alias value this;

    vec2 value = vec2(0,0);
}

struct CVelocityFactor
{
    mixin ECS.Component;

    alias value this;

    vec2 value = vec2(1);
}


struct DampingSystem
{
    mixin ECS.System!32;

    struct EntitiesData
    {
        uint length;
        const (Entity)[] entity;
        @readonly CDamping[] damping;
        CVelocity[] velocity;
    }

    float[20] damp = 0;

    bool onBegin()
    {
        foreach(i;0..20)
        {
            damp[i] = powf((0.99 - cast(float)i * 0.01),launcher.delta_time*0.1);
        }

        return true;
    }

    void onUpdate(EntitiesData data)
    {
        foreach(i; 0..data.length)
        {
            data.velocity[i] = data.velocity[i] * damp[data.damping[i]];
        }
    }
}

struct MoveSystem
{
    mixin ECS.System!64;

    struct EntitiesData
    {
        uint length;
        CLocation[] location;
        @readonly CVelocity[] velocity;
        @optional @readonly CVelocityFactor[] vel_factor;
    }

    void onUpdate(EntitiesData data)
    {
        if(data.vel_factor)
        {
            foreach(i; 0..data.length)
            {
                data.location[i] += data.velocity[i] * data.vel_factor[i] * launcher.delta_time;
            }
        }
        else
        {
            foreach(i; 0..data.length)
            {
                data.location[i] += data.velocity[i] * launcher.delta_time;
            }
        }
    }
}

/**
*System is responsible for movement of objects with CInput component.
*In this example every entity has same speed when using movement system.
*/
struct InputMovementSystem
{
    mixin ECS.System!32;

    vec2 move_vector;

    struct EntitiesData
    {
        uint length;
        //read only components can be marked with @readonly attribute or with const expression instead 
        const (CInput)[] input;
        //components are treated as required by default
        //CLocation[] locations;
        CVelocity[] velocity;
        //CTexture[] textures;
    }

    /**
    *onBegin gives opportunity to check keys once and call update on entities only when
    *one key is pressed.
    */
    bool onBegin()
    {
        move_vector = vec2(0,0);
        if(launcher.getKeyState(SDL_SCANCODE_W))
        {
            move_vector += vec2(0,1);
        }
        else if(launcher.getKeyState(SDL_SCANCODE_S))
        {
            move_vector += vec2(0,-1);
        } 
        if(launcher.getKeyState(SDL_SCANCODE_A))
        {
            move_vector += vec2(-1,0);
        }
        else if(launcher.getKeyState(SDL_SCANCODE_D))
        {   
            move_vector += vec2(1,0);
        }

        if(move_vector.x != 0 ||move_vector.y != 0)
        {
            move_vector = move_vector / sqrtf(move_vector.x * move_vector.x + move_vector.y * move_vector.y);
            return true;
        }
        //don't call system update because no key pressed 
        return false;
    }

    /**
    *Update is called multiple times in one "manager.update()" call.
    *Number of "onUpdate" calls is count of buffers which must be updated during pass.
    *When multithreading is used, number of "onUpdate" calls can be greater due to fact that
    *JobSystem can split buffers for better data packing.
    */
    void onUpdate(EntitiesData data)
    {
        /*if(move_vector.x == 0)
        {
            foreach(i; 0..data.length)
            {
                data.textures[i].coords = vec4(0*px,80*px,48*px,32*px);
            }
            //return;
        }*/
        //move every entity using movement vector
        //if(move_vector.x != 0 || move_vector.y != 0)
        foreach(i; 0..data.length)
        {
            data.velocity[i] += move_vector * launcher.delta_time * 0.005;
            if(data.velocity[i].x > 0.5)data.velocity[i].x = 0.5;
            else if(data.velocity[i].x < -0.5)data.velocity[i].x = -0.5;
            if(data.velocity[i].y > 0.5)data.velocity[i].y = 0.5;
            else if(data.velocity[i].y < -0.5)data.velocity[i].y = -0.5;
            //data.locations[i].x += move_vector.x * launcher.delta_time * 0.25;
            //data.locations[i].y += move_vector.y * launcher.delta_time * 0.25;
            //if(move_vector.x > 0)data.textures[i].coords = vec4(48*px,80*px,48*px,32*px);
            //else data.textures[i].coords = vec4(0*px,80*px,48*px,32*px);
        }
        /*else 
        foreach(i; 0..data.length)
        {
            data.velocity[i] = vec2(0,0);
        }*/
    }
}