"""
Example showing how we can interact with spritelist using shaders.

We simply hijack the position buffer of the spritelist and
fill it with new data generated by a transform shader.

If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.gl.spritelist_interaction_hijack_positions
"""

import math
import arcade
from arcade import hitbox

NUM_COINS = 500


class HijackSpritePositions(arcade.Window):

    def __init__(self):
        super().__init__(1280, 720, "Hijack Sprite Positions", resizable=True)
        # Generate lots of coins. We don't care about the initial positions
        # since our shader is setting those
        self.coins = arcade.SpriteList()
        texture = arcade.load_texture(
            ":resources:images/items/coinGold.png",
            hit_box_algorithm=hitbox.algo_bounding_box,
        )
        for _ in range(NUM_COINS):
            self.coins.append(arcade.Sprite(texture, scale=0.5))
        # Ensure the internal buffers are up to date (size etc)
        self.coins.write_sprite_buffers_to_gpu()

        # This shader simply generate some new positions
        self.position_program = self.ctx.program(
            vertex_shader="""
            #version 330

            // The current time to add some movement
            uniform float time;
            // The "bendyness" value accelerating rotations
            uniform float bend;

            // The current size of the screen
            uniform vec2 size;

            // The new positions we are writing into a new buffer
            out vec3 out_pos;

            void main() {
                // gl_VertexID is the sprite position in the spritelist.
                // We can use that to value to create unique positions with
                // some simple math.
                float vertId = float(gl_VertexID);
                out_pos = vec3(size, 0.0) / 2.0 + vec3(
                    sin(vertId + time + vertId * bend),
                    cos(vertId + time + vertId * bend),
                    0.0
                ) * vertId;
            }
            """
        )
        self.position_program["size"] = self.get_size()

    def on_draw(self):
        self.clear()

        # Write the new positions directly into the position
        # buffer of the spritelist. A little bit rude, but it works.
        self.coins.geometry.transform(
            self.position_program,
            self.coins.buffer_positions,
            vertices=len(self.coins),
        )
        self.coins.draw()

    def on_update(self, delta_time: float):
        # Keep updating the current time to animation the movement
        self.position_program["time"] = self.time / 4
        # Update the "bendyness" value
        self.position_program["bend"] = math.cos(self.time) / 400

    def on_resize(self, width: int, height: int):
        super().on_resize(width, height)
        self.position_program["size"] = width, height


HijackSpritePositions().run()
