require 'app/lowrez.rb'

def tick args
  # How to set the background color
  args.lowrez.background_color = [255, 255, 255]

  # ==== HELLO WORLD ======================================================
  # Steps to get started:
  # 1. ~def tick args~ is the entry point for your game.
  # 2. There are quite a few code samples below, remove the "##"
  #    before each line and save the file to see the changes.
  # 3. 0,  0 is in bottom left and 63, 63 is in top right corner.
  # 4. Be sure to come to the discord channel if you need
  #    more help: [[http://discord.dragonruby.org]].

  # Commenting and uncommenting code:
  # - Add a "#" infront of lines to comment out code
  # - Remove the "#" infront of lines to comment out code

  # Invoke the hello_world subroutine/method
  hello_world args # <---- add a "#" to the beginning of the line to stop running this subroutine/method.
  # =======================================================================


  # ==== HOW TO RENDER A LABEL ============================================
  # Uncomment the line below to invoke the how_to_render_a_label subroutine/method.
  # Note: The method is defined in this file with the signature ~def how_to_render_a_label args~
  #       Scroll down to the method to see the details.

  # Remove the "#" at the beginning of the line below
  # how_to_render_a_label args # <---- remove the "#" at the begging of this line to run the method
  # =======================================================================


  # ==== HOW TO RENDER A FILLED SQUARE (SOLID) ============================
  # Remove the "#" at the beginning of the line below
  # how_to_render_solids args
  # =======================================================================


  # ==== HOW TO RENDER AN UNFILLED SQUARE (BORDER) ========================
  # Remove the "#" at the beginning of the line below
  # how_to_render_borders args
  # =======================================================================


  # ==== HOW TO RENDER A LINE =============================================
  # Remove the "#" at the beginning of the line below
  # how_to_render_lines args
  # =======================================================================


  # == HOW TO RENDER A SPRITE =============================================
  # Remove the "#" at the beginning of the line below
  # how_to_render_sprites args
  # =======================================================================


  # ==== HOW TO MOVE A SPRITE BASED OFF OF USER INPUT =====================
  # Remove the "#" at the beginning of the line below
  # how_to_move_a_sprite args
  # =======================================================================


  # ==== HOW TO ANIMATE A SPRITE (SEPERATE PNGS) ==========================
  # Remove the "#" at the beginning of the line below
  # how_to_animate_a_sprite args
  # =======================================================================


  # ==== HOW TO ANIMATE A SPRITE (SPRITE SHEET) ===========================
  # Remove the "#" at the beginning of the line below
  # how_to_animate_a_sprite_sheet args
  # =======================================================================


  # ==== HOW TO DETERMINE COLLISION =============================================
  # Remove the "#" at the beginning of the line below
  # how_to_determine_collision args
  # =======================================================================


  # ==== HOW TO CREATE BUTTONS ==================================================
  # Remove the "#" at the beginning of the line below
  # how_to_create_buttons args
  # =======================================================================


  # ==== The line below renders a debug grid, mouse information, and current tick
  render_debug args
end

def hello_world args
  args.lowrez.solids  << { x: 0, y: 64, w: 10, h: 10, r: 255 }

  args.lowrez.labels  << {
    x: 32,
    y: 63,
    text: "lowrezjam 2020",
    size_enum: LOWREZ_FONT_SM,
    alignment_enum: 1,
    r: 0,
    g: 0,
    b: 0,
    a: 255,
    font: LOWREZ_FONT_PATH
  }

  args.lowrez.sprites << {
    x: 32 - 10,
    y: 32 - 10,
    w: 20,
    h: 20,
    path: 'sprites/lowrez-ship-blue.png',
    a: Kernel.tick_count % 255,
    angle: Kernel.tick_count % 360
  }
end


# =======================================================================
# ==== HOW TO RENDER A LABEL ============================================
# =======================================================================
def how_to_render_a_label args
  # NOTE: Text is aligned from the TOP LEFT corner

  # Render an EXTRA LARGE/XL label (remove the "#" in front of each line below)
  args.lowrez.labels << { x: 0, y: 57, text: "Hello World",
                         size_enum: LOWREZ_FONT_XL,
                         r: 0, g: 0, b: 0, a: 255,
                         font: LOWREZ_FONT_PATH }

  # Render a LARGE/LG label (remove the "#" in front of each line below)
  args.lowrez.labels << { x: 0, y: 36, text: "Hello World",
                          size_enum: LOWREZ_FONT_LG,
                          r: 0, g: 0, b: 0, a: 255,
                          font: LOWREZ_FONT_PATH }

  # Render a MEDIUM/MD label (remove the "#" in front of each line below)
  args.lowrez.labels << { x: 0, y: 20, text: "Hello World",
                          size_enum: LOWREZ_FONT_MD,
                          r: 0, g: 0, b: 0, a: 255,
                          font: LOWREZ_FONT_PATH }

  # Render a SMALL/SM label (remove the "#" in front of each line below)
  args.lowrez.labels << { x: 0, y: 9, text: "Hello World",
                          size_enum: LOWREZ_FONT_SM,
                          r: 0, g: 0, b: 0, a: 255,
                          font: LOWREZ_FONT_PATH }

  # You are provided args.lowrez.default_label which returns a Hash that you
  # can ~merge~ properties with
  # Example 1
  args.lowrez.labels << args.lowrez
                            .default_label
                            .merge(text: "Default")

  # Example 2
  args.lowrez.labels << args.lowrez
                            .default_label
                            .merge(x: 31,
                                   text: "Default",
                                   r: 128,
                                   g: 128,
                                   b: 128)
end

## # =============================================================================
## # ==== HOW TO RENDER FILLED SQUARES (SOLIDS) ==================================
## # =============================================================================
def how_to_render_solids args
  # Render a red square at 0, 0 with a width and height of 1
  args.lowrez.solids << { x: 0, y: 0, w: 1, h: 1, r: 255, g: 0, b: 0, a: 255 }

  # Render a red square at 1, 1 with a width and height of 2
  args.lowrez.solids << { x: 1, y: 1, w: 2, h: 2, r: 255, g: 0, b: 0, a: 255 }

  # Render a red square at 3, 3 with a width and height of 3
  args.lowrez.solids << { x: 3, y: 3, w: 3, h: 3, r: 255, g: 0, b: 0 }

  # Render a red square at 6, 6 with a width and height of 4
  args.lowrez.solids << { x: 6, y: 6, w: 4, h: 4, r: 255, g: 0, b: 0 }
end

## # =============================================================================
## # ==== HOW TO RENDER UNFILLED SQUARES (BORDERS) ===============================
## # =============================================================================
def how_to_render_borders args
  # Render a red square at 0, 0 with a width and height of 3
  args.lowrez.borders << { x: 0, y: 0, w: 3, h: 3, r: 255, g: 0, b: 0, a: 255 }

  # Render a red square at 3, 3 with a width and height of 3
  args.lowrez.borders << { x: 3, y: 3, w: 4, h: 4, r: 255, g: 0, b: 0, a: 255 }

  # Render a red square at 5, 5 with a width and height of 4
  args.lowrez.borders << { x: 7, y: 7, w: 5, h: 5, r: 255, g: 0, b: 0, a: 255 }
end

## # =============================================================================
## # ==== HOW TO RENDER A LINE ===================================================
## # =============================================================================
def how_to_render_lines args
  # Render a horizontal line at the bottom
  args.lowrez.lines << { x: 0, y: 0, x2: 63, y2:  0, r: 255 }

  # Render a vertical line at the left
  args.lowrez.lines << { x: 0, y: 0, x2:  0, y2: 63, r: 255 }

  # Render a diagonal line starting from the bottom left and going to the top right
  args.lowrez.lines << { x: 0, y: 0, x2: 63, y2: 63, r: 255 }
end

## # =============================================================================
## # == HOW TO RENDER A SPRITE ===================================================
## # =============================================================================
def how_to_render_sprites args
  # Loop 10 times and create 10 sprites in 10 positions
  # Render a sprite at the bottom left with a width and height of 5 and a path of 'sprites/lowrez-ship-blue.png'
  10.times do |i|
    args.lowrez.sprites << {
      x: i * 5,
      y: i * 5,
      w: 5,
      h: 5,
      path: 'sprites/lowrez-ship-blue.png'
    }
  end

  # Given an array of positions create sprites
  positions = [
    { x: 10, y: 42 },
    { x: 15, y: 45 },
    { x: 22, y: 33 },
  ]

  positions.each do |position|
    # use Ruby's ~Hash#merge~ function to create a sprite
    args.lowrez.sprites << position.merge(path: 'sprites/lowrez-ship-red.png',
                                          w: 5,
                                          h: 5)
  end
end

## # =============================================================================
## # ==== HOW TO ANIMATE A SPRITE (SEPERATE PNGS) ==========================
## # =============================================================================
def how_to_animate_a_sprite args
  # STEP 1: Define when you want the animation to start. The animation in this case will start in 3 seconds
  start_animation_on_tick = 180

  # STEP 2: Get the frame_index given the start tick.
  sprite_index = start_animation_on_tick.frame_index count: 7,     # how many sprites?
                                                     hold_for: 4,  # how long to hold each sprite?
                                                     repeat: true  # should it repeat?

  # STEP 3: frame_index will return nil if the frame hasn't arrived yet
  if sprite_index
    # if the sprite_index is populated, use it to determine the sprite path and render it
    sprite_path  = "sprites/explosion-#{sprite_index}.png"
    args.lowrez.sprites << { x: 0, y: 0, w: 64, h: 64, path: sprite_path }
  else
    # if the sprite_index is nil, render a countdown instead
    countdown_in_seconds = ((start_animation_on_tick - Kernel.tick_count) / 60).round(1)

    args.lowrez.labels  << args.lowrez
                               .default_label
                               .merge(x: 32,
                                      y: 32,
                                      text: "Count Down: #{countdown_in_seconds}",
                                      alignment_enum: 1)
  end

  # render the current tick and the resolved sprite index
  args.lowrez.labels  << args.lowrez
                               .default_label
                               .merge(x: 0,
                                      y: 11,
                                      text: "Tick: #{Kernel.tick_count}")
  args.lowrez.labels  << args.lowrez
                               .default_label
                               .merge(x: 0,
                                      y: 5,
                                      text: "sprite_index: #{sprite_index}")
end

## # =============================================================================
## # ==== HOW TO ANIMATE A SPRITE (SPRITE SHEET) =================================
## # =============================================================================
def how_to_animate_a_sprite_sheet args
  # STEP 1: Define when you want the animation to start. The animation in this case will start in 3 seconds
  start_animation_on_tick = 180

  # STEP 2: Get the frame_index given the start tick.
  sprite_index = start_animation_on_tick.frame_index count: 7,     # how many sprites?
                                                     hold_for: 4,  # how long to hold each sprite?
                                                     repeat: true  # should it repeat?

  # STEP 3: frame_index will return nil if the frame hasn't arrived yet
  if sprite_index
    # if the sprite_index is populated, use it to determine the source rectangle and render it
    args.lowrez.sprites << {
      x: 0,
      y: 0,
      w: 64,
      h: 64,
      path:  "sprites/explosion-sheet.png",
      source_x: 32 * sprite_index,
      source_y: 0,
      source_w: 32,
      source_h: 32
    }
  else
    # if the sprite_index is nil, render a countdown instead
    countdown_in_seconds = ((start_animation_on_tick - Kernel.tick_count) / 60).round(1)

    args.lowrez.labels  << args.lowrez
                               .default_label
                               .merge(x: 32,
                                      y: 32,
                                      text: "Count Down: #{countdown_in_seconds}",
                                      alignment_enum: 1)
  end

  # render the current tick and the resolved sprite index
  args.lowrez.labels  << args.lowrez
                               .default_label
                               .merge(x: 0,
                                      y: 11,
                                      text: "tick: #{Kernel.tick_count}")
  args.lowrez.labels  << args.lowrez
                               .default_label
                               .merge(x: 0,
                                      y: 5,
                                      text: "sprite_index: #{sprite_index}")
end

## # =============================================================================
## # ==== HOW TO STORE STATE, ACCEPT INPUT, AND RENDER SPRITE BASED OFF OF STATE =
## # =============================================================================
def how_to_move_a_sprite args
  args.lowrez.labels << args.lowrez
                            .default_label
                            .merge(x: 32,
                                   y: 62, text: "Use Arrow Keys",
                                   alignment_enum: 1)

  args.lowrez.labels << args.lowrez
                            .default_label
                            .merge(x: 32,
                                   y: 56, text: "Use WASD",
                                   alignment_enum: 1)

  args.lowrez.labels << args.lowrez
                            .default_label
                            .merge(x: 32,
                                   y: 50, text: "Or Click",
                                   alignment_enum: 1)

  # set the initial values for x and y using ||= ("or equal operator")
  args.state.ship.x ||= 0
  args.state.ship.y ||= 0

  # if a mouse click occurs, update the ship's x and y to be the location of the click
  if args.lowrez.mouse_click
    args.state.ship.x = args.lowrez.mouse_click.x
    args.state.ship.y = args.lowrez.mouse_click.y
  end

  # if a or left arrow is pressed/held, decrement the ships x position
  if args.lowrez.keyboard.left
    args.state.ship.x -= 1
  end

  # if d or right arrow is pressed/held, increment the ships x position
  if args.lowrez.keyboard.right
    args.state.ship.x += 1
  end

  # if s or down arrow is pressed/held, decrement the ships y position
  if args.lowrez.keyboard.down
    args.state.ship.y -= 1
  end

  # if w or up arrow is pressed/held, increment the ships y position
  if args.lowrez.keyboard.up
    args.state.ship.y += 1
  end

  # render the sprite to the screen using the position stored in args.state.ship
  args.lowrez.sprites << {
    x: args.state.ship.x,
    y: args.state.ship.y,
    w: 5,
    h: 5,
    path: 'sprites/lowrez-ship-blue.png',
    # parameters beyond this point are optional
    angle: 0, # Note: rotation angle is denoted in degrees NOT radians
    r: 255,
    g: 255,
    b: 255,
    a: 255
  }
end

# =======================================================================
# ==== HOW TO DETERMINE COLLISION =======================================
# =======================================================================
def how_to_determine_collision args
  # Render the instructions
  args.lowrez.labels << args.lowrez
                            .default_label
                            .merge(x: 32,
                                   y: 62, text: "Click Anywhere",
                                   alignment_enum: 1)

  # if a mouse click occurs:
  # - set ship_one if it isn't set
  # - set ship_two if it isn't set
  # - otherwise reset ship one and ship two
  if args.lowrez.mouse_click
    # is ship_one set?
    if !args.state.ship_one
      args.state.ship_one = { x: args.lowrez.mouse_click.x - 10,
                              y: args.lowrez.mouse_click.y - 10,
                              w: 20,
                              h: 20 }
    # is ship_one set?
    elsif !args.state.ship_two
      args.state.ship_two = { x: args.lowrez.mouse_click.x - 10,
                              y: args.lowrez.mouse_click.y - 10,
                              w: 20,
                              h: 20 }
    # should we reset?
    else
      args.state.ship_one = nil
      args.state.ship_two = nil
    end
  end

  # render ship one if it's set
  if args.state.ship_one
    # use Ruby's .merge method which is available on ~Hash~ to set the sprite and alpha
    # render ship one
    args.lowrez.sprites << args.state.ship_one.merge(path: 'sprites/lowrez-ship-blue.png', a: 100)
  end

  if args.state.ship_two
    # use Ruby's .merge method which is available on ~Hash~ to set the sprite and alpha
    # render ship two
    args.lowrez.sprites << args.state.ship_two.merge(path: 'sprites/lowrez-ship-red.png', a: 100)
  end

  # if both ship one and ship two are set, then determine collision
  if args.state.ship_one && args.state.ship_two
    # collision is determined using the intersect_rect? method
    if args.state.ship_one.intersect_rect? args.state.ship_two
      # if collision occurred, render the words collision!
      args.lowrez.labels << args.lowrez
                            .default_label
                            .merge(x: 31,
                                   y: 5,
                                   text: "Collision!",
                                   alignment_enum: 1)
    else
      # if collision occurred, render the words no collision.
      args.lowrez.labels << args.lowrez
                            .default_label
                            .merge(x: 31,
                                   y: 5,
                                   text: "No Collision.",
                                   alignment_enum: 1)
    end
  else
    # if both ship one and ship two aren't set, then render --
      args.lowrez.labels << args.lowrez
                            .default_label
                            .merge(x: 31,
                                   y: 6,
                                   text: "--",
                                   alignment_enum: 1)
  end
end

## # =============================================================================
## # ==== HOW TO CREATE BUTTONS ==================================================
## # =============================================================================
def how_to_create_buttons args
  # Define a button style
  args.state.button_style = { w: 62, h: 10, r: 80, g: 80, b: 80 }
  args.state.label_style  = { r: 80, g: 80, b: 80 }

  # Render instructions
  args.state.button_message ||= "Press a Button!"
  args.lowrez.labels << args.lowrez
                            .default_label
                            .merge(args.state.label_style)
                            .merge(x: 32,
                                   y: 62,
                                   text: args.state.button_message,
                                   alignment_enum: 1)


  # Creates button one using a border and a label
  args.state.button_one_border = args.state.button_style.merge( x: 1, y: 32)
  args.lowrez.borders << args.state.button_one_border
  args.lowrez.labels  << args.lowrez
                             .default_label
                             .merge(args.state.label_style)
                             .merge(x: args.state.button_one_border.x + 2,
                                    y: args.state.button_one_border.y + LOWREZ_FONT_SM_HEIGHT + 2,
                                    text: "Button One")

  # Creates button two using a border and a label
  args.state.button_two_border = args.state.button_style.merge( x: 1, y: 20)

  args.lowrez.borders << args.state.button_two_border
  args.lowrez.labels << args.lowrez
                            .default_label
                            .merge(args.state.label_style)
                            .merge(x: args.state.button_two_border.x + 2,
                                   y: args.state.button_two_border.y + LOWREZ_FONT_SM_HEIGHT + 2,
                                   text: "Button Two")

  # Initialize the state variable that tracks which button was clicked to "" (empty stringI
  args.state.last_button_clicked ||= "--"

  # If a click occurs, check to see if either button one, or button two was clicked
  # using the inside_rect? method of the mouse
  # set args.state.last_button_clicked accordingly
  if args.lowrez.mouse_click
    if args.lowrez.mouse_click.inside_rect? args.state.button_one_border
      args.state.last_button_clicked = "One Clicked!"
    elsif args.lowrez.mouse_click.inside_rect? args.state.button_two_border
      args.state.last_button_clicked = "Two Clicked!"
    else
      args.state.last_button_clicked = "--"
    end
  end

  # Render the current value of args.state.last_button_clicked
  args.lowrez.labels << args.lowrez
                             .default_label
                             .merge(args.state.label_style)
                             .merge(x: 32,
                                    y: 5,
                                    text: args.state.last_button_clicked,
                                    alignment_enum: 1)
end


def render_debug args
  if !args.state.grid_rendered
    65.map_with_index do |i|
      args.outputs.static_debug << {
        x:  LOWREZ_X_OFFSET,
        y:  LOWREZ_Y_OFFSET + (i * 10),
        x2: LOWREZ_X_OFFSET + LOWREZ_ZOOMED_SIZE,
        y2: LOWREZ_Y_OFFSET + (i * 10),
        r: 128,
        g: 128,
        b: 128,
        a: 80
      }.line!

      args.outputs.static_debug << {
        x:  LOWREZ_X_OFFSET + (i * 10),
        y:  LOWREZ_Y_OFFSET,
        x2: LOWREZ_X_OFFSET + (i * 10),
        y2: LOWREZ_Y_OFFSET + LOWREZ_ZOOMED_SIZE,
        r: 128,
        g: 128,
        b: 128,
        a: 80
      }.line!
    end
  end

  args.state.grid_rendered = true

  args.state.last_click ||= 0
  args.state.last_up    ||= 0
  args.state.last_click   = Kernel.tick_count if args.lowrez.mouse_down # you can also use args.lowrez.click
  args.state.last_up      = Kernel.tick_count if args.lowrez.mouse_up
  args.state.label_style  = { size_enum: -1.5 }

  args.state.watch_list = [
    "Kernel.tick_count is:           #{Kernel.tick_count}",
    "args.lowrez.mouse_position is:  #{args.lowrez.mouse_position.x}, #{args.lowrez.mouse_position.y}",
    "args.lowrez.mouse_down tick:    #{args.state.last_click || "never"}",
    "args.lowrez.mouse_up tick:      #{args.state.last_up || "false"}",
  ]

  args.outputs.debug << args.state
                            .watch_list
                            .map_with_index do |text, i|
    {
      x: 5,
      y: 720 - (i * 20),
      text: text,
      size_enum: -1.5
    }.label!
  end

  args.outputs.debug << {
    x: 640,
    y:  25,
    text: "INFO: dev mode is currently enabled. Comment out the invocation of ~render_debug~ within the ~tick~ method to hide the debug layer.",
    size_enum: -0.5,
    alignment_enum: 1
  }.label!
end

GTK.reset
