lib/services/upload.ex

defmodule SEV.UP do
  require N2O
  import Record

  @prefix "priv/sev/upload/"

  defrecordp(:state, id: [], login: [], pass: [], pid: [], msg: [])

  defp spec(i, l, p, msg, pid), do:
    N2O.pi(module: __MODULE__, table: :sev, sup: SEV.Listener, restart: :temporary,
           timeout: :brutal_kill, state: state(id: i, msg: msg, login: l, pass: p, pid: pid), name: "upload#{i}")

  def start(login, pass, id, msg, pid), do:
    (case :n2o_pi.start(spec(id, login, pass, msg, pid)) do {x, _} when is_pid(x) -> send(x, :generate); _ -> [] end)

  def proc(:init, N2O.pi(state: state(id: id, msg: msg, pid: pid)) = pi), do:
    (send(pid, {:upload, :init, id, [], msg}); :erlang.process_flag(:trap_exit, true); :filelib.ensure_dir("#{@prefix}#{id}.uploading"); {:ok, pi})

  def proc(:generate, N2O.pi(state: state(id: i, login: login, pass: pass, msg: msg)) = pi) do
    {from, to, time, body} = SEV.Body.gen(login, msg)
    :ok = :file.write_file("#{@prefix}#{i}.uploading", body, [:raw, :binary])
    SEV.UTL.info('UPLOAD START ~s ~p ~p ~p ~tp', [i, from, to, time, :erlang.element(1, msg)])
    type = :kvs.field(:kvs.field(msg, :msg_type), :type)
    send(self(), {:start, SEV.BUS.startUpload(i, from, to, login, pass, :erlang.size(body), SEV.UTL.sha256(body), type, time), :erlang.size(body)})
    {:noreply, pi}
  end

  def proc({:start, %{:partSize => partSize, :bytesCount => pos, :session => sId}, size}, N2O.pi(state: state(id: id, msg: msg, pid: pid)) = pi), do:
    (send(pid, {:upload, :uploading, id, sId, msg}); send(self(), {:upload, size, pos, partSize, sId, size - pos}); {:noreply, pi})

  def proc({:upload, size, pos, _, sId, _}, N2O.pi(state: state(login: login, pass: pass)) = pi) when pos >= size, do:
    (send(self(), {:final, SEV.BUS.endUploading(login, pass, sId)}); {:noreply, pi})

  def proc({:upload, size, pos, count, sId, rest}, N2O.pi(state: state(id: id, login: login, pass: pass)) = pi) do
    count = :erlang.min(count, rest)
    {:ok, fd} = :file.open("#{@prefix}#{id}.uploading", [:read, :raw, :binary])
    {:ok, data} = :file.pread(fd, pos, count)
    :ok = :file.close(fd)
    SEV.UTL.info('UP ~p', [{:partial, id, size, pos, count, rest}])
    send(self(), {:chunk, SEV.BUS.uploadChunk(login, pass, sId, :base64.encode(data)), size, pos, count, :erlang.size(data)})
    {:noreply, pi}
  end

  def proc({:chunk, %{:session => sId}, size, pos, count, chunkSize}, N2O.pi() = pi) do
    rest = size - (pos + chunkSize)
    send(self(), {:upload, size, pos + chunkSize, :erlang.min(rest, count), sId, rest})
    {:noreply, pi}
  end

  def proc({:final, %{:session => s}}, N2O.pi(state: state(id: id, msg: msg, pid: pid)) = pi) do
    send(pid, {:upload, :uploaded, id, s, msg})
    SEV.UTL.info('UPLOAD FINISH ~s ~tp', [id, s])
    :filelib.ensure_dir("#{@prefix}#{id}/#{id}.xml")
    File.rename("#{@prefix}#{id}.uploading", "#{@prefix}#{id}/#{id}.xml")
    {:stop, :normal, pi}
  end

  def proc(_, pi), do: {:stop, :error, pi}

  def terminate(:normal, _), do: []
  def terminate(reason, N2O.pi(state: state(id: i, pid: pid, msg: msg))), do:
    (send(pid, {:upload, :error, i, [], msg}); SEV.UTL.warning('UPLOAD ~ts TERMINATE REASON ~tp', [i, reason]))

end