defmodule Asciinema.Asciicasts.Asciicast do
  use Ecto.Schema
  import Ecto.Changeset
  alias Asciinema.Users.User
  alias Asciinema.Asciicasts.Asciicast
  alias Asciinema.PngGenerator.PngParams

  @default_png_scale 2
  @default_theme "asciinema"

  schema "asciicasts" do
    field :version, :integer
    field :file, :string
    field :terminal_columns, :integer
    field :terminal_lines, :integer
    field :terminal_type, :string
    field :stdout_data, :string
    field :stdout_timing, :string
    field :stdout_frames, :string
    field :private, :boolean
    field :secret_token, :string
    field :duration, :float
    field :title, :string
    field :theme_name, :string
    field :snapshot_at, :float
    field :snapshot, Asciinema.Ecto.Type.JsonArray
    field :command, :string
    field :shell, :string
    field :uname, :string
    field :user_agent, :string

    timestamps(inserted_at: :created_at)

    belongs_to :user, User
  end

  defimpl Phoenix.Param, for: Asciicast do
    def to_param(%Asciicast{private: false, id: id}) do
      Integer.to_string(id)
    end
    def to_param(%Asciicast{private: true, secret_token: secret_token}) do
      secret_token
    end
  end

  def changeset(struct, attrs) do
    struct
    |> cast(attrs, [:title, :private, :snapshot_at])
    |> validate_required([:private])
  end

  def create_changeset(struct, attrs) do
    struct
    |> changeset(attrs)
    |> cast(attrs, [:version, :file, :duration, :terminal_columns, :terminal_lines, :terminal_type, :command, :shell, :uname, :user_agent])
    |> validate_required([:user_id, :version, :duration, :terminal_columns, :terminal_lines])
    |> generate_secret_token
  end

  def update_changeset(struct, attrs) do
    struct
    |> changeset(attrs)
    |> cast(attrs, [:theme_name])
  end

  def snapshot_changeset(struct, snapshot) do
    cast(struct, %{snapshot: snapshot}, [:snapshot])
  end

  defp generate_secret_token(changeset) do
    put_change(changeset, :secret_token, Crypto.random_token(25))
  end

  def json_store_path(%Asciicast{file: v} = asciicast) when is_binary(v) do
    file_store_path(asciicast, :file)
  end
  def json_store_path(%Asciicast{stdout_frames: v} = asciicast) when is_binary(v) do
    file_store_path(asciicast, :stdout_frames)
  end

  def file_store_path(%Asciicast{id: id, file: fname}, :file) do
    file_store_path(:file, id, fname)
  end
  def file_store_path(%Asciicast{id: id, stdout_frames: fname}, :stdout_frames) do
    file_store_path(:stdout_frames, id, fname)
  end
  def file_store_path(%Asciicast{id: id, stdout_data: fname}, :stdout_data) do
    file_store_path(:stdout, id, fname)
  end
  def file_store_path(%Asciicast{id: id, stdout_timing: fname}, :stdout_timing) do
    file_store_path(:stdout_timing, id, fname)
  end
  def file_store_path(type, id, fname) when is_binary(fname) do
    "asciicast/#{type}/#{id}/#{fname}"
  end

  def snapshot_at(%Asciicast{snapshot_at: snapshot_at, duration: duration}) do
    snapshot_at || duration / 2
  end

  def theme_name(%Asciicast{theme_name: a_theme_name}, %User{theme_name: u_theme_name}) do
    a_theme_name || u_theme_name || @default_theme
  end

  def png_params(%Asciicast{} = asciicast, %User{} = user) do
    %PngParams{snapshot_at: snapshot_at(asciicast),
               theme: theme_name(asciicast, user),
               scale: @default_png_scale}
  end
end
