defmodule Util.Cache do
  @moduledoc false
  # Simple LRU cache, with optional persistence to disk.

  @doc """
  Start the cache service for a collection
  """
  def start(frame, size \\ 50) do
    {ets_name, dets_name, dets_path} = expand(frame)
    unless Process.whereis(ets_name) do
      alias Searchex.Command.CmdHelpers
      File.mkdir_p(CmdHelpers.cache_dir(frame))
      LruCache.start_link(ets_name, size)
      :dets.open_file(dets_name, [file: dets_path])
      :ets.from_dets(ets_name, dets_name)
      :dets.close(dets_name)
    end
    :ok
  end

  @doc "Stop the cache process"
  def stop(frame) do
    save(frame)
    {ets_name, dets_name, _} = expand(frame)
    if Process.whereis(ets_name),  do: GenServer.stop(ets_name)
    if Process.whereis(dets_name), do: GenServer.stop(dets_name)
  end

  @doc "Save the cache data to disk"
  def save(frame) do
    unless frame.halted do
      {ets_name, dets_name, dets_path} = expand(frame)
      if Process.whereis(ets_name) do
        :dets.open_file(dets_name, [file: dets_path])
        :ets.to_dets(ets_name, dets_name)
        :dets.close(dets_name)
      end
    end
    frame
  end

  def save(frame, _el) do
    save(frame)
    frame
  end

  @doc "Get a value from the cache"
  def get_cache(_frame, ""),  do: nil
  def get_cache(frame, key) do
    {ets_name, _, _} = expand(frame)
    start(frame)
    LruCache.get(ets_name, Util.Ext.Term.to_atom(key))
  end

  @doc "Put a value in the cache"
  def put_cache(frame, key, val) do
    start(frame)
    {ets_name, _, _} = expand(frame)
    LruCache.put(ets_name, Util.Ext.Term.to_atom(key), val)
    key
  end

  @doc "Remove all values from cache"
  def clear_cache(frame) do
    start(frame)
    {ets_name, _, _} = expand(frame)
    :ets.delete_all_objects(ets_name)
    save(frame)
  end

  defp expand(frame) do
    alias Searchex.Command.CmdHelpers
    cfg_str   = to_string(frame.cfg_name) |> String.replace("/", "_")
    ets_name  = Util.Ext.Term.to_atom("ets_#{cfg_str}")
    dets_name = Util.Ext.Term.to_atom("dets_#{cfg_str}")
    dets_path = CmdHelpers.cache_file(frame) |> String.to_charlist
    {ets_name, dets_name, dets_path}
  end
end
