module main

import vendor.raylib rl

let draw_texture_in_rectangle(
  texture: *rl.Texture2D,
  destination: rl.Rectangle,
  flip_vertical: bool,
) {
  let source = rl.Rectangle.{
    x = 0.0,
    y = if flip_vertical { texture.height.(f32) } else { 0.0 },
    width = texture.width.(f32),
    height = texture.height.(f32) * if flip_vertical { -1.0 } else { 1.0 },
  }

  rl.draw_texture_pro(
    texture.*,
    source,
    destination,
    .{ x = 0.0, y = 0.0, },
    0.0,
    rl.White,
  )
}

let current_bird_texture(
  state: *GameState,
  textures: *GameAssets,
): *rl.Texture2D {
  if state.bird_velocity_y > 0.25 {
    return textures.bird_upflap.&
  }

  if state.bird_velocity_y < -0.25 {
    return textures.bird_downflap.&
  }

  return textures.bird_midflap.&
}

let render_background(state: *GameState, textures: *GameAssets) {
  let texture = textures.background.&
  let screen_width = rl.get_screen_width().(f32)
  let screen_height = rl.get_screen_height().(f32)
  let tile_width = screen_height * texture.width.(f32) / texture.height.(f32)
  let scroll_offset = state.background_scroll.(f32) * tile_width

  for let mut x = -scroll_offset; x < screen_width; x += tile_width {
    draw_texture_in_rectangle(
      texture,
      .{
        x = x,
        y = 0.0,
        width = tile_width,
        height = screen_height,
      },
      false,
    )
  }
}

let render_base(state: *GameState, textures: *GameAssets) {
  let texture = textures.base.&
  let rectangle = base_rectangle()
  let tile_width =
    rectangle.height * texture.width.(f32) / texture.height.(f32)
  let scroll_offset = state.base_scroll.(f32) * tile_width

  for let mut x = -scroll_offset; x < rectangle.width; x += tile_width {
    draw_texture_in_rectangle(
      texture,
      .{
        x = x,
        y = rectangle.y,
        width = tile_width,
        height = rectangle.height,
      },
      false,
    )
  }
}

let render_collision_rectangle(rectangle: rl.Rectangle) {
  rl.draw_rectangle_rec(
    rectangle,
    .{ r = 255, g = 0, b = 0, a = 64, },
  )
  rl.draw_rectangle_lines_ex(
    rectangle,
    2.0,
    .{ r = 255, g = 0, b = 0, a = 255, },
  )
}

let render_collision_boxes(state: *GameState) {
  if !state.show_collision_boxes {
    return
  }

  render_collision_rectangle(bird_rectangle(state))
  render_collision_rectangle(base_rectangle())

  for pipe in state.pipes {
    let rectangles = pipe_rectangles(pipe.&)
    render_collision_rectangle(rectangles.top)
    render_collision_rectangle(rectangles.bottom)
  }
}

let render_bird(state: *GameState, textures: *GameAssets) {
  let texture = current_bird_texture(state, textures)
  let bird = bird_rectangle(state)
  let source = rl.Rectangle.{
    x = 0.0,
    y = 0.0,
    width = texture.width.(f32),
    height = texture.height.(f32),
  }
  let destination = rl.Rectangle.{
    x = bird.x + bird.width / 2.0,
    y = bird.y + bird.height / 2.0,
    width = bird.width,
    height = bird.height,
  }
  let origin = rl.Vector2.{
    x = destination.width / 2.0,
    y = destination.height / 2.0,
  }
  let rotation = clamp(
    -state.bird_velocity_y * 20.0,
    -25.0,
    75.0,
  ).(f32)

  rl.draw_texture_pro(
    texture.*,
    source,
    destination,
    origin,
    rotation,
    rl.White,
  )
}

let draw_pipe_body_tile(
  texture: *rl.Texture2D,
  destination: rl.Rectangle,
  source_height: f32,
  flip_vertical: bool,
) {
  let source = rl.Rectangle.{
    x = 0.0,
    y = if flip_vertical { source_height } else { 0.0 },
    width = texture.width.(f32),
    height = source_height * if flip_vertical { -1.0 } else { 1.0 },
  }

  rl.draw_texture_pro(
    texture.*,
    source,
    destination,
    .{ x = 0.0, y = 0.0, },
    0.0,
    rl.White,
  )
}

let render_pipe_half(
  rectangle: rl.Rectangle,
  textures: *GameAssets,
  flip_vertical: bool,
) {
  let body = textures.pipe.&
  let head = textures.pipe_head.&
  let scale = rectangle.width / body.width.(f32)
  let body_tile_height = body.height.(f32) * scale
  let head_height = head.height.(f32) * scale
  let mut head_y = rectangle.y

  if flip_vertical {
    head_y = rectangle.y + rectangle.height - head_height
    let mut body_bottom = head_y

    for body_bottom > rectangle.y {
      let remaining_height = body_bottom - rectangle.y
      let mut tile_height = body_tile_height

      if tile_height > remaining_height {
        tile_height = remaining_height
      }

      draw_pipe_body_tile(
        body,
        .{
          x = rectangle.x,
          y = body_bottom - tile_height,
          width = rectangle.width,
          height = tile_height,
        },
        tile_height / scale,
        true,
      )
      body_bottom = body_bottom - tile_height
    }
  } else {
    let mut body_top = rectangle.y + head_height
    let body_bottom = rectangle.y + rectangle.height

    for body_top < body_bottom {
      let remaining_height = body_bottom - body_top
      let mut tile_height = body_tile_height

      if tile_height > remaining_height {
        tile_height = remaining_height
      }

      draw_pipe_body_tile(
        body,
        .{
          x = rectangle.x,
          y = body_top,
          width = rectangle.width,
          height = tile_height,
        },
        tile_height / scale,
        false,
      )
      body_top = body_top + tile_height
    }
  }

  draw_texture_in_rectangle(
    head,
    .{
      x = rectangle.x,
      y = head_y,
      width = rectangle.width,
      height = head_height,
    },
    flip_vertical,
  )
}

let render_pipe(pipe: *Pipe, textures: *GameAssets) {
  let rectangles = pipe_rectangles(pipe)
  let screen_width = rl.get_screen_width().(f32)

  if rectangles.top.x >= screen_width ||
     rectangles.top.x + rectangles.top.width <= 0.0 {
    return
  }

  // Flip the upper pipe so its opening points downwards.
  render_pipe_half(rectangles.top, textures, true)
  render_pipe_half(rectangles.bottom, textures, false)
}

let render_pipes(state: *GameState, textures: *GameAssets) {
  for pipe in state.pipes {
    render_pipe(pipe.&, textures)
  }
}

let draw_text_with_shadow(
  text: cstr,
  x, y, font_size: i32,
  color: rl.Color,
) {
  let shadow_offset = scale_with_window_height(2)
  rl.draw_text(
    text,
    x + shadow_offset,
    y + shadow_offset,
    font_size,
    rl.Black,
  )
  rl.draw_text(text, x, y, font_size, color)
}

let render_overlay(
  title: cstr,
  title_color: rl.Color,
  instruction: cstr,
  second_instruction: cstr,
  opacity: u8,
) {
  let screen_width = rl.get_screen_width()
  let screen_height = rl.get_screen_height()

  rl.draw_rectangle(
    0,
    0,
    screen_width,
    screen_height,
    .{ r = 0, g = 0, b = 0, a = opacity, },
  )

  let title_font_size = scale_with_window_height(overlay_title_font_size)
  let instruction_font_size =
    scale_with_window_height(overlay_instruction_font_size)
  let title_y_offset = scale_with_window_height(overlay_title_y_offset)
  let instruction_y_offset =
    scale_with_window_height(overlay_instruction_y_offset)
  let title_width = rl.measure_text(title, title_font_size)

  draw_text_with_shadow(
    title,
    (screen_width - title_width) / 2,
    screen_height / 2 - title_y_offset,
    title_font_size,
    title_color,
  )
  if instruction != nil {
    let instruction_width = rl.measure_text(instruction, instruction_font_size)
    draw_text_with_shadow(
      instruction,
      (screen_width - instruction_width) / 2,
      screen_height / 2 + instruction_y_offset,
      instruction_font_size,
      rl.White,
    )
  }

  if second_instruction != nil {
    let second_y_offset =
      scale_with_window_height(overlay_second_instruction_y_offset)
    let second_width =
      rl.measure_text(second_instruction, instruction_font_size)

    draw_text_with_shadow(
      second_instruction,
      (screen_width - second_width) / 2,
      screen_height / 2 + second_y_offset,
      instruction_font_size,
      rl.White,
    )
  }
}

let render_defeat(state: *GameState) {
  if state.lost {
    render_overlay(
      defeat_title,
      rl.Red,
      nil,
      nil,
      160,
    )
  }
}

let render_pause(state: *GameState) {
  if state.paused {
    render_overlay(
      pause_title,
      rl.White,
      nil,
      nil,
      160,
    )
  }
}

let render_begin(state: *GameState) {
  if !state.started {
    render_overlay(
      begin_title,
      rl.Yellow,
      begin_instruction,
      nil,
      128,
    )
  }
}

let render_points(state: *GameState) {
  let text = rl.text_format("%u", state.points)
  let font_size = scale_with_window_height(32)
  let shadow_offset = scale_with_window_height(2)
  let text_width = rl.measure_text(text, font_size)
  let x = (rl.get_screen_width() - text_width) / 2
  let y = (rl.get_screen_height().(f64) * 0.10).(i32)

  rl.draw_text(
    text,
    x + shadow_offset,
    y + shadow_offset,
    font_size,
    rl.Black,
  )
  rl.draw_text(text, x, y, font_size, rl.White)
}

let render(state: *GameState, textures: *GameAssets) {
  render_background(state, textures)
  render_pipes(state, textures)
  render_base(state, textures)
  render_bird(state, textures)
  when ReleaseMode == .Debug {
    render_collision_boxes(state)
  }
  render_defeat(state)
  render_pause(state)
  render_begin(state)
  render_points(state)
}