lib/naiveical/helpers.ex

defmodule Naiveical.Helpers do
  @moduledoc """
  Some helper functions.
  """

  @doc """
  Splits a long line into several lines starting with a space.
  """
  def unfold(ical_text) do
    ical_text
    |> String.replace(~r/\r?\n[ \t]/, "")
  end

  @doc """
  https://www.rfc-editor.org/rfc/rfc5545#section-3.1:

   Lines of text SHOULD NOT be longer than 75 octets, excluding the line
   break.  Long content lines SHOULD be split into a multiple line
   representations using a line "folding" technique.  That is, a long
   line can be split between any two characters by inserting a CRLF
   immediately followed by a single linear white-space character (i.e.,
   SPACE or HTAB).  Any sequence of CRLF followed immediately by a
   single linear white-space character is ignored (i.e., removed) when
   processing the content type.

  The fold function splits a string across graphemes if the byte-size of the substring will
  exceed the max_size. It then adds a CRLF and an empty space at the split-point.
  """
  def fold(line, max_size \\ 75) do
    # current_element = List.last(list)
    # if byte_size(current_element + char) > max_size do

    # end
    # String.split("abc", "", parts: 2)
    line
    |> String.split("")
    |> Enum.reduce([""], fn char, acc ->
      [current_element | rest] = acc

      if byte_size(current_element <> char) > max_size do
        [" " <> char] ++ [current_element <> "\r\n"] ++ rest
      else
        [current_element <> char | rest]
      end
    end)
    |> Enum.reverse()
    |> Enum.join()
  end

  @doc """
  Parse a timedate text into DateTime.
  """
  def parse_datetime(datetime_str) do
    datetime_format_str = "{YYYY}{0M}{0D}T{h24}{m}{s}Z"
    naive_datetime_format_str = "{YYYY}{0M}{0D}T{h24}{m}{s}"

    case Timex.parse(datetime_str, datetime_format_str) do
      {:ok, datetime} ->
        {:ok, DateTime.from_naive!(datetime, "Etc/UTC")}

      _ ->
        case Timex.parse(datetime_str, naive_datetime_format_str) do
          {:ok, datetime} ->
            {:ok, datetime}

          _ ->
            {:error, "could not parse #{datetime_str}"}
        end
    end
  end

  def parse_datetime!(datetime_str) do
    case parse_datetime(datetime_str) do
      {:ok, datetime} -> datetime
      {:error, error} -> raise ArgumentError, error
    end
  end

  def parse_datetime(datetime_str, timezone) do
    if is_nil(timezone) or String.length(timezone) == 0 do
      parse_datetime(datetime_str)
    else
      datetime_format_str = "{YYYY}{0M}{0D}T{h24}{m}{s}"

      case Timex.parse(datetime_str, datetime_format_str) do
        {:ok, datetime} -> DateTime.from_naive(datetime, timezone)
      end
    end
  end

  def parse_datetime!(datetime_str, timezone) do
    case parse_datetime(datetime_str, timezone) do
      {:ok, datetime} -> datetime
      {:error, error} -> raise ArgumentError, error
    end
  end

  @doc """
  Parse a timedate text into DateTime.
  """
  def parse_date!(date_str) do
    date_format_str = "{YYYY}{0M}{0D}"

    Timex.parse!(date_str, date_format_str)
  end

  def parse_date(date_str) do
    date_str =
      if String.contains?(date_str, "T") do
        date_str
        |> String.split("T")
        |> List.first()
      else
        date_str
      end

    date_format_str = "{YYYY}{0M}{0D}"

    case Timex.parse(date_str, date_format_str) do
      {:ok, parsed_datetime} -> {:ok, NaiveDateTime.to_date(parsed_datetime)}
      {:error, error} -> {:error, error}
    end
  end

  def is_fullday(attributes, datetime_str) do
    date_format_str = "{YYYY}{0M}{0D}"

    if String.contains?(attributes, "VALUE=DATE") do
      true
    else
      case Timex.parse(datetime_str, date_format_str) do
        {:ok, datetime} -> true
        _ -> false
      end
    end
  end
end