defmodule ServerMacros do
  @moduledoc """
    This module defines some helper macros that are used within the server macro
  """
  
  @doc """
    This macro creates a wait loop to wait for the messages that are receive match what is inside the
    loop
  """
  defmacro create_loop(name, do: match_exp) do
    function_name = :"#{name}_loop"

    ast1 = quote do
      {:timeout} -> :timeout
    end
    ast2 = quote do
      value ->
        # TODO check spelling
        log("Disreguarding: #{inspect(value)}")
        # Process.send_after(self(), value, t + 2000)
        unquote(function_name)(v, t)
    end

    ast3 = ast1 ++ match_exp ++ ast2

    quote do
      defp unquote(function_name)(v, t) do
        var!(v) = v
        receive do
          unquote(ast3)
        after
          t -> :timeout
        end
      end
    end
  end
  
  @doc """
    this function tries to propose and on failure it calls the else block and
    on success tries to match with the expressions on the do block
  """
  defmacro try_propose(val, do: ready, else: recal_do) do
    ast1 = quote do
      {:timeout} ->  unquote(recal_do)
      {:abort} ->   unquote(recal_do)
    end
    ast2 = quote do
      {:decision, v} ->
        var!(state) = apply_game(var!(state), v)
        unquote(recal_do)

      v ->
        raise "Unknown message on try_propose #{inspect(v)}"
    end

    ast3 = ast1 ++ ready ++ ast2

    quote do
      v = Paxos.propose(var!(state).paxos, var!(state).instance, unquote(val), 1000)
      case v do
        unquote(ast3)
      end
    end
  end

end

defmodule Server do
  @moduledoc """
    Contains the to run the server Code
  """
  require ServerMacros
  import ServerMacros
  require Utils
  import Utils

  create_log 2
  
  @doc """
    Contains the start code for the server
  """
  def start(name, participants) do
    log("#{name} Starting server")

    pid = spawn(Server, :init, [name, participants])
    register_name(name, pid, false)
  end
  
  @doc """
    Initializes the state and starts the paxos inspect and then it calls the run fn
  """
  def init(name, participants) do
    paxos = Paxos.start(alter_name(name, "_paxos"), Enum.map(participants, fn name -> alter_name(name, "_paxos") end), true)
    state = %{
      name: name,
      procs: participants,
      games: %{},
      paxos: paxos,
      instance: 0,
    }

    run(state)
  end

  runfn do
    {:start_game, participants, pid_to_inform} ->
      or_state is_list(participants) do
        {state, game_id} = try_to_create_game(state, participants)
        state = set_modifed(state, game_id)
        safecast(pid_to_inform, {:start_game_ans, game_id})
        state
      end

    {:get_game_state, game_id, pid_to_inform} ->
      state = get_game_state(state, game_id, pid_to_inform)
      set_modifed(state, game_id)


    {:make_move, game_id, move, pid_to_inform} ->
      try_to_play_checks(state, game_id, move, pid_to_inform)

  end
  
  # Checks if the user can play the move before starting the requesting the user to play
  defp try_to_play_checks(state, game_id, move, pid_to_inform, repeat \\ false) do
    cond do
      state.games[game_id] == :not_playing_in_game ->
        safecast(pid_to_inform, {:make_move, game_id, :not_playing})
        state

      state.games[game_id] == nil ->
        if repeat do
          safecast(pid_to_inform, {:make_move, game_id, :game_does_not_exist})
          state
        else
          state = qurey_status(state)
          try_to_play_checks(state, game_id, move, pid_to_inform, true)
        end 

      true ->
        state = qurey_status(state)
        game = state.games[game_id]
        cond do
          is_finished(state, game_id) ->
            {_, score} = state.games[game_id]
            safecast(pid_to_inform, {:make_move, game_id, :game_finished, score})
            state
          game.modified ->
            safecast(pid_to_inform, {:make_move, game_id, :player_moved_before, game.game_state, game.hand})
            set_modifed(state, game_id)
          true ->
            cond do
              not is_number(move) ->
                safecast(pid_to_inform, {:make_move, game_id, :invalid_move})
                state
              move >= length(game.game_state) or  move < 0 ->
                safecast(pid_to_inform, {:make_move, game_id, :invalid_move})
                state
              true ->
                try_to_play(state, game_id, move, pid_to_inform)
            end
        end
    end
  end
  
  # Tries to propose to paxos the game action
  defp try_to_play(state, game_id, move, pid_to_inform) do
    name = state.name
    
    new_hand = if state.games[game_id].hand[state.name] == :- do
      Enum.at(state.games[game_id].game_state, move)
    else
      get_hand_for_game_state(state.games[game_id].game_state)
    end

    try_propose {:make_move, game_id, name, move, new_hand}
    do
      {:decision, {:make_move, ^game_id, ^name, ^move, ^new_hand}} ->
        state = apply_game(state, {:make_move, game_id, name, move, new_hand})

        if is_finished(state, game_id) do
          {_, score} = state.games[game_id]
          safecast(pid_to_inform, {:make_move, game_id, :game_finished, score})
        else
          game = state.games[game_id]
          safecast(pid_to_inform, {:make_move, game_id, game.game_state, game.hand[state.name]})
        end

        set_modifed(state, game_id)

      {:decision, {:make_move, ^game_id, new_name, new_move, new_new_hand}} ->
        state = apply_game(state, {:make_move, game_id, new_name, new_move, new_new_hand})

        if is_finished(state, game_id) do
          {_, score} = state.games[game_id]
          safecast(pid_to_inform, {:make_move, game_id, :game_finished, score})
        else
          game = state.games[game_id]
          safecast(pid_to_inform, {:make_move, game_id, :player_moved_before, game.game_state, game.hand})
        end

        set_modifed(state, game_id)
    else
      try_to_play(state, game_id, move, pid_to_inform)
    end
  end
  
  # Get the most recent game_state and return it to the player
  defp get_game_state(state, game_id, pid_to_inform, repeat \\ false) do
    cond do 
      state.games[game_id] == :not_playing_in_game ->
        safecast(pid_to_inform, {:game_state, game_id, :not_playing})
        state

      state.games[game_id] == nil ->
        if repeat do
          safecast(pid_to_inform, {:game_state, game_id, :game_does_not_exist})
          state
        else
          state = qurey_status(state)
          get_game_state(state, game_id, pid_to_inform, true)
        end

      true ->
        state = qurey_status(state)
        if is_finished(state, game_id) do
          {_, score} = state.games[game_id]
          safecast(pid_to_inform, {:game_state, game_id, :game_finished, score})
        else
          game = state.games[game_id]
          safecast(pid_to_inform, {:game_state, game_id, game.game_state, game.hand[state.name]})
        end
        state
    end
  end
  
  # This generates a new hand based on the current game state
  defp get_hand_for_game_state(game_state) do
    r1 = Enum.random(0..100)
    cond do 
      r1 <= 1 -> :b
      r1 <= 5 -> :-
      r1 <= 20 -> :+
      true ->
        mx = game_state |> Enum.filter(fn m -> m != :+ end) |> Enum.max()
        mn = max(mx - 20, 1)
        mx = max(mx - 2, 4)
        Enum.random(mn..mx)
    end 
  end
  
  # This tries to create a game by sending the create message to paxos
  defp try_to_create_game(state, participants) do
    game_ids = Map.keys(state.games)
    latest = Enum.at(Enum.sort(game_ids), length(game_ids) - 1)
    new_game_id = if latest do latest else 0 end + 1

    new_game_state = Enum.to_list(0..Enum.random(3..8)) |> Enum.map(fn _ -> Enum.random(1..4) end)
    hand = Enum.reduce(participants, %{}, fn p, acc -> Map.put(acc, p, get_hand_for_game_state(new_game_state)) end)

    try_propose {:start_game, new_game_id, participants, new_game_state, hand}
    do
      {:decision, {:start_game, ^new_game_id, ^participants, ^new_game_state, ^hand}} ->
        state = apply_game(state, {:start_game, new_game_id, participants, new_game_state, hand})
        {state, new_game_id}
    else
      try_to_create_game(state, participants)
    end
  end

  #
  # Utils
  #

  # Checks if a game has been finished
  defp is_finished(state, game) do
    case state.games[game] do
      {:finished, _} -> true
      _ -> false
    end
  end
  
  # Gets up to the most recent instance
  defp qurey_status(state) do
    v = Paxos.get_decision(state.paxos, state.instance, 100)
    or_state v != nil do
      state = apply_game(state, v)
      qurey_status(state)
    end
  end
  
  # Sets the modified flag
  defp set_modifed(state, game, val \\ false) do
    or_state not is_finished(state, game) and state.games[game] != :not_playing_in_game and state.games[game] != nil do
      %{state | games: Map.put(state.games, game, %{state.games[game] | modified: val})}
    end
  end

  #
  # Apply Game States
  #

  # Calculates the index based on the incoming index and a move
  defp get_index(indexed_game_state, spos, index) do
    index = spos + index
    len = length(indexed_game_state)
    cond do
      index < 0 ->
        len - 1
      index >= len ->
        rem(index, len)
      true ->
        index 
    end
  end
  
  # This funcion looks at pluses and blackholes and chekes if they can be merged
  # This funcion will pick a state like this [ 1, 2, :+, 2, 1 ] and create a new state [ 1, {:merged, 3} , 1] and then call expand merge which will expand the merge
  # Note by this point the state is indexed so it looks like [{1, 0}, {:+, 1}, {1, 2}]
  defp simplify_game_state_pluses([], indexed_game_state), do: {false, indexed_game_state}
  defp simplify_game_state_pluses([{:+, i} | tl], indexed_game_state) do
    before_i = get_index(indexed_game_state, i, -1)
    after_i = get_index(indexed_game_state, i, 1)

    if before_i != after_i do 
      {b, b_i} = Enum.at(indexed_game_state, before_i)
      {a, a_i} = Enum.at(indexed_game_state, after_i)

      if b == a do
        case b do
          :+ -> simplify_game_state_pluses(tl, indexed_game_state)
          :b -> simplify_game_state_pluses(tl, indexed_game_state)
          n ->
            list =
              indexed_game_state |>
              Enum.map(fn {x, ti} -> if ti == i, do: {{:merged, n + 1}, i}, else: {x, ti} end) |>
              Enum.filter(fn {_, ti} -> cond do
                  b_i == ti -> false
                  a_i == ti -> false
                  true -> true
                end
              end) |>
              reindex()
            {true, expand_merge(list)}
        end
      else
        simplify_game_state_pluses(tl, indexed_game_state)
      end
    else
        simplify_game_state_pluses(tl, indexed_game_state)
    end
  end

  defp simplify_game_state_pluses([{:b, i} | tl], indexed_game_state) do
    before_i = get_index(indexed_game_state, i, -1)
    after_i = get_index(indexed_game_state, i, 1)

    if before_i != after_i do 
      {b, b_i} = Enum.at(indexed_game_state, before_i)
      {a, a_i} = Enum.at(indexed_game_state, after_i)

      a = if is_atom(a) do 1 else a end
      b = if is_atom(b) do 1 else b end

      list =
        indexed_game_state |>
        Enum.map(fn {x, ti} -> if ti == i, do: {{:merged, trunc(:math.floor((a + b) / 2))}, i}, else: {x, ti} end) |>
        Enum.filter(fn {_, ti} -> cond do
            b_i == ti -> false
            a_i == ti -> false
            true -> true
          end
        end) |>
        reindex()
      {true, expand_merge(list)}
    else
        simplify_game_state_pluses(tl, indexed_game_state)
    end
  end
  
  # Check if the item in an game is the substate merged
  defp is_merged(item) do
    case item do
      {:merged, _} -> true
      _ -> false
    end
  end

  # This funcion expands merges
  # With a state like this [1, {:merged, 3}, 1] it will create a game state like this [ {:merged, 4 } ]
  # Note by this point the state is indexed so it looks like [{1, 0}, {:+, 1}, {1, 2}]
  defp expand_merge(indexed_game_state) do
    {{:merged, n}, i} = indexed_game_state |> Enum.find(fn {x, _} -> is_merged(x) end)

    b_i = get_index(indexed_game_state, i, - 1)
    a_i = get_index(indexed_game_state, i, + 1)

    if b_i != a_i do
      {b, b_i} = Enum.at(indexed_game_state, b_i)
      {a, a_i} = Enum.at(indexed_game_state, a_i)
      if a == b do
        case b do
          :+ -> indexed_game_state
          {:merged, _} -> indexed_game_state
          _ ->
            indexed_game_state |>
              Enum.map(fn {x, ti} -> if ti == i, do: {{:merged, max(n, a) + 1}, i}, else: {x, ti} end) |>
              Enum.filter(fn {_, ti} -> cond do
                  b_i == ti -> false
                  a_i == ti -> false
                  true -> true
                end
              end) |>
              reindex() |>
              expand_merge()
        end
      else
        indexed_game_state
      end
    else
      indexed_game_state
    end
  end
  
  # This funcion removes previous indexes and reindexs the state
  defp reindex(list, flat \\ true) do
    list = if flat do
      list |> Enum.map(fn {n, _} -> n end)
    else
      list
    end

    [list, 0..(length(list) - 1)] |> Enum.zip()
  end
  
  # Removes all merged from the array
  defp remove_merged(l), do: l |> Enum.map(fn x -> case x do
    {:merged, n} -> n
    x -> x
  end end)
  
  # This funcion recieves the game state after the move was done and tries to simplify the game 
  defp simplify_game_state(game_state) do
    indexed_game_state = 
      game_state |>
      reindex(false)

    {repeat, indexed_game_state} =
      indexed_game_state |>
      Enum.filter(fn x -> case x do 
        {:+, _} -> true
        {:b, _} -> true
        _ -> false
        end
      end) |>
      simplify_game_state_pluses(indexed_game_state)

    if repeat do
      indexed_game_state |> 
        Enum.map(fn {v, _} -> v end) |>
        remove_merged() |>
        simplify_game_state()
    else
      indexed_game_state |> Enum.map(fn {v, _} -> v end)
    end
  end
  
  # This function tries to apply the make_move command
  defp apply_game(state, {:make_move, game_id, player_name, pos_move, new_hand}) do
    game = state.games[game_id]
    case game do
      {:finished, _} ->
        raise "Game already finished"
      :not_playing_in_game ->
        %{state | instance: state.instance + 1 }
      game ->
        game_state = game.game_state
        game_state = if game.hand[player_name] == :- do
          List.delete_at(game_state, pos_move)
        else
          {b, e} = Enum.split(game_state, pos_move)
          b ++ [ game.hand[player_name] ] ++ e
        end
        game_state = simplify_game_state(game_state)
        hand = Map.put(game.hand, player_name, new_hand)
        game = %{game| hand: hand, game_state: game_state, modified: true }

        if length(game.game_state) > 16 do
          %{state| games: Map.put(state.games, game_id, {
            :finished, 
            game.game_state |> 
              Enum.filter(fn x -> not is_atom(x) end) |> 
              Enum.sum()}), instance: state.instance + 1
          }
        else
          %{state| games: Map.put(state.games, game_id, game), instance: state.instance + 1}
        end
    end
  end
  
  # This function tries to apply the start_game command
  defp apply_game(state, {:start_game, game_id, participants, new_game_state, hand}) do
    cond do
      state.games[game_id] ->
        raise "Game Already Exists"
      state.name in participants ->
        %{state | 
          games: Map.put(state.games, game_id, %{
            game_state: new_game_state,
            participants: participants,
            hand: hand,
            modified: true,
          }),
          instance: state.instance + 1
        }
      true ->
        %{state | 
          games: Map.put(state.games, game_id, :not_playing_in_game),
          instance: state.instance + 1,
        }
    end
  end

  defp apply_game(_, _), do: raise "Do not know how to apply game state"

  ############
  # Interface 
  ############
  
  # handles responses from the start game request
  create_loop :start_game do
     {:start_game_ans, game_id} ->
       log("Started a game #{game_id}")
       {:start_game, game_id}
  end

  @doc """
    Requests the server to start a game
  """
  def start_game(name, participants) do
    safecast(name, {:start_game, participants, self()})
    start_game_loop(nil, 10000)
  end

  # handles responses from the get game state request
  create_loop :get_game_state do
    {:game_state, ^v, :not_playing} ->
      log("Not Playing in that game")
      {:not_playing}
    {:game_state, ^v, :game_finished, score} ->
      log("Game finsihed, #{score}")
      {:game_finished, score}
    {:game_state, ^v, game_state, hand} ->
      log("Got game state, #{inspect(game_state)}, hand: #{inspect(hand)}")
      {:state, game_state, hand}
    {:game_state, ^v, :game_does_not_exist} ->
      log("Got game does not exist")
      {:not_exists}
  end

  @doc """
    Requests the server to get the game state
  """
  def get_game_state(name, game_id) do
    safecast(name, {:get_game_state, game_id, self()})
    get_game_state_loop(game_id, 10000)
  end

  # handles responses from the make move request
  create_loop :make_move do
    {:make_move, ^v, :game_does_not_exist} ->
      log("Got game does not exist")
      {:not_exists}
    {:make_move, ^v, :not_playing} ->
      log("Not Playing in that game")
      {:not_playing}
    {:make_move, ^v, :game_finished, score} ->
      log("Game finsihed, #{score}")
      {:game_finished, score}
    {:make_move, ^v, :player_moved_before, game_state, hand} ->
      log("Player moved_before, #{inspect(game_state)} #{inspect(hand)}")
      {:player_moved_before, game_state, hand}
    {:make_move, ^v, :invalid_move} ->
      log("Invalid Move")
      {:invalid_move}
    {:make_move, ^v, game_state, hand} ->
      log("Got game state, #{inspect(game_state)}, hand: #{inspect(hand)}")
      {:state, game_state, hand}
  end

  @doc """
    Requests to make a move
  """
  def make_move(name, game_id, move) do
    safecast(name, {:make_move, game_id, move, self()})
    make_move_loop(game_id, 10000)
  end

  ############
  # Debug 
  ############
  
  @doc """
    Quicky creates some servers it's useful in testing
  """
  def spinup(number_of_participants) do
    procs = Enum.to_list(1..number_of_participants) |> Enum.map(fn n -> :"p#{n}" end)
    Enum.map(procs, fn proc -> Server.start(proc, procs) end)
  end

  @doc """
    Quicky kills some servers it's useful in testing
  """
  def kill (pids) do
    pids |> Enum.map(fn m -> Process.exit(m, :kill) end)
  end
end

defmodule Client do
  @moduledoc """
    This module handles displaying the game state in a nice to understand 
    way
  """
  import Utils
  require Utils

  create_log 3
  
  @doc """
    This function plays the displays the game then waits for user input and displays the next state of the game
  """
  def play_game(process, game_id) do
    game = Server.get_game_state(process, game_id)

    cont = case game do
      :timeout ->
        log("Could to not comunicate with the server")
        true
      {:not_exists} -> 
        log("Game does not exist")
        true
      {:not_playing} ->
        log("Not Playing in that game")
        true
      {:game_finished, score} ->
        log("Game finsihed, #{score}")
        true
      {:game_does_not_exist} ->
        log("Got game does not exist")
        true
      {:state, game_state, hand} ->
        game_state |> 
          to_name() |>
          interpolate() |>
          fill() |>
          printpt(to_name(hand))
        false
    end

    if not cont do
      to_play = IO.gets("Type the number you want to play or q to exit: ")
      to_play = to_play |> String.trim("\"") |> String.trim()

      case to_play do
        "q" ->
          log("Bye Bye")
        v ->
          try do
            {n, _} = Integer.parse(v)

            res = Server.make_move(process, game_id, n)
            case res do
              :timeout ->
                log("Could to not comunicate with the server")
              {:not_exists} -> log("Game does not exist")
              {:not_playing} -> log("Not Playing in that game")
              {:game_finished, score} -> log("Game finsihed, #{score}")
              {:game_does_not_exist} -> log("Got game does not exist")
              {:player_moved_before, _, _} ->
                log("Player Moved before you did please check the map and try again")
                play_game(process, game_id)
              {:invalid_move} -> raise "Invalid Move"
              {:state, _, _} -> play_game(process, game_id)
            end
          rescue
            _ -> 
              log("Please provide a valid number")
              play_game(process, game_id)
          end
      end
    end
  end


  @doc """
    This funcion waits for user input and then commands the server to act
    
    Use the display_game funcion in a seperate terminal to see the game
  """
  def control_game(process, game_id) do
    to_play = IO.gets("Type the number you want to play or q to exit: ")
    to_play = to_play |> String.trim("\"") |> String.trim()

    case to_play do
      "q" ->
        log("Bye Bye")
      v ->
        try do
          {n, _} = Integer.parse(v)

          res = Server.make_move(process, game_id, n)
          case res do
            :timeout -> log("Could to not comunicate with the server")
            {:not_exists} -> control_game(process, game_id)
            {:not_playing} -> control_game(process, game_id)
            {:game_finished, _} -> control_game(process, game_id)
            {:player_moved_before, _, _} ->
              log("Player Moved before you did please check the map and try again")
              control_game(process, game_id)
            {:invalid_move} -> raise "Invalid Move"
            {:state, _, _} -> control_game(process, game_id)
          end
        rescue
          _ -> 
            log("Please provide a valid number")
            control_game(process, game_id)
        end
    end
  end


  @doc """
    This funcion tries to display the most recent version of the game

    Use control_game to control game
  """
  def display_game(process, game_id, temp_game \\ [], temp_hand \\ []) do
    game = Server.get_game_state(process, game_id)

    case game do
      :timeout -> log("Could to not comunicate with the server")
      {:not_exists} -> log("Game does not exist")
      {:not_playing} -> log("Not Playing in that game")
      {:game_finished, score} -> log("Game finsihed, #{score}")
      {:game_does_not_exist} -> log("Got game does not exist")
      {:state, game_state, hand} ->
        if temp_game != game_state or temp_hand != hand do
          # IO.ANSI.clear()
          game_state |> 
            to_name() |>
            interpolate() |>
            fill() |>
            printpt(to_name(hand))
          Process.sleep(1000)
          display_game(process, game_id, game_state, hand)
        else
          Process.sleep(1000)
          display_game(process, game_id, temp_game, temp_hand)
        end
    end
  end
  
  # This funcion recieves some objects and transforms them into user friendly text 
  defp to_name(list) when is_list(list), do: list |> Enum.map(fn x -> to_name(x) end)
  defp to_name(atom) when atom == :+, do: IO.ANSI.color_background(9) <> IO.ANSI.color(15) <> " + " <> IO.ANSI.reset()
  defp to_name(atom) when atom == :-, do: IO.ANSI.color_background(21) <> IO.ANSI.color(15) <> " - " <> IO.ANSI.reset()
  defp to_name(atom) when atom == :b, do: IO.ANSI.color_background(232) <> IO.ANSI.color(15) <> " b " <> IO.ANSI.reset()
  defp to_name(atom) when is_atom(atom), do: atom
  defp to_name(i) do
    letter = [ "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", "Md", "No", "Lr", "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds", "Rg", "Cn", "Nh", "Fl", "Mc", "Lv", "Ts", "Og" ] |> Enum.at(i - 1)
    color = [46,45,138,19,11,140,8,47,57,159,72,48,55,35,251,188,107,110,21,11,156,134,128,179,140,234,14,90,206,7,249,209,253,123,192,165,234,136,198,208,43,34,215,127,23,250,177,237,124,202,229,
63,206,220,224,109,202,113,253,7,243,26,160,65,39,112,57,75, 252,82,213,186,68,243,134,100,226,48,90,134,208,102,25,106,72, 242,26,59,166,26,187,54,194,165,97,219,186,130,7,154,233,85, 130,67,43,200,90,60,148,49,161,110,247,116,223,159,132,132] |> Enum.at(i - 1) |> IO.ANSI.color()
    color <> String.pad_leading("#{letter}", 3, "  ") <> IO.ANSI.reset()
  end
  
  # Adds the index as an indicator and pads the index
  defp interpolate(list) do
    [list, 0..length(list) - 1] |> 
      Enum.zip() |> 
      Enum.reduce([], fn {v, i}, acc -> acc ++ [String.pad_leading("#{i}", 3, "  "), v] end)
  end

  # This grows a list with between spaces every item of the interpolated list
  # This allows the items to take as puch space in the possilble
  defp grow_empty_list(t, i, acc) when i == 0, do: t ++ acc
  defp grow_empty_list([], i, acc), do: grow_empty_list(acc, i, [])
  defp grow_empty_list([h | tl], i, acc), do:
    grow_empty_list(tl, i - 1, acc ++ [ h ++ ["   "] ])

  # This takes the list that was generated in the grow_empty_list function and merges it between every other item
  defp fill(list) do
    to_fill = 32 -  length(list)
    to_add = grow_empty_list(Enum.map(list, fn _ -> [] end), to_fill, [])
    fill(list, to_add, [])
  end

  defp fill([], _, acc), do: acc
  defp fill([hd | tail], [add_hd | add_tail], acc) do
    fill(tail, add_tail ,acc ++ [hd] ++ add_hd)
  end

  # This functions prints the circle
  defp printpt(game_state, hand), do: printpt(game_state, hand, 0)
  defp printpt(_, _,  i) when i > 16, do: nil
  defp printpt(game_state, hand, i) do
    res = case i do
      0 ->
        "                       xxx             \n" |>
          String.replace("xxx", Enum.at(game_state, 1))
      1 ->
        "                  xxx       yyy           \n" |>
          String.replace("xxx", Enum.at(game_state, 0)) |>
          String.replace("yyy", Enum.at(game_state, 2))
      2 ->
        "              xxx               yyy        \n" |>
          String.replace("xxx", Enum.at(game_state, 31)) |>
          String.replace("yyy", Enum.at(game_state, 3))
      3 ->
        "            xxx                   yyy       \n" |>
          String.replace("xxx", Enum.at(game_state, 30)) |>
          String.replace("yyy", Enum.at(game_state, 4))
      4 ->
        "          xxx                       yyy       \n" |>
          String.replace("xxx", Enum.at(game_state, 29)) |>
          String.replace("yyy", Enum.at(game_state, 5))
      5 ->
        "         xxx                         yyy      \n" |>
          String.replace("xxx", Enum.at(game_state, 28)) |>
          String.replace("yyy", Enum.at(game_state, 6))
      6 ->
        "       xxx                             yyy      \n" |>
          String.replace("xxx", Enum.at(game_state, 27)) |>
          String.replace("yyy", Enum.at(game_state, 7))
      7 ->
        "       xxx                             yyy      \n" |>
          String.replace("xxx", Enum.at(game_state, 26)) |>
          String.replace("yyy", Enum.at(game_state, 8))
      8 ->
        "       xxx             zzz             yyy      \n" |>
          String.replace("xxx", Enum.at(game_state, 25)) |>
          String.replace("yyy", Enum.at(game_state, 9)) |>
          String.replace("zzz", hand)
      9 ->
        "       xxx                             yyy      \n" |>
          String.replace("xxx", Enum.at(game_state, 24)) |>
          String.replace("yyy", Enum.at(game_state, 10))
      10 ->
        "        xxx                           yyy       \n" |>
          String.replace("xxx", Enum.at(game_state, 23)) |>
          String.replace("yyy", Enum.at(game_state, 11))
      11 ->
        "         xxx                         yyy       \n" |>
          String.replace("xxx", Enum.at(game_state, 22)) |>
          String.replace("yyy", Enum.at(game_state, 12))
      12 ->
        "          xxx                       yyy        \n" |>
          String.replace("xxx", Enum.at(game_state, 21)) |>
          String.replace("yyy", Enum.at(game_state, 13))
      13 ->
        "            xxx                   yyy        \n" |>
          String.replace("xxx", Enum.at(game_state, 20)) |>
          String.replace("yyy", Enum.at(game_state, 14))
      14 ->
        "              xxx               yyy         \n" |>
          String.replace("xxx", Enum.at(game_state, 19)) |>
          String.replace("yyy", Enum.at(game_state, 15))
      15 ->
        "                  xxx       yyy           \n" |>
          String.replace("xxx", Enum.at(game_state, 18)) |>
          String.replace("yyy", Enum.at(game_state, 16))
      16 ->
        "                       xxx        \n" |>
          String.replace("xxx", Enum.at(game_state, 17))
    end

    IO.write("#{res}")
    printpt(game_state, hand, i + 1)
  end
end