lib/identicon_generator.ex

defmodule IdenticonGenerator do
  @moduledoc """
  Rules for generating an identicon.
  250x250px image composed by 25 50x50px squares.
  The left side of the identicon is always mirrored to the right side.
  """

  def main(input) do
    input
    |> create_seed
    |> pick_color
    |> build_grid
    |> filter_odds
    |> build_pixel_map
    |> draw_image
    |> save_image(input)
  end

  @doc """
  Saves the `image` with the `filename` given.
  """
  def save_image(image, filename) do
    File.write("#{filename}.png", image)
  end

  @doc """

  """
  def draw_image(%IdenticonGenerator.Image{color: color, pixel_map: pixel_map}) do
    image = :egd.create(250, 250)
    fill = :egd.color(color)

    Enum.each pixel_map, fn({start, stop}) ->
      :egd.filledRectangle(image, start, stop, fill)
    end

    :egd.render(image)
  end

  def build_pixel_map(%IdenticonGenerator.Image{grid: grid} = image) do
    pixel_map = Enum.map grid, fn({_code, index}) ->
      x = rem(index, 5) * 50
      y = div(index, 5) * 50

      start_point = {x, y}
      end_point = {x+50, y+50}

      {start_point, end_point}
    end
    %IdenticonGenerator.Image{image | pixel_map: pixel_map}
  end

  def filter_odds(%IdenticonGenerator.Image{grid: grid} = image) do
    grid =
      Enum.filter grid, fn({code, _index}) ->
        rem(code, 2) == 0 #if true Enum.filter will keep
      end

    %IdenticonGenerator.Image{image | grid: grid}
  end

  @doc """
  Returns a list of lists representing a grid.
  """
  def build_grid(%IdenticonGenerator.Image{seed: seed} = image) do
    grid =
      seed
      |> Enum.chunk_every(3, 3, :discard)
      |> Enum.map(&mirror_row/1)
      |> List.flatten
      |> Enum.with_index

    %IdenticonGenerator.Image{image | grid: grid}
  end

  @doc """
  Returns a list. The second index of `row` is appended as the third index. The first index of `row` is appended as the fourth index.
  """
  def mirror_row(row) do
    [first, second | _tail] = row

    row ++ [second, first]
  end

  @doc """
    Returns a new struct from `image` and `color`.
  """
  def pick_color(%IdenticonGenerator.Image{seed: [r, g, b | _tail]} = image) do
    %IdenticonGenerator.Image{image | color: {r, g, b}}
  end

  @doc """
  Returns a list generated from the `input` given.

  ## Examples

      iex> IdenticonGenerator.create_hash("John")
      [97, 64, 154, 161, 253, 71, 212, 165, 51, 45, 226, 60, 191, 89, 163, 111]

  """
  def create_seed(input) do
    seed =
      :crypto.hash(:md5, input)
      |> :binary.bin_to_list

    %IdenticonGenerator.Image{seed: seed}
  end
end