%%
%%  Copyright (C) 2020, 2021 by Xiangdong Zeng <xdzeng96@gmail.com>
%%
%%  This work may be distributed and/or modified under the
%%  conditions of the LaTeX Project Public License, either
%%  version 1.3c of this license or (at your option) any later
%%  version. The latest version of this license is in:
%%
%%    http://www.latex-project.org/lppl.txt
%%
%%  and version 1.3 or later is part of all distributions of
%%  LaTeX version 2005/12/01 or later.
%%
%%  This work has the LPPL maintenance status `maintained'.
%%
%%  The Current Maintainer of this work is Xiangdong Zeng.
%%
\NeedsTeXFormat{LaTeX2e}[2020/02/02]
\ProvidesExplPackage{emoji}{2021/07/17}{0.2.2}{Emoji support in (Lua)LaTeX}

\msg_new:nnn { emoji } { require-luatex }
  { The~'emoji'~package~requires~LuaTeX. }
\sys_if_engine_luatex:F
  { \msg_critical:nn { emoji } { require-luatex } }

\RequirePackage { fontspec }

% Set emoji font. When used in preamble, it will be delayed until the
% `\AtBeginDocument` hook.
% #1: name
% #2: option
\NewDocumentCommand \setemojifont { m o }
  {
    \__emoji_if_preamble:TF
      {
        % The actual `\emoji_font:` command will be defined in the
        % `\AtBeginDocument` hook, but it will precede our checking.
        % So we need to "pretend" that it's defined.
        \cs_set_eq:NN \emoji_font: \prg_do_nothing:
        \AtBeginDocument
      }
      { \use:n }
    {
      \IfValueTF {#2}
        { \emoji_set_font:nn {#1} {#2} }
        { \emoji_set_font:n  {#1} }
    }
  }

% Check if in the preamble.
\prg_set_conditional:Npnn \__emoji_if_preamble: { TF }
  {
    \cs_if_eq:NNTF \@onlypreamble \@notprerr
      { \prg_return_false: }
      { \prg_return_true:  }
  }

% Define `\emoji_font:` when used.
% #1: name
% #2: option
\cs_new_protected:Npn \emoji_set_font:nn #1#2
  { \setfontface \emoji_font: {#1} [ Renderer = HarfBuzz, #2 ] }
\cs_new_protected:Npn \emoji_set_font:n #1
  { \emoji_set_font:nn {#1} {} }

% Detect available emoji fonts from OS or TEXMF tree. Currently support:
%   - Apple Color Emoji
%   - Segoe UI Emoji
%   - Noto Color Emoji
% Remarks:
%   - `\file_if_exist:nTF` is much faster than `\fontspec_font_if_exist:nTF`.
%   - We use environment variable `$WINDIR` for path on Windows.
\cs_new_protected:Npn \__emoji_detect_font:
  {
    \file_if_exist:nTF { /System/Library/Fonts/Apple~Color~Emoji.ttc }
      { \emoji_set_font:n { Apple~Color~Emoji } }
      {
        \file_if_exist:nTF { \c_dollar_str WINDIR/Fonts/seguiemj.ttf }
          { \emoji_set_font:n { Segoe~UI~Emoji } }
          {
            \fontspec_font_if_exist:nTF { Noto~Color~Emoji }
              { \emoji_set_font:n { Noto~Color~Emoji } }
              { \msg_warning:nn { emoji } { no-emoji-font } }
          }
      }
  }
\msg_new:nnn { emoji } { no-emoji-font }
  {
    It~seems~that~you~have~not~declare~an~emoji~font. \\
    You~should~use~"\setemojifont"~to~set~a~font.
  }

% The main command for use emoji.
% #1: name
\NewDocumentCommand \emoji { m }
  {
    % TODO: options
    \emoji_if_name_exist:nTF {#1}
      { \emoji_print:n {#1} }
      { \msg_error:nnn { emoji } { emoji-not-exist } {#1} }
  }
\msg_new:nnn { emoji } { emoji-not-exist }
  {
    The~emoji~name~"#1"~can't~be~found. \\
    Please~check~your~spelling~or~try~another~one.
  }

% Check if an emoji name exists or not.
% #1: name
\prg_new_protected_conditional:Npnn \emoji_if_name_exist:n #1 { T, F, TF }
  {
    \tl_if_exist:cTF { c__emoji_ #1 _tl }
      { \prg_return_true:  }
      { \prg_return_false: }
  }

% In a group, change to the emoji font then use corresponding tl constant.
\cs_new_protected:Npn \emoji_print:n #1
  {
    \group_begin:
      \exp_args:Nv \__emoji_ltj_set_range:n { c__emoji_ #1 _tl }
      \emoji_font:
      \tl_use:c { c__emoji_ #1 _tl }
    \group_end:
  }
\cs_new_eq:NN \__emoji_ltj_set_range:n \use_none:n

% Define new emoji. They are stored in tl constants internally.
% For special characters (#), we first change their catcode to 12 (other),
% then define the tl constant.
% This function is mainly used in `emoji-table.def`, but will be changed to
% print the table in the documentation.
% #1: Code points
% #2: Name
% #3: Aliases
% #4: Description
% #5: Version
\cs_new_protected:Npn \__emoji_def:nnnnn
  {
    \group_begin:
      \char_set_catcode_other:N \#
      \__emoji_def_aux:nnnnn
  }
\cs_new_protected:Npn \__emoji_def_aux:nnnnn #1#2#3#4#5
  {
      \clist_map_inline:nn { #2, #3 }
        { \tl_const:cn { c__emoji_ ##1 _tl } {#1} }
    \group_end:
  }

% These two commands will be used in the documentation. Set to be empty
% commands as placeholders.
\cs_set:Npn \__emoji_group:n    #1 {}
\cs_set:Npn \__emoji_subgroup:n #1 {}

% Input the emoji definition file.
% This file is generated by a Python script from Unicode and GitHub data.
\file_input:n { emoji-table.def }

\cs_new_protected:Npn \__emoji_if_package_loaded:nT #1#2
  { \@ifpackageloaded {#1} {#2} {} }

\AtBeginDocument
  {
    % Compatibility with hyperref.
    % `\emoji{...}` will be turned to a normal character sequence.
    \__emoji_if_package_loaded:nT { hyperref }
      {
        \pdfstringdefDisableCommands
          {
            \cs_set_nopar:Npn \emoji #1
              { \tl_use:c { c__emoji_ #1 _tl } }
          }
      }

    % Compatibility with luatexja.
    % Emoji should be "ALchar" so that the correct font can be used.
    \__emoji_if_package_loaded:nT { luatexja }
      {
        \cs_set_protected:Npn \__emoji_ltj_set_range:n #1
          {
            \tl_set:Nx \l_tmpa_tl
              { \tl_map_function:nN {#1} \__emoji_encode_from_char:n }
            % Range 4 means "characters usually not in Japanese fonts".
            % `\ltjdefcharrange` does not accespt empty value, so we need to
            % remove extra comma.
            \exp_args:Nnx \ltjdefcharrange {4} { \tl_tail:N \l_tmpa_tl }
          }
        % 0-7F are always treated as an ALchar and can't be customized.
        \cs_new:Npn \__emoji_encode_from_char:n #1
          { \int_compare:nNnF {`#1} < {"80} {,`#1} }
      }

    % If the user doesn't set emoji font explicitly, then detect and set default
    % fonts automatically.
    \cs_if_exist:NF \emoji_font:
      { \__emoji_detect_font: }
  }

\endinput