defmodule N2O.FTP do
  require N2O
  require Kernel

  def root() do
     {:ok, cwd} = :file.get_cwd()
    :filename.join(cwd, :application.get_env(:n2o,:upload,:code.priv_dir(:n2o)))
  end

  def chunk(), do: 256 * 1024

  def fileName(N2O.ftp(filename: fileName)), do: fileName

  def event({:ftpInit, N2O.ftp(id: guid, status: "init", filename: fileName) = ftp, pid}) do
    IO.inspect("DEBUG 1")
    filePath = :filename.join(root(), fileName)
    :filelib.ensure_dir(filePath)
    Supervisor.start_link([], strategy: :one_for_one, name: FTP)

    try do :n2o_pi.stop(:ftp, guid) catch _,_ -> [] end
    initFtp = N2O.ftp(ftp, block: chunk(), offset: 0, data: <<>>)
    N2O.pi(
      module: FTP,
      table: :ftp,
      sup: FTP,
      state: {initFtp, []},
      timeout: 180000,
      name: guid
    )
    |> :n2o_pi.start()
    IO.inspect(pid, label: "PID")
    :erlang.send(pid, {:direct, {:ftp_init, initFtp}})

    apply(:nitro,:wire,["ftp_init('#{guid}', 0, #{chunk()}, '#{fileName}')"])
  end

  def event({:ftpSend, N2O.ftp(id: guid, status: "send") = ftp, pid}) do
    IO.inspect(ftp, label: "DEBUG 2")
    N2O.ftp(size: totalSize, offset: offset, block: block, filename: fileName) = ftp2 =
      try do
        :n2o_pi.send(:ftp, guid, {:send, ftp, pid})
      catch _ -> N2O.ftp(ftp, data: <<>>, block: 0)
      end
    IO.inspect(ftp2, label: "DEBUG 3")
    apply(:nitro,:wire,["ftp_send('#{guid}', #{totalSize}, #{offset}, #{block}, '#{fileName}')"])
  end

  def proc(:init, N2O.pi(state: {N2O.ftp() = ftp, _}) = pi) do
    IO.inspect(ftp, label: "N2O PI INIT")
    {:ok, N2O.pi(pi, state: {ftp, ping(100)})}
  end

  def proc({:check}, N2O.pi(state: {x, timer}) = pi) do
    :erlang.cancel_timer(timer)
    new_timer = ping(30000)
    {:noreply, N2O.pi(pi, state: {x, new_timer})}
  end

  def proc({:send, N2O.ftp(status: "send", data: data, block: block) = ftp, web_pid}, N2O.pi(name: guid, state: {N2O.ftp(filename: fileName, size: totalSize, offset: offset), timer}) = pi) when offset + block >= totalSize do
    filePath = :filename.join(root(), fileName)
    finishFtp = N2O.ftp(ftp, data: <<>>, offset: totalSize, filename: fileName, block: 0)
    case :file.write_file(filePath, :erlang.iolist_to_binary(data), [:append, :raw]) do
      :ok ->
        spawn(fn -> :erlang.send(web_pid, {:direct, {:ftp_finish, finishFtp}}) end)
        spawn(fn -> try do :n2o_pi.stop(:ftp, guid) catch _,_ -> [] end end)
        {:stop, :normal, finishFtp, N2O.pi(pi, state: {finishFtp, timer})}
      {:error, _} = x -> {:reply, x, pi}
    end
  end

  def proc({:send, N2O.ftp(status: "send", data: data, block: block) = ftp, _web_pid}, N2O.pi(state: {N2O.ftp(filename: fileName, offset: offset), timer}) = pi) do
    IO.inspect("N2O PI SEND")
    filePath = :filename.join(root(), fileName)
    nextFtp = N2O.ftp(ftp, data: <<>>, offset: offset + block, filename: fileName)
    case :file.write_file(filePath, :erlang.iolist_to_binary(data), [:append, :raw]) do
      :ok -> {:reply, nextFtp, N2O.pi(pi, state: {nextFtp, timer})}
      {:error, _} = x -> {:reply, x, pi}
    end
  end

  def proc(_, N2O.pi(state: {_, timer}) = pi), do: {:reply, :ok, N2O.pi(pi, state: {[], timer})}

  def ping(milliseconds), do: :erlang.send_after(milliseconds, self(), {:check})

end
