class Bitmap
  def outline_rect(x, y, width, height, color, thickness = 1)
    fill_rect(x, y, width, thickness, color)
    fill_rect(x, y, thickness, height, color)
    fill_rect(x, y + height - thickness, width, thickness, color)
    fill_rect(x + width - thickness, y, thickness, height, color)
  end

  # Draws a series of concentric outline_rects around the defined area. From
  # inside to outside, the color of each ring alternates.
  def border_rect(x, y, width, height, thickness, color1, color2)
    thickness.times do |i|
      col = (i.even?) ? color1 : color2
      outline_rect(x - i - 1, y - i - 1, width + (i * 2) + 2, height + (i * 2) + 2, col)
    end
  end

  def fill_diamond(x, y, radius, color)
    ((radius * 2) + 1).times do |i|
      height = (i <= radius) ? (i * 2) + 1 : (((radius * 2) - i) * 2) + 1
      fill_rect(x - radius + i, y - ((height - 1) / 2), 1, height, color)
    end
  end

  # TODO: Add more curve types once it's decided which ones they are. See
  #       INTERPOLATION_TYPES.
  def draw_interpolation_line(x, y, width, height, gradient, type, color)
    case type
    when :linear
      # NOTE: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
      start_x = x
      end_x = x + width - 1
      start_y = (gradient) ? y + height - 1 : y
      end_y = (gradient) ? y : y + height - 1
      dx = end_x - start_x
      dy = -((end_y - start_y).abs)
      error = dx + dy
      draw_x = start_x
      draw_y = start_y
      loop do
        fill_rect(draw_x, draw_y, 1, 1, color)
        break if draw_x == end_x && draw_y == end_y
        e2 = 2 * error
        if e2 >= dy
          break if draw_x == end_x
          error += dy
          draw_x += 1
        end
        if e2 <= dx
          break if draw_y == end_y
          error += dx
          draw_y += (gradient) ? -1 : 1
        end
      end
    end
  end
end

#===============================================================================
# Fixed Compiler.pbWriteCsvRecord to make it detect enums first, allowing enum
# values to be turned into symbols/booleans/whatever instead of just numbers.
#===============================================================================
module Compiler
  module_function

  def pbWriteCsvRecord(record, file, schema)
    rec = (record.is_a?(Array)) ? record.flatten : [record]
    start = (["*", "^"].include?(schema[1][0, 1])) ? 1 : 0
    index = -1
    loop do
      (start...schema[1].length).each do |i|
        index += 1
        value = rec[index]
        if schema[1][i, 1][/[A-Z]/]   # Optional
          # Check the rest of the values for non-nil things
          later_value_found = false
          (index...rec.length).each do |j|
            later_value_found = true if !rec[j].nil?
            break if later_value_found
          end
          if !later_value_found
            start = -1
            break
          end
        end
        file.write(",") if index > 0
        next if value.nil?
        case schema[1][i, 1]
        when "e", "E"   # Enumerable
          enumer = schema[2 + i]
          case enumer
          when Array
            file.write(enumer[value])
          when Symbol, String
            mod = Object.const_get(enumer.to_sym)
            file.write(getConstantName(mod, value))
          when Module
            file.write(getConstantName(enumer, value))
          when Hash
            enumer.each_key do |key|
              next if enumer[key] != value
              file.write(key)
              break
            end
          end
        when "y", "Y"   # Enumerable or integer
          enumer = schema[2 + i]
          case enumer
          when Array
            file.write((enumer[value].nil?) ? value : enumer[value])
          when Symbol, String
            mod = Object.const_get(enumer.to_sym)
            file.write(getConstantNameOrValue(mod, value))
          when Module
            file.write(getConstantNameOrValue(enumer, value))
          when Hash
            hasenum = false
            enumer.each_key do |key|
              next if enumer[key] != value
              file.write(key)
              hasenum = true
              break
            end
            file.write(value) unless hasenum
          end
        else
          if value.is_a?(String)
            file.write((schema[1][i, 1].downcase == "q") ? value : csvQuote(value))
          elsif value.is_a?(Symbol)
            file.write(csvQuote(value.to_s))
          elsif value == true
            file.write("true")
          elsif value == false
            file.write("false")
          else
            file.write(value.inspect)
          end
        end
      end
      break if start > 0 && index >= rec.length - 1
      break if start <= 0
    end
    return record
  end
end
