/*
 * Copyright (c) 2021 Naoaki Iwakiri
 * This program is released under GNU General Public License version 3 or later
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 * SPDX-FileType: SOURCE
 * SPDX-FileName: libcskk.h
 * SPDX-FileCopyrightText: Copyright (c) 2021 Naoaki Iwakiri
 */


#ifndef LIBCSKK_H
#define LIBCSKK_H

/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
#define LIBCSKK_MAJOR 3
#define LIBCSKK_MINOR 2
#define LIBCSKK_PATCH 0


#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>


typedef enum CommaStyle {
  /**
   * Use "、" for ","
   */
  CommaJa,
  /**
   * Use "，" for ","
   */
  CommaEn,
} CommaStyle;

/**
 * Rough design prototype yet
 * SKKの変換モード
 * DDSKK 16.2 マニュアル 4.3 に依る
 */
typedef enum CompositionMode {
  Direct,
  PreComposition,
  PreCompositionOkurigana,
  CompositionSelection,
  Abbreviation,
  Register,
  Completion,
} CompositionMode;

/**
 * Rough design prototype yet
 * SKKの入力モード
 * DDSKK 16.2 マニュアル 4.2 に依る
 */
typedef enum InputMode {
  Hiragana,
  Katakana,
  HankakuKatakana,
  Zenkaku,
  Ascii,
} InputMode;

typedef enum PeriodStyle {
  /**
   * Use "。"  for "."
   */
  PeriodJa,
  /**
   * Use "．"  for "."
   */
  PeriodEn,
} PeriodStyle;

/**
 *
 * CSKK のメインの構造体。このcontextをに対しキー入力を行って変換された状態を得る。
 * 元々C向けAPIを作ることを想定していた多くの関数が*_rsとして公開されている。
 *
 * FIXME: Rustのインタフェースとしてあまり直感的ではないので*_rsのものはCSKKContextの関数にする。
 *
 * TODO: Rustのstructまわりの一部分mutに変更があったら非mutでstateアクセスしているところを直す
 *
 */
typedef struct CskkContext CskkContext;

typedef struct CskkDictionaryFfi CskkDictionaryFfi;

/**
 *
 * In-lib structure of key event
 *
 *
 *
 * String representation of key event is paren enclosed LongModifiers and single KeyName, or just one ShortModifier and one KeyName joined, or single KeyName.
 * LongModifier := "control" | "meta" | "alt" | "lshift" | "rshift" | "shift"
 * ShortModifier := "C-" | "A-" | "M-" | "G-" for ctrl, mod1, meta, mod5 respectively
 * KeyName := ↓
 * https://github.com/xkbcommon/libxkbcommon/blob/master/include/xkbcommon/xkbcommon-keysyms.h
 * https://xkbcommon.org/doc/current/xkbcommon_8h.html#a79e604a22703391bdfe212cfc10ea007
 *
 * e.g.
 * "(control a)" "C-a" "M-Left" "l" "space"
 *
 */
typedef struct CskkKeyEvent CskkKeyEvent;

typedef struct DirectDataFfi {
  /**
   * pollされた時に返す確定済み文字列。
   *
   * 通常のIMEでは[CskkContext::poll_output]で都度取り出して確定文字列として渡すので空である。
   */
  char *confirmed;
  /**
   * まだかな変換を成されていないキー入力の文字列表現
   */
  char *unconverted;
} DirectDataFfi;

typedef struct PreCompositionDataFfi {
  /**
   * pollされた時に返す確定済み文字列。
   *
   * 通常のIMEでは[poll_output]で都度取り出して確定文字列として渡すので空である。
   */
  char *confirmed;
  /**
   * 漢字変換に用いようとしている部分
   */
  char *kana_to_composite;
  /**
   * 漢字変換時に送り仮名として用いようとしている部分
   */
  char *okuri;
  /**
   * かな変換が成されていない入力キーの文字列表現。
   *
   * 現在のCompositionModeがPreCompositionならば漢字変換に用いようとしている部分に付き、
   *
   * PreCompositionOkuriganaならば送り仮名に用いようとしている部分に付く。
   *
   * surrounding_textで指定範囲のみからの変換に対応していない現在、正常な遷移ではRegisterには存在しない。
   */
  char *unconverted;
} PreCompositionDataFfi;

typedef struct CompositionSelectionDataFfi {
  /**
   * 通常、pollされた時に返す確定済み文字列。
   */
  char *confirmed;
  /**
   * 現在選択されている変換候補
   */
  char *composited;
  /**
   * 現在の変換候補に付く送り仮名
   */
  char *okuri;
  /**
   * 現在の候補のアノテーション
   */
  char *annotation;
} CompositionSelectionDataFfi;

typedef struct RegisterDataFfi {
  /**
   * pollされた時に返す確定済み文字列。
   *
   * 通常のIMEでは[poll_output]で都度取り出して確定文字列として渡すので空である。
   */
  char *confirmed;
  /**
   * 漢字変換に用いようとしている部分
   */
  char *kana_to_composite;
  /**
   * 漢字変換時に送り仮名として用いようとしている部分
   */
  char *okuri;
  /**
   * 漢字変換時に後に付く部分。auto-start-henkanの「。」等
   */
  char *postfix;
} RegisterDataFfi;

typedef struct CompleteDataFfi {
  /**
   * 通常、pollされた時に返す確定済み文字列。
   */
  char *confirmed;
  /**
   * 補完に用いようとしている元の部分
   */
  char *complete_origin;
  /**
   * 補完の送り仮名として用いようとしている部分。v3.0.0では送り仮名付きからの補完は未実装。
   */
  char *origin_okuri;
  /**
   * 現在選択されている変換候補の見出し。
   */
  char *completed_midashi;
  /**
   * 現在選択されている変換候補。見出しの候補ではなく変換候補そのもの。
   */
  char *completed;
  /**
   * 現在の変換候補に付く送り仮名。v3.0.0では送り仮名付きからの補完は未実装。
   */
  char *okuri;
  /**
   * 現在の候補のアノテーション
   */
  char *annotation;
} CompleteDataFfi;

/**
 *
 * 入力途上の状態を返す構造体群
 * CompositionModeに合わせた構造体で、各要素は存在すれば\0終端のUTF-8文字配列、存在しなければNULLが含まれる。
 *
 */
typedef enum CskkStateInfoFfi_Tag {
  DirectStateInfo,
  /**
   * PreCompositionはAbbreviationモードを含む。
   */
  PreCompositionStateInfo,
  PreCompositionOkuriganaStateInfo,
  CompositionSelectionStateInfo,
  RegisterStateInfo,
  CompleteStateInfo,
} CskkStateInfoFfi_Tag;

typedef struct CskkStateInfoFfi {
  CskkStateInfoFfi_Tag tag;
  union {
    struct {
      struct DirectDataFfi direct_state_info;
    };
    struct {
      struct PreCompositionDataFfi pre_composition_state_info;
    };
    struct {
      struct PreCompositionDataFfi pre_composition_okurigana_state_info;
    };
    struct {
      struct CompositionSelectionDataFfi composition_selection_state_info;
    };
    struct {
      struct RegisterDataFfi register_state_info;
    };
    struct {
      struct CompleteDataFfi complete_state_info;
    };
  };
} CskkStateInfoFfi;

typedef struct CskkRulesFfi {
  char *id;
  char *name;
  char *description;
} CskkRulesFfi;

/**
 *
 * i番目の候補として確定し、Directモードに移行する。
 * 現在のコンテキストが▼モード(CompositionSelection)でない場合や、iがリスト範囲外になる場合は、無視してfalseを返す。
 *
 */
bool skk_context_confirm_candidate_at(struct CskkContext *context,
                                      unsigned int i);

/**
 *
 * Get the composition mode of current state.
 *
 */
enum CompositionMode skk_context_get_composition_mode(struct CskkContext *context);

/**
 *
 * 現在のcandidate listの長さを返す。
 *
 */
unsigned int skk_context_get_current_candidate_count(const struct CskkContext *context);

/**
 * 何番目の候補を指しているかを返す
 * 現在候補が存在しない場合や、返せない場合、適当に負数を返す
 */
int skk_context_get_current_candidate_cursor_position(struct CskkContext *context);

/**
 *
 * context内の現在のcandidate listとして候補のリストをUTF-8のC形式のNULL終端文字列で返す。
 *
 * 現在のリストのoffsetから最大でbuf_sizeまでをcandidate_buf内に入れる。
 * 実際に返した個数は返り値として返す。
 *
 * 失敗時には返り値-1を返す。
 * 失敗時にはcandidate_bufの中身については何も保証されず、freeする必要はない。
 *
 * # Safety
 *
 * candidate_bufに*c_charがbuf_size分の容量があることはCaller側が保証しなければならない。
 * candidate_bufに入れられた文字列はCallerがskk_free_candidate_listで解放しなければならない。
 * candidate_bufに入れられた文字列の長さは変更してはならない。
 *
 */
int skk_context_get_current_candidates(const struct CskkContext *context,
                                       char **candidate_buf,
                                       unsigned int buf_size,
                                       unsigned int offset);

/**
 *
 * 現在の漢字変換対象文字列をUTF-8のC文字列で返す。
 * Cで扱えない文字列などの失敗時にはNULLが返る。
 *
 * # Safety
 * 返り値はCallerがskk_free_stringしなければならない。
 *
 */
char *skk_context_get_current_to_composite(const struct CskkContext *context);

/**
 *
 * Get the input mode of current state.
 *
 */
enum InputMode skk_context_get_input_mode(struct CskkContext *context);

/**
 *
 * preedit文字列を返す。
 * 返り値は\0終端のUTF-8文字配列。C++20で言うchar8_t*
 * 失敗時にはNULLを返す。
 *
 * # Safety
 * 返り値のポインタの文字列を直接編集して文字列長を変えてはいけない。
 * 返り値はcallerがskk_free_stringしないとメモリリークする。
 * ueno/libskkと違う点なので注意が必要
 *
 */
char *skk_context_get_preedit(const struct CskkContext *context);

/**
 *
 * preedit状態の配列を返す。
 * 最初のものが一番外側の状態で、Registerモードの時には後にその内側の状態が続く。
 * 結果の配列の長さは引数のstate_stack_lenにセットする。
 * 失敗時にはNULLを返す。
 *
 * # Safety
 * 返り値の内容およびポインタは変更してはならない。
 * 返り値はcallerがskk_free_preedit_detailで解放しないとメモリリークする
 * state_stack_lenが有効なunsigned intへのポインタでないと予期せぬ動作を起こす。
 *
 */
struct CskkStateInfoFfi *skk_context_get_preedit_detail(const struct CskkContext *context,
                                                        unsigned int *state_stack_len);

/**
 *
 * Get emphasizing range of preedit.
 * offset: starting offset (in UTF-8 chars) of underline
 * nchars: number of characters to be underlined
 *
 * # Deprecated
 * Fancy formatting will be delegated to IME in favor of [skk_context_get_preedit_detail].
 *
 * Deprecated interface might be deleted on major update.
 *
 * # Safety
 *
 * offset, nchars must be a valid pointer to int type that memory is allocated by the caller.
 */
void skk_context_get_preedit_underline(struct CskkContext *context, int *offset, int *nchars);

/**
 *
 * Returns newly allocated CSKKContext.
 * On error, returns NULL.
 *
 * # Safety
 * Caller have to retain the pointer returned from this function
 * Caller must free it using skk_free_context when non-NULL pointer is returned.
 * dictionary_array must have at least dictionary_count number of CskkDictionary
 *
 */
struct CskkContext *skk_context_new(struct CskkDictionaryFfi *const *dictionary_array,
                                    size_t dictionary_count);

/**
 *
 * Returns newly allocated CSKKContext.
 * On error, still tries to return an empty context that can convert nothing.
 *
 * Try to use skk_context_new when possible.
 * This interface is for IMEs that cannot fail on creating context.
 *
 * # Safety
 * Caller have to retain the pointer returned from this function
 * Caller must free it using skk_free_context.
 * dictionary_array must have at least dictionary_count number of CskkDictionary
 *
 */
struct CskkContext *skk_context_new_with_empty_fallback(struct CskkDictionaryFfi *const *dictionary_array,
                                                        size_t dictionary_count);

/**
 * 現在のoutputをpollingする。
 *
 * 成功時にはRustでallocateされたCの文字列として扱える(=ヌル終端のある)UTF-8のバイト配列を返す。
 * C++20のchar8_t*のようなもの。
 * エラー時にはNULLを返す。
 *
 * # Safety
 * 返り値のポインタの文字列を直接編集して文字列長を変えてはいけない。
 * 返り値はcallerがskk_free_stringしないと実体がメモリリークする。
 *
 */
char *skk_context_poll_output(struct CskkContext *context);

/**
 *
 * 1入力を処理する。CSKKコンテキスト内でキーが受理された場合はtrueを返す。
 * CSKKでは入力としてもコマンドとしても受けつけられなかった場合はfalseを返す。
 *
 * keyeventはfreeされる。
 *
 * # Safety
 * context and keyevent must be a valid non-null pointer created from this library.
 * keyevent will be freed. keyevent must not be reused after this function call
 *
 */
bool skk_context_process_key_event(struct CskkContext *context,
                                   struct CskkKeyEvent *keyevent);

/**
 * Only for library test purpose. Do not use.
 * # Safety
 *
 * This function must be called by a valid C string terminated by a NULL.
 */
bool skk_context_process_key_events(struct CskkContext *context, char *keyevents_cstring);

/**
 *
 * Reset the context. Doesn't change the inputmode but flushes all inputs and compisitionmode will be reset to direct
 *
 */
void skk_context_reset(struct CskkContext *context);

/**
 *
 * save current dictionaries
 *
 */
void skk_context_save_dictionaries(struct CskkContext *context);

/**
 * i番目の候補を指す。確定はしない。
 * 候補が負の方向に範囲外の場合、▼モードから▽モードに戻る。
 * 候補が正の方向に範囲外の場合、最後の候補を指し辞書登録モードに移る。
 *
 * 現在のコンテキストが▼モード(CompositionSelection)でない場合、無視してfalseを返す。
 */
bool skk_context_select_candidate_at(struct CskkContext *context,
                                     int i);

/**
 * Set auto start henkan keywords
 *
 * # Safety
 * keywords_array must be an pointer of C-style array that contains at least keywords_count number of C string.
 *
 */
void skk_context_set_auto_start_henkan_keywords(struct CskkContext *context,
                                                const char *const *keywords_array,
                                                size_t keywords_count);

/**
 *
 * Comma style を設定する
 *
 */
void skk_context_set_comma_style(struct CskkContext *context, enum CommaStyle comma_style);

/**
 *
 * Set dictionaries to context.
 *
 * # Safety
 * dictionary_array must have at least dictionary_count number of CskkDictionary
 */
void skk_context_set_dictionaries(struct CskkContext *context,
                                  struct CskkDictionaryFfi *const *dictionary_array,
                                  size_t dictionary_count);

/**
 *
 * Set the input mode of current state.
 *
 */
void skk_context_set_input_mode(struct CskkContext *context, enum InputMode input_mode);

/**
 *
 * Period style を設定する
 *
 */
void skk_context_set_period_style(struct CskkContext *context, enum PeriodStyle period_style);

/**
 *
 * Sets the conversion rule to the given rule_name.
 * Returns 0 on success, -1 on error.
 *
 * # Safety
 * rule_name must be a valid pointer to a C-style string with string length smaller than 2^32 - 1
 *
 */
int skk_context_set_rule(struct CskkContext *context, const char *rule_name);

/**
 *
 * Creates an empty dictionary. Returns the pointer of it.
 * On error returns NULL pointer.
 *
 * # Safety
 * Dictionary must be freed by skk_free_dictionary
 * If not, memory leaks.
 *
 */
struct CskkDictionaryFfi *skk_empty_dict_new(void);

/**
 *
 * Creates a skk static file dict based on the path_string. Returns the pointer of it.
 * Returns NULL on error. In error case, you don't have to free it.
 *
 * # Safety
 * c_path_string and c_encoidng must be a valid c string that terminates with \0.
 *
 * Dictionary must be freed by skk_free_dictionary
 * If not, memory leaks.
 *
 */
struct CskkDictionaryFfi *skk_file_dict_new(const char *c_path_string,
                                            const char *c_encoding,
                                            bool use_for_completion);

/**
 *
 * candidate_listの各々の候補を解放する。
 *
 * # Safety
 *
 * candidate_list_ptr は必ずskk_context_get_current_candidatesで候補を取得した配列のポインタでなければならない。
 * sizeは取得した候補の全数と一致している必要がある。
 *
 */
void skk_free_candidate_list(char **candidate_list_ptr,
                             unsigned int size);

/**
 *
 * CskkContextを解放する。
 *
 * # Safety
 *
 * context_ptr は必ずCskkContextのポインタでなければならない。
 *
 */
void skk_free_context(struct CskkContext *context_ptr);

/**
 *
 * CskkDictionaryを解放する。
 *
 * # Safety
 *
 * dictionary_ptr は必ずCskkDictionaryのポインタでなければならない。
 *
 */
void skk_free_dictionary(struct CskkDictionaryFfi *dictionary_ptr);

/**
 *
 * preedit_detailsを解放する。
 *
 * # Safety
 * ptrとlengthはskk_context_get_preedit_detailsの返り値でなければならない。
 *
 */
void skk_free_preedit_detail(struct CskkStateInfoFfi *ptr, unsigned int length);

/**
 *
 * Free the rules given from [skk_context_get_rules]
 *
 * # Safety
 * 引数はいずれも[skk_context_get_rules]で得られるペアでなければならない。
 *
 */
void skk_free_rules(struct CskkRulesFfi *rules_ptr, unsigned int length);

/**
 *
 * cskk libraryが渡したC言語文字列をfreeする。
 *
 * # Safety
 *
 * CSKKライブラリで返したC言語文字列のポインタ以外を引数に渡してはいけない。
 * 他で管理されるべきメモリを過剰に解放してしまう。
 *
 */
void skk_free_string(char *ptr);

/**
 *
 * returns a pointer to an Rules array. Retruns NULL in error.
 * sets the total number of entries in the array in the given `length` parameter.
 * The parameter is required to free the returned array later.
 *
 * 失敗時にはNULLを返し、lengthの内容は保証されない。
 * 失敗時にはfreeする必要はない。
 *
 * # Safety
 *
 * the parameter `length` must be a pointer to a 32 bit unsigned int allocated in the caller side.
 * caller must not modify any field in the returned struct in the array.
 * caller must free the returned array by [skk_free_rules] API.
 *
 */
struct CskkRulesFfi *skk_get_rules(unsigned int *length);

/**
 *
 * Create a cskk keyevent type
 * keysym: u32  "X11 Window System Protocol" Appendix A based keysym code.
 * modifier: Fcitx modifier
 * is_release: True if this event for releasing the key
 *
 * # Safety
 * Must use this return value with process_key_event. If not, memory leaks.
 *
 */
struct CskkKeyEvent *skk_key_event_new_from_fcitx_keyevent(uint32_t keysym,
                                                           uint32_t modifier,
                                                           bool is_release);

/**
 *
 * Get library version at the compile time.
 * 失敗時にはNULLを返す。
 *
 * # Safety
 * 返り値のポインタの文字列を直接編集して文字列長を変えてはいけない。
 * 返り値はcallerがskk_free_stringしないと実体がメモリリークする。
 *
 */
char *skk_library_get_version(void);

/**
 *
 * Creates a skk read and write user dict based on the path_string. Returns the pointer of it.
 * Returns NULL on error. In error case, you don't have to free it.
 *
 * # Safety
 * c_path_string and c_encoidng must be a valid c string that terminates with \0.
 *
 * Dictionary must be freed by skk_free_dictionary
 * If not, memory leaks.
 *
 */
struct CskkDictionaryFfi *skk_user_dict_new(const char *c_path_string,
                                            const char *c_encoding,
                                            bool use_for_completion);

#endif  /* LIBCSKK_H */
