library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

package types is
    constant MAX_SNAKE : integer := 1200;
    constant SNAKE_ADDRESS_SIZE : integer :=11;
    
    constant SPRITES_ADDRESS_SIZE : integer := 10;
    constant SPRITES_DATA_LENGTH : integer := 768;
    constant SPRITES_DATA_SIZE : integer := 24;
    
    constant HEAD_SPRITE_OFFSET : integer := 0;
    constant BODY_SPRITE_OFFSET : integer := 256;
    constant TAIL_SPRITE_OFFSET : integer := 512;
    
    type coord is array(0 to 39, 0 to 29) of unsigned(SNAKE_ADDRESS_SIZE-1 downto 0);
    type pos is record
        X: unsigned(9 downto 0);
        Y: unsigned(8 downto 0);
        dirX: signed(1 downto 0);
        dirY: signed(1 downto 0);
        isDefined: std_logic;
    end record;
    function to_stdlogicvector(snake : pos) return std_logic_vector;
    function to_pos(input : std_logic_vector) return pos;
    
    type color is record
        R : unsigned (4 downto 0);
        G : unsigned (5 downto 0);
        B : unsigned (4 downto 0);
        A : unsigned (7 downto 0);
    end record;
    function to_color(input : std_logic_vector) return color;

    type nSnakes is array(0 to 8) of pos;
    type addresses is array(0 to 8) of unsigned(SNAKE_ADDRESS_SIZE-1 downto 0);
    type std_logic_vector_array is array(natural range <>) of std_logic_vector;

    function to_addresses(input : std_logic_vector_array) return addresses;
    function to_pos(input : std_logic_vector_array) return nSnakes;
    
    function constrain(input : integer; min : integer; max : integer) return integer;
    function min_int(input : integer;val : integer) return integer;
    
    function pValue(c : color) return color;
end package;

package body types is
    function to_stdlogicvector(snake : pos) return std_logic_vector is
    --variable sortie : std_logic_vector(pos.X'length+pos.Y'length+pos.dirX'length+pos.dirY'length downto 0);
    begin
        return std_logic_vector(std_logic_vector(snake.X) & std_logic_vector(snake.Y) & std_logic_vector(snake.dirX) & std_logic_vector(snake.dirY) & snake.isDefined);
    end to_stdlogicvector;
    
    function to_pos(input : std_logic_vector) return pos is
    variable sortie : pos;
    variable offset : integer;
    begin
        --si on trouve une facon plus simple de deserialiser je suis preneur
        offset := 0;
        sortie.isDefined := input(0);
        offset := offset+1;
        sortie.dirY := signed(input(offset+sortie.dirY'length-1 downto offset));
        offset := offset+sortie.dirY'length;
        sortie.dirX := signed(input(offset+sortie.dirX'length-1 downto offset));
        offset := offset+sortie.dirX'length;
        sortie.Y := unsigned(input(offset+sortie.Y'length-1 downto offset));
        offset := offset+sortie.Y'length;
        sortie.X := unsigned(input(offset+sortie.X'length-1 downto offset));
        return sortie;
    end to_pos;
    
    function to_color(input : std_logic_vector) return color is
    variable sortie : color;
    begin
        sortie.R := unsigned(input(23 downto 19));
        sortie.G := unsigned(input(18 downto 13));
        sortie.B := unsigned(input(12 downto 8));
        sortie.A := unsigned(input(7 downto 0));
        return sortie;
    end to_color;

    function to_addresses(input : std_logic_vector_array) return addresses is
    variable sortie : addresses;
    begin
        for i in input'LOW to input'HIGH loop
            sortie(i) := unsigned(input(i));
        end loop;
        return sortie;
    end to_addresses;

    function to_pos(input : std_logic_vector_array) return nSnakes is
    variable sortie : nSnakes;
    begin
        for i in input'LOW to input'HIGH loop
            sortie(i) := to_pos(input(i));
        end loop;
        return sortie;
    end to_pos;
    
    function constrain(input : integer; min : integer; max : integer) return integer is
    variable output : integer;
    begin
        if(input < min) then
            output := min;
        elsif(input > max) then
            output := max;
        else
            output := input;
        end if;
        return output;
    end constrain;
    
    
    function min_int(input : integer;val : integer) return integer is
    variable output : integer;
    begin
        if(input > val) then
            output := val;
        else
            output := input;
        end if;
        return output;
    end min_int;
    
    function pValue(c : color) return color is
    variable output : color;
    begin
        output.R := unsigned(c.R and resize(shift_right(c.A,3),5));
        output.G := unsigned(c.G and resize(shift_right(c.A,2),6));
        output.B := unsigned(c.B and resize(shift_right(c.A,3),5));
        output.A := c.A;
        return output;
    end pValue;
    
end package body;
