lib/excalt/icalendar/helper.ex

defmodule Excalt.Icalendar.Helper do
  def split_by(ical_text, splitter) do
    ical_text = unfold(ical_text)
    {:ok, regex} = Regex.compile("(BEGIN|END)#{splitter}", [:multiline])
  end

  def extract_tag(ical_text, tag) do
    {:ok, regex} = Regex.compile("^#{tag}[;]?(.*):(.*)$", [:multiline])
    ical_text = unfold(ical_text)

    [_, properties, values] = Regex.run(regex, ical_text)
    {tag, String.trim(properties), String.trim(values)}
  end

  def change_value(ical_text, tag, new_value) do
    {tag, properties, values} = extract_tag(ical_text, tag)
    {:ok, regex} = Regex.compile("^#{tag}[;]?.*:.*$", [:multiline])
    [{start_idx, str_len}] = Regex.run(regex, ical_text, return: :index)
    ics_before = String.slice(ical_text, 0, start_idx)
    ics_after = String.slice(ical_text, start_idx + str_len, String.length(ical_text))

    new_line =
      if String.length(properties) > 0 do
        "#{tag};#{properties}:#{new_value}"
      else
        "#{tag}:#{new_value}"
      end

    new_line = fold(new_line)

    ics_before <> "#{new_line}" <> ics_after
  end

  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

  def create_vevent(
        summary,
        dtstart,
        dtend,
        location \\ "",
        description \\ "",
        method \\ "PUBLISH",
        class \\ "PUBLIC"
      ) do
    ical = """
    BEGIN:VCALENDAR
    VERSION:2.0
    PRODID:Excalt
    METHOD:#{method}
    BEGIN:VEVENT
    UID:#{UUID.uuid1()}
    LOCATION:#{location}
    SUMMARY:#{summary}
    DESCRIPTION:#{description}
    CLASS:#{class}
    DTSTART:#{Timex.format(dtstart, "{ISO:Basic:Z}")}
    DTEND:#{Timex.format(dtend, "{ISO:Basic:Z}")}
    DTSTAMP:#{Timex.format(DateTime.utc_now(), "{ISO:Basic:Z}")}
    END:VEVENT
    END:VCALENDAR
    """
  end
end