#### This file is part of RemiCharms, providing Crystal bindings to libcurses.
####
#### Copyright (c) 2023 Remilia Scarlet <remilia@posteo.jp>
#### Copyright (c) 2014-2018 Robert Smith <quad@symbo1ics.com>
#### Copyright (c) 2014 Mark Fedurin <hitecnologys@gmail.com>
#### Copyright (c) 2010 Abhishek Reddy <http://abhishek.geek.nz>
####
#### This file includes portions of code from cl-ncurses, an ncurses interface
#### for Common Lisp.  The copyright notices from cl-ncurses are reproduced
#### below.
####
#### Copyright (c) 2003 Nikodemus Siivola
#### Copyright (c) 2004 Marcelo Ramos <mramos@montevideo.com.uy>
#### Copyright (c) 2007 Jacob Gabrielson <jacobg23@pobox.com>
####
#### This program is free software: you can redistribute it and/or modify it
#### under the terms of the GNU Affero General Public License as published by
#### the Free Software Foundation, either version 3 of the License, or (at your
#### option) any later version.
####
#### This program is distributed in the hope that it will be useful, but WITHOUT
#### ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
#### FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
#### License for more details.
####
#### You should have received a copy of the GNU Affero General Public License
#### along with this program.  If not, see <https://www.gnu.org/licenses/>.
#### This file is part of RemiCharms, providing Crystal bindings to libcurses.

{% begin %}
@[Link("ncursesw")] # Specifically use ncursesw so we have Unicode.
lib LibNCurses
  ###
  ### Aliases
  ###

  alias PVoid = Pointer(Void)
  alias CInt = LibC::Int
  alias CUInt = LibC::UInt
  alias PCUInt = Pointer(CUInt)
  alias CUInt8 = UInt8
  alias CInt32 = Int32
  alias CUShort = LibC::UShort
  alias CShort = LibC::Short
  alias PCShort = Pointer(CShort)
  alias CString = Pointer(UInt8)
  alias CChar = LibC::Char
  alias CULong = LibC::ULong

  alias ChType = CUInt
  alias AttrT = ChType
  alias PAttrT = Pointer(AttrT)
  alias MMask = CUInt
  alias PChar = Pointer(CUInt8)
  alias ChStr = Pointer(ChType)
  alias FilePtr = PVoid
  alias ScreenPtr = PVoid
  alias WindowPtr = PVoid
  alias WChar = CUShort
  alias CWString = Pointer(WChar)
  alias NCBool = CUInt8

  ###
  ### Globals
  ###

  $colors = COLORS : CInt
  $color_pairs = COLOR_PAIRS : CInt
  $color_paths = COLOR_PATHS : CInt
  $lines = LINES : CInt
  $cols = COLS : CInt
  $tabsize = TABSIZE : CInt
  $escdelay = ESCDELAY : CInt
  $stdscr : WindowPtr
  $curscr : WindowPtr
  $newscr : WindowPtr
  $acs_map : Pointer(ChType)

  ###
  ### Structs
  ###

  struct MEvent
    id : CShort
    x : CInt
    y : CInt
    z : CInt
    bstate : CULong
  end

  ###
  ### Functions
  ###

  ## addch...
  fun addch(ch : ChType) : CInt
  fun echochar(ch : ChType) : CInt
  fun waddch(win : WindowPtr, ch : ChType) : CInt
  fun wechochar(win : WindowPtr, ch : ChType) : CInt
  fun mvaddch(y : CInt, x : CInt, ch : ChType) : CInt
  fun mvwaddch(win : WindowPtr, y : CInt, x : CInt, ch : ChType) : CInt

  ## addchstr...
  fun addchstr(chstr : ChStr) : CInt
  fun addchnstr(chstr : ChStr, n : CInt) : CInt
  fun waddchstr(win : WindowPtr, chstr : ChStr) : CInt
  fun waddchnstr(win : WindowPtr, chstr : ChStr, n : CInt) : CInt
  fun mvaddchstr(y : CInt, x : CInt, chstr : ChStr) : CInt
  fun mvaddchnstr(y : CInt, x : CInt, chstr : ChStr, n : CInt) : CInt
  fun mvwaddchstr(win : WindowPtr, y : CInt, x : CInt, chstr : ChStr) : CInt
  fun mvwaddchnstr(win : WindowPtr, y : CInt, x : CInt, chstr : ChStr, n : CInt) : CInt

  ## addstr...
  fun addstr(str : CString) : CInt
  fun addnstr(str : CString, n : CInt) : CInt
  fun waddstr(win : WindowPtr, str : CString) : CInt
  fun waddnstr(win : WindowPtr, str : CString, n : CInt) : CInt
  fun mvaddstr(y : CInt, x : CInt, str : CString) : CInt
  fun mvaddnstr(y : CInt, x : CInt, str : CString, n : CInt) : CInt
  fun mvwaddstr(win : WindowPtr, y : CInt, x : CInt, str : CString) : CInt
  fun mvwaddnstr(win : WindowPtr, y : CInt, x : CInt, str : CString, n : CInt) : CInt

  ## attr... color...
  fun attroff(attrs : CInt) : CInt
  fun attron(attrs : CInt) : CInt
  fun attrset(attrs : CInt) : CInt

  fun wattroff(win : WindowPtr, attrs : CInt) : CInt
  fun wattron(win : WindowPtr, attrs : CInt) : CInt
  fun wattrset(win : WindowPtr, attrs : CInt) : CInt

  fun color_set(colorPairNum : CShort, opts : PVoid) : CInt
  fun wcolor_set(win : WindowPtr, color : CShort, opts : PVoid) : CInt

  fun standend : CInt
  fun standout : CInt
  fun wstandend(win : WindowPtr) : CInt
  fun wstandout(win : WindowPtr) : CInt

  fun attr_get(attrs : AttrT, pair : PCShort, opts : PVoid) : CInt
  fun attr_set(attrs : AttrT, pair : PCShort, opts : PVoid) : CInt
  fun wattr_get(win : WindowPtr, attrs : PAttrT, pair : PCShort, opts : PVoid) : CInt
  fun wattr_set(win : WindowPtr, attrs : AttrT, pair : PCShort, opts : PVoid) : CInt

  fun attr_off(attrs : AttrT, opts : PVoid) : CInt
  fun attr_on(attrs : AttrT, opts : PVoid) : CInt
  fun wattr_off(win : WindowPtr, attrs : AttrT, opts : PVoid) : CInt
  fun wattr_on(win : WindowPtr, attrs : AttrT, opts : PVoid) : CInt

  fun chgat(n : CInt, attrs : AttrT, color : CShort, opts : PVoid) : CInt
  fun wchgat(win : WindowPtr, n : CInt, attrs : AttrT, color : CShort, opts : PVoid) : CInt
  fun mvchgat(y : CInt, x : CInt, n : CInt, attrs : AttrT, color : CShort, opts : PVoid) : CInt
  fun mvwchgat(win : WindowPtr, y : CInt, x : CInt, n : CInt, attrs : AttrT, color : CShort, opts : PVoid) : CInt

  ## beep...
  fun beep : CInt
  fun flash : CInt

  ## bkgd...
  fun bkgdset(ch : ChType) : Void
  fun bkgd(ch : ChType) : CInt
  fun wbkgdset(win : WindowPtr, ch : ChType) : Void
  fun wbkgd(win : WindowPtr, ch : ChType) : CInt
  fun getbkgd(win : WindowPtr) : ChType

  ## bkgrnd...
  # TODO
  #
  # C Prototype: int bkgrnd( const cchar_t *wch);
  # C Prototype: int wbkgrnd( WINDOW *win, const cchar_t *wch);
  # C Prototype: void bkgrndset(const cchar_t *wch );
  # C Prototype: void wbkgrndset(WINDOW *win, const cchar_t *wch);
  # C Prototype: int getbkgrnd(cchar_t *wch);
  # C Prototype: int wgetbkgrnd(WINDOW *win, cchar_t *wch);

  ## border... box... line...
  fun c_border = "border"(ls : ChType, rs : ChType, ts : ChType, bs : ChType,
                          tl : ChType, tr : ChType, bl : ChType, br : ChType) : CInt
  fun c_wborder = "wborder"(win : WindowPtr,
                            ls : ChType, rs : ChType, ts : ChType, bs : ChType,
                            tl : ChType, tr : ChType, bl : ChType, br : ChType) : CInt

  fun c_box = "box"(win : WindowPtr, verCh : ChType, horCh : ChType) : CInt

  fun c_hline = "hline"(ch : ChType, n : CInt) : CInt
  fun c_vline = "vline"(ch : ChType, n : CInt) : CInt
  fun c_whline = "whline"(win : WindowPtr, ch : ChType, n : CInt) : CInt
  fun c_wvline = "wvline"(win : WindowPtr, ch : ChType, n : CInt) : CInt
  fun c_mvhline = "mvhline"(y : CInt, x : CInt, ch : ChType, n : CInt) : CInt
  fun c_mvvline = "mvvline"(y : CInt, x : CInt, ch : ChType, n : CInt) : CInt
  fun c_mvwhline = "mvwhline"(win : WindowPtr, y : CInt, x : CInt, ch : ChType, n : CInt) : CInt
  fun c_mvwvline = "mvwvline"(win : WindowPtr, y : CInt, x : CInt, ch : ChType, n : CInt) : CInt

  ## border_set...
  # TODO:
  #
  # cchar_t * ???
  # C Prototype: int border_set(
  #        const cchar_t *ls, const cchar_t *rs,
  #        const cchar_t *ts, const cchar_t *bs,
  #        const cchar_t *tl, const cchar_t *tr,
  #        const cchar_t *bl, const cchar_t *br );
  # C Prototype: int wborder_set(
  #        WINDOW *win,
  #        const cchar_t *ls, const cchar_t *rs,
  #        const cchar_t *ts, const cchar_t *bs,
  #        const cchar_t *tl, const cchar_t *tr,
  #        const cchar_t *bl, const cchar_t *br);
  # C Prototype: int box_set(
  #        WINDOW *win,
  #        const cchar_t *verch,
  #        const cchar_t *horch);
  # C Prototype: int hline_set(
  #        const cchar_t *wch, int n);
  # C Prototype: int whline_set(
  #        WINDOW *win,
  #        const cchar_t *wch, int n);
  # C Prototype: int mvhline_set(
  #         int y, int x,
  #        const cchar_t *wch, int n);
  # C Prototype: int mvwhline_set(
  #        WINDOW *win,
  #         int y, int x,
  #        const cchar_t *wch, int n);
  # C Prototype: int vline_set(
  #        const cchar_t *wch, int n);
  # C Prototype: int wvline_set(
  #        WINDOW *win,
  #        const cchar_t *wch, int n);
  # C Prototype: int mvvline_set(
  #         int y, int x,
  #        const cchar_t *wch, int n);
  # C Prototype: int mvwvline_set(
  #        WINDOW *win,
  #         int y, int x,
  #        const cchar_t *wch, int n);

  ## clear...
  fun erase : CInt
  fun clear : CInt
  fun clrtobot : CInt
  fun clrtoeol : CInt

  fun werase(win : WindowPtr) : CInt
  fun wclear(win : WindowPtr) : CInt
  fun wclrtobot(win : WindowPtr) : CInt
  fun wclrtoeol(win : WindowPtr) : CInt

  ## color...
  fun start_color : CInt
  fun color_pair = "COLOR_PAIR"(pair : CInt) : CInt

  fun init_pair(pair : CShort, f : CShort, b : CShort) : CInt
  fun init_color(color : CShort, r : CShort, g : CShort, b : CShort) : CInt

  fun has_colors : NCBool
  fun can_change_color : NCBool

  fun color_content(color : CShort, r : PCShort, g : PCShort, b : PCShort) : CInt
  fun pair_content(pair : CShort, f : PCShort, b : PCShort) : CInt

  ## default_colors...
  fun use_default_colors : CInt
  fun assume_default_colors(fg : CInt, bg : CInt) : CInt

  ## delch
  fun delch : CInt
  fun wdelch(win : WindowPtr) : CInt

  fun mvdelch(y : CInt, x : CInt) : CInt
  fun mvwdelch(win : WindowPtr, y : CInt, x : CInt) : CInt

  ## deleteln...
  fun deleteln : CInt
  fun insertln : CInt

  fun wdeleteln(win : WindowPtr) : CInt
  fun winsertln(win : WindowPtr) : CInt

  fun insdelln(n : CInt) : CInt
  fun winsdelln(win : WindowPtr, n : CInt) : CInt

  # extensions...
  fun curses_version : CString
  fun use_extended_names(enable : NCBool) : CInt

  ## get_wch...
  ##
  ## These are wrapped to return better values.

  # :nodoc:
  fun c_get_wch = "get_wch"(target : PCUInt) : CInt
  # :nodoc:
  fun c_wget_wch = "wget_wch"(win : WindowPtr, target : PCUInt) : CInt
  # :nodoc:
  fun c_mvget_wch = "mvget_wch"(y : CInt, x : CInt, target : PCUInt) : CInt
  # :nodoc:
  fun c_mvwget_wch = "mvwget_wch"(win : WindowPtr, y : CInt, x : CInt, target : PCUInt) : CInt

  fun unget_wch(wch : WChar) : CInt

  ## get_wstr...
  # TODO:
  #
  # C Prototype: int get_wstr(wint_t *wstr);
  # C Prototype: int getn_wstr(wint_t *wstr, int n);
  # C Prototype: int wget_wstr(WINDOW *win, wint_t *wstr);
  # C Prototype: int wgetn_wstr(WINDOW *win, wint_t *wstr, int n);
  # C Prototype: int mvget_wstr(int y, int x, wint_t *wstr);
  # C Prototype: int mvgetn_wstr(int y, int x, wint_t *wstr, int n);
  # C Prototype: int mvwget_wstr(WINDOW *win, int y, int x, wint_t *wstr);
  # C Prototype: int mvwgetn_wstr(WINDOW *win, int y, int x, wint_t *wstr, int n);

  ## getcchar...
  # TODO:
  #
  # C Prototype: int getcchar(
  #             const cchar_t *wcval,
  #             wchar_t *wch,
  #             attr_t *attrs,
  #             short *color_pair,
  #             void *opts );

  # C Prototype: int setcchar(
  #             cchar_t *wcval,
  #             const wchar_t *wch,
  #             const attr_t attrs,
  #             short color_pair,
  #             void *opts );

  ## getch
  fun getch : CInt
  fun wgetch(win : WindowPtr) : CInt
  fun mvgetch(y : CInt, x : CInt) : CInt
  fun mvwgetch(win : WindowPtr, y : CInt, x : CInt) : CInt

  fun ungetch(ch : CInt) : CInt

  fun getstr(str : CString) : CInt
  fun getnstr(str : CString, n : CInt) : CInt
  fun wgetstr(win : WindowPtr, str : CString) : CInt
  fun wgetnstr(win : WindowPtr, str : CString, n : CInt) : CInt
  fun mvgetstr(y : CInt, x : CInt, str : CString) : CInt
  fun mvgetnstr(y : CInt, x : CInt, str : CString, n : CInt) : CInt
  fun mvwgetstr(win : WindowPtr, y : CInt, x : CInt, str : CString) : CInt
  fun mvwgetnstr(win : WindowPtr, y : CInt, x : CInt, str : CString, n : CInt) : CInt

  {% for fn in [:getcurx, :getcury, :getbegx, :getbegy, :getmaxx, :getmaxy, :getparx, :getpary] %}
    fun {{fn.id}}(win : WindowPtr) : CInt
  {% end %}

  ## in_wch...
  # TODO:
  #
  # C-prototype: int in_wch(cchar_t *wcval);
  # C-prototype: int mvin_wch(int y, int x, cchar_t *wcval);
  # C-prototype: int mvwin_wch(WINDOW *win, int y, int x, cchar_t *wcval);
  # C-prototype: int win_wch(WINDOW *win, cchar_t *wcval);

  ## in_wchstr...
  # TODO:
  #
  # C-prototype: int in_wchstr(cchar_t *wchstr);
  # C-prototype: int in_wchnstr(cchar_t *wchstr, int n);
  # C-prototype: int win_wchstr(WINDOW *win, cchar_t *wchstr);
  # C-prototype: int win_wchnstr(WINDOW *win, cchar_t *wchstr, int n);
  # C-prototype: int mvin_wchstr(int y, int x, cchar_t *wchstr);
  # C-prototype: int mvin_wchnstr(int y, int x, cchar_t *wchstr, int n);
  # C-prototype: int mvwin_wchstr(WINDOW *win, int y, int x, cchar_t *wchstr);
  # C-prototype: int mvwin_wchnstr(WINDOW *win, int y, int x, cchar_t *wchstr, int n);

  ## inch...
  fun inch : ChType
  fun winch(win : WindowPtr) : ChType
  fun mvinch(y : CInt, x : CInt) : ChType
  fun mvwinch(win : WindowPtr, y : CInt, x : CInt) : ChType

  ## inchstr
  fun inchstr(chstr : ChStr) : CInt
  fun inchnstr(chstr : ChStr, n : CInt) : CInt
  fun winchstr(win : WindowPtr, chstr : ChStr) : CInt
  fun winchnstr(win : WindowPtr, chstr : ChStr, n : CInt) : CInt
  fun mvinchstr(y : CInt, x : CInt, chstr : ChStr) : CInt
  fun mvinchnstr(y : CInt, x : CInt, chstr : ChStr, n : CInt) : CInt
  fun mvwinchstr(win : WindowPtr, y : CInt, x : CInt, chstr : ChStr) : CInt
  fun mvwinchnstr(win : WindowPtr, y : CInt, x : CInt, chstr : ChStr, n : CInt) : CInt

  ## initscr...
  fun initscr : WindowPtr
  fun endwin : CInt
  fun isendwin : NCBool
  fun newterm(typ : PChar, outd : FilePtr, infd : FilePtr) : ScreenPtr

  fun set_term(sp : ScreenPtr) : ScreenPtr
  fun delscreen(sp : ScreenPtr) : Void

  ## inopts...

  fun cbreak : CInt
  fun nocbreak : CInt
  fun echo : CInt
  fun noecho : CInt
  fun raw : CInt
  fun noraw : CInt

  fun halfdelay(tenths : CInt) : CInt

  fun intrflush(win : WindowPtr, bf : NCBool) : CInt
  fun keypad(win : WindowPtr, bf : NCBool) : CInt
  fun meta(win : WindowPtr, bf : NCBool) : CInt
  fun nodelay(win : WindowPtr, bf : NCBool) : CInt
  fun notimeout(win : WindowPtr, bf : NCBool) : CInt

  fun noqiflush : CInt
  fun qiflush : CInt

  fun timeout(delay : CInt) : Void
  fun wtimeout(win : WindowPtr, delay : CInt) : Void

  fun typeahead(fd : CInt) : CInt

  ## ins_wch...
  # TODO:
  #
  # C-prototype: int ins_wch(const cchar_t *wch);
  # C-prototype: int wins_wch(WINDOW *win, const cchar_t *wch);
  # C-prototype: int mvins_wch(int y, int x, const cchar_t *wch);
  # C-prototype: int mvwins_wch(WINDOW *win, int y, int x, const cchar_t *wch);

  ## ins_wstr...
  # TODO:
  #
  # C-prototype: int ins_wstr(const wchar_t *wstr);
  # C-prototype: int ins_nwstr(const wchar_t *wstr, int n);
  # C-prototype: int wins_wstr(WINDOW *win, const wchar_t *wstr);
  # C-prototype: int wins_nwstr(WINDOW *win, const wchar_t *wstr, int n);
  # C-prototype: int mvins_wstr(int y, int x, const wchar_t *wstr);
  # C-prototype: int mvins_nwstr(int y, int x, const wchar_t *wstr, int n);
  # C-prototype: int mvwins_wstr(WINDOW *win, int y, int x, const wchar_t *wstr);
  # C-prototype: int mvwins_nwstr(WINDOW *win, int y, int x, const wchar_t *wstr, int n);

  ## insch...
  fun insch(ch : ChType) : CInt
  fun winsch(win : WindowPtr, ch : ChType) : CInt
  fun mvinsch(y : CInt, x : CInt, ch : ChType) : CInt
  fun mvwinsch(y : CInt, x : CInt, win : WindowPtr, ch : ChType) : CInt

  ## insstr...
  fun insstr(str : CString) : CInt
  fun insnstr(str : CString, n : CInt) : CInt
  fun winsstr(win : WindowPtr, str : CString) : CInt
  fun winsnstr(win : WindowPtr, str : CString, n : CInt) : CInt
  fun mvinsstr(y : CInt, x : CInt, str : CString) : CInt
  fun mvinsnstr(y : CInt, x : CInt, str : CString, n : CInt) : CInt
  fun mvwinsstr(win : WindowPtr, y : CInt, x : CInt, str : CString) : CInt
  fun mvwinsnstr(win : WindowPtr, y : CInt, x : CInt, str : CString, n : CInt) : CInt

  ## instr...
  fun instr(str : CString) : CInt
  fun innstr(str : CString, n : CInt) : CInt
  fun winstr(win : WindowPtr, str : CString) : CInt
  fun winnstr(win : WindowPtr, str : CString, n : CInt) : CInt
  fun mvinstr(y : CInt, x : CInt, str : CString) : CInt
  fun mvinnstr(y : CInt, x : CInt, str : CString, n : CInt) : CInt

  ## inwstr...
  # TODO:
  #
  # C-prototype: int inwstr(wchar_t *str);
  # C-prototype: int innwstr(wchar_t *str, int n);
  # C-prototype: int winwstr(WINDOW *win, wchar_t *str);
  # C-prototype: int winnwstr(WINDOW *win, wchar_t *str, int n);
  # C-prototype: int mvinwstr(int y, int x, wchar_t *str);
  # C-prototype: int mvinnwstr(int y, int x, wchar_t *str, int n);
  # C-prototype: int mvwinwstr(WINDOW *win, int y, int x, wchar_t *str);
  # C-prototype: int mvwinnwstr(WINDOW *win, int y, int x, wchar_t *str, int n);

  ## kernel...
  fun def_prog_mode : CInt
  fun def_shell_mode : CInt
  fun reset_prog_mode : CInt
  fun reset_shell_mode : CInt
  fun resetty : CInt
  fun savetty : CInt

  fun curs_set(visibility : CInt) : CInt
  fun napms(ms : CInt) : CInt
  fun keybound(keycode : CInt, count : CInt) : CString
  fun keyok(keycode : CInt, enable : NCBool) : CInt

  ## key_defined...
  # TODO: wrap this function if the local C ncurses library supports EXTENSIONS.
  # C-prototype: int key_defined(const char *definition);
  #(def :int ((definition :cstring))
  #     "key_defined")

  ## mouse...
  # TODO:
  #
  # C-prototype: typedef unsigned long mmask_t;
  #
  # C-prototype: typedef struct
  #        {
  #         short id;         /* ID to distinguish multiple devices */
  #         int x, y, z;      /* event coordinates */
  #         mmask_t bstate;   /* button state bits */
  #        }
  #        MEVENT;
  # C-prototype: int getmouse(MEVENT *event);
  # C-prototype: int ungetmouse(MEVENT *event);
  # C-prototype: mmask_t mousemask(mmask_t newmask, mmask_t *oldmask);
  # C-prototype: bool wenclose(const WINDOW *win, int y, int x);
  # C-prototype: bool mouse_trafo(int* pY, int* pX, bool to_screen);
  # C-prototype: bool wmouse_trafo(const WINDOW* win, int* pY, int* pX, bool to_screen)#
  # C-prototype: int mouseinterval(int erval);

  ## move...
  fun move(y : CInt, x : CInt) : CInt
  fun wmove(win : WindowPtr, y : CInt, x : CInt) : CInt
  fun clearok(win : WindowPtr, bf : NCBool) : CInt
  fun idlok(win : WindowPtr, bf : NCBool) : CInt
  fun leaveok(win : WindowPtr, bf : NCBool) : CInt
  fun scrollok(win : WindowPtr, bf : NCBool) : CInt
  fun idcok(win : WindowPtr, bf : NCBool) : Void
  fun immedok(win : WindowPtr, bf : NCBool) : Void
  fun setscrreg(top : CInt, bot : CInt) : CInt
  fun wsetscrreg(win : WindowPtr, top : CInt, bot : CInt) : CInt
  fun nl : CInt
  fun nonl : CInt

  ## overlay...

  fun overlay(srcwin : WindowPtr, dstwin : WindowPtr) : CInt
  fun overwrite(srcwin : WindowPtr, dstwin : WindowPtr) : CInt

  # TODO:
  #
  # C-prototype: int copywin(const WINDOW *srcwin, WINDOW *dstwin, int sminrow,
  #              int smincol, int dminrow, int dmincol, int dmaxrow,
  #              int dmaxcol, int overlay);

  ## pad...
  fun newpad(nlines : CInt, ncols : CInt) : WindowPtr
  fun subpad(orig : WindowPtr, nlines : CInt, ncols : CInt, beginY : CInt, beginX : CInt) : WindowPtr
  fun prefresh(pad : WindowPtr, pminrow : CInt, pmincol : CInt, sminrow : CInt, smincol : CInt,
               smaxrow : CInt, smaxcol : CInt) : CInt
  fun pnoutrefresh(pad : WindowPtr, pminrow : CInt, pmincol : CInt, sminrow : CInt, smincol : CInt,
                   smaxrow : CInt, smaxcol : CInt) : CInt
  fun pechochar(pad : WindowPtr, ch : ChType) : CInt

  ## print...
  fun mcprint(data : CString, len : CInt) : CInt

  ## printw...
  fun printw(fmt : CString, ...) : CInt
  fun wprintw(win : WindowPtr, fmt : CString, ...) : CInt
  fun mvprintw(y : CInt, x : CInt, fmt : CString, ...) : CInt
  fun mvwprintw(win : WindowPtr, y : CInt, x : CInt, fmt : CString, ...) : CInt

  # TODO:
  #
  # C-prototype: int vwprintw(WINDOW *win, const char *fmt, va_list varglist);
  # C-prototype: int vw_printw(WINDOW *win, const char *fmt, va_list varglist);

  ## refresh...
  fun refresh : CInt
  fun doupdate : CInt
  fun wrefresh(win : WindowPtr) : CInt
  fun wnoutrefresh(win : WindowPtr) : CInt
  fun redrawwin(win : WindowPtr) : CInt
  fun wredrawln(win : WindowPtr, begLine : CInt, numLines : CInt) : CInt

  ## resizeterm...
  fun is_term_resized(lines : CInt, columns : CInt) : NCBool
  fun resize_term(line : CInt, columns : CInt) : CInt
  fun resizeterm(line : CInt, columns : CInt) : CInt

  ## scanw
  # TODO:
  #
  # C-prototype: int scanw(char *fmt, ...);
  # C-prototype: int wscanw(WINDOW *win, char *fmt, ...);
  # C-prototype: int mvscanw(int y, int x, char *fmt, ...);
  # C-prototype: int mvwscanw(WINDOW *win, int y, int x, char *fmt, ...);
  # C-prototype: int vw_scanw(WINDOW *win, char *fmt, va_list varglist);
  # C-prototype: int vwscanw(WINDOW *win, char *fmt, va_list varglist);

  ## scr_dump...
  fun scr_dump(filename : PChar) : CInt
  fun scr_restore(filename : PChar) : CInt
  fun scr_init(filename : PChar) : CInt
  fun scr_set(filename : PChar) : CInt

  ## scroll...
  fun scroll(win : WindowPtr) : CInt
  fun scrl(n : CInt) : CInt
  fun wscrl(win : WindowPtr, n : CInt) : CInt

  ## slk...
  fun slk_init(fmt : CInt) : CInt
  fun slk_set(labnum : CInt, label : CString, fmt : CInt) : CInt
  fun slk_refresh : CInt
  fun slk_noutrefresh : CInt
  fun slk_clear : CInt
  fun slk_restore : CInt
  fun slk_touch : CInt
  fun slk_label(labnum : CInt) : CString
  fun slk_attron(attrs : ChType) : CInt
  fun slk_attroff(attrs : ChType) : CInt
  fun slk_attrset(attrs : ChType) : CInt

  # TODO:
  #
  # C-prototype: int slk_attr_on(attr_t attrs, void* opts);
  # C-prototype: int slk_attr_off(const attr_t attrs, void * opts);
  # C-prototype: int slk_attr_set(const attr_t attrs, short color_pair_number, void* opts);
  # C-prototype: attr_t slk_attr(void);

  fun slk_color(color_pair_number : CShort) : CInt

  ## termattrs...
  fun baudrate : CInt
  fun erasechar : CChar
  fun killchar : CChar

  # TODO:
  #
  # C-prototype: int erasewchar(wchar_t *ch);

  fun has_ic : NCBool
  fun has_il : NCBool

  # TODO:
  #
  # C-prototype: int killwchar(wchar_t *ch);
  # C-prototype: attr_t term_attrs(void);

  fun termattrs : ChType
  fun longname : PChar
  fun termname : PChar

  ## termcap...

  # TODO:
  #
  # C-prototype: extern char PC; extern char *  UP;  extern  char  *  BC;  extern  short ospeed;
  # C-prototype: int tgetent(char *bp, const char *name);

  fun tgetflag(id : PChar) : CInt
  fun tgetnum(id : PChar) : CInt

  # TODO:
  #
  # C-prototype: char *tgetstr(char *id, char **area);
  # C-prototype: char *tgoto(const char *cap, int col, int row);
  # C-prototype: int tputs(const char *str, int affcnt, int (*putc)(int));

  ## terminfo...

  # TODO:
  #
  # C-prototype: int setupterm(char *term, int fildes, int *errret);
  # C-prototype: int setterm(char *term);
  # C-prototype: TERMINAL *set_curterm(TERMINAL *nterm);
  # C-prototype: int del_curterm(TERMINAL *oterm);
  # C-prototype: int restartterm(const char *term, int fildes, int *errret);
  # C-prototype: char *tparm(char *str, ...);
  # C-prototype: int tputs(const char *str, int affcnt, int (*putc)(int));
  # C-prototype: int putp(const char *str);
  # C-prototype: int vidputs(chtype attrs, int (*putc)(int));
  # C-prototype: int vidattr(chtype attrs);
  # C-prototype: int vid_puts(attr_t attrs, short pair, void *opts, int (*putc)(char));
  # C-prototype: int vid_attr(attr_t attrs, short pair, void *opts);
  # C-prototype: int mvcur(int oldrow, int oldcol, int newrow, int newcol);
  # C-prototype: int tigetflag(char *capname);
  # C-prototype: int tigetnum(char *capname);
  # C-prototype: char *tigetstr(char *capname);

  ## touch...
  fun touchwin(win : WindowPtr) : CInt
  fun untouchwin(win : WindowPtr) : CInt
  fun touchline(win : WindowPtr, start : CInt, count : CInt) : CInt
  fun wtouchln(win : WindowPtr, y : CInt, n : CInt, changed : CInt) : CInt
  fun is_wintouched(win : WindowPtr) : NCBool
  fun is_linetouched(win : WindowPtr, line : CInt) : NCBool

  ## util...
  fun unctrl(c : ChType) : PChar
  fun keyname(c : CInt) : PChar
  fun filter : Void
  fun use_env(f : NCBool) : Void
  fun delay_output(ms : CInt) : CInt
  fun flushinp : CInt

  # TODO:
  #
  # C-prototype: char *wunctrl(cchar_t *c);
  # C-prototype: char *key_name(wchar_t w);
  # C-prototype: int putwin(WINDOW *win, FILE *filep);
  # C-prototype: WINDOW *getwin(FILE *filep);

  ## window...
  fun newwin(nlines : CInt, ncols : CInt, beginY : CInt, beginX : CInt) : WindowPtr
  fun delwin(win : WindowPtr) : CInt
  fun wsyncup(win : WindowPtr) : Void
  fun wcursyncup(win : WindowPtr) : Void
  fun wsyncdown(win : WindowPtr) : Void
  fun mvwin(win : WindowPtr, y : CInt, x : CInt) : CInt
  fun mvderwin(win : WindowPtr, y : CInt, x : CInt) : CInt
  fun subwin(orig : WindowPtr, nlines : CInt, ncols : CInt, beginY : CInt, beginX : CInt) : WindowPtr
  fun dupwin(win : WindowPtr) : WindowPtr
  fun syncok(win : WindowPtr, bf : NCBool) : CInt

  ## wresize...
  fun wresize(win : WindowPtr, lines : CInt, columns : CInt) : CInt

  ## mouse...

  # :nodoc:
  fun c_mousemask = "mousemask"(newmask : CULong, oldmask : PVoid) : CULong

  # :nodoc:
  fun c_getmouse = "getmouse"(event : PVoid) : CInt

  # :nodoc:
  fun c_ungetmouse = "ungetmouse"(event : PVoid) : CInt

  fun wenclose(win : WindowPtr, y : CInt, x : CInt) : NCBool
  fun mouse_trafo(py : PVoid, px : PVoid, toScreen : Bool) : NCBool
  fun mouse_wtrafo(win : WindowPtr, py : PVoid, px : PVoid, toScreen : Bool) : NCBool
  fun mouseinterval(erval : CInt) : CInt
  fun has_mouse : NCBool
end
{% end %}

# Low-level bindings to the ncurses library.  This is actually a very, very thin
# wrapper around the `LibNCurses` C bindings.  It is expected that you use this
# module rather than calling directly into `LibNCurses`.
module NCurses
  extend self

  alias WindowPtr = LibNCurses::WindowPtr

  TRUE  = 1u8
  FALSE = 0u8
  ERR   = -1
  OK    = 0

  enum Acs : UInt8
    Ulcorner = 108u8 # Upper left corner
    Llcorner = 109u8 # Lower left corner
    Urcorner = 107u8 # Upper right corner
    Lrcorner = 106u8 # Lower right corner
    Ltee     = 116u8 # Tee pointing right
    Rtee     = 117u8 # Tee pointing left
    Btee     = 118u8 # Tee pointing up
    Ttee     = 119u8 # Tee pointing down
    Hline    = 113u8 # Horizontal line
    Vline    = 120u8 # Vertical line
    Plus     = 110u8 # Large plus or crossover
    S1       = 111u8 # Scan line 1
    S9       = 115u8 # Scan line 9
    Diamond  = 96u8 # Diamond
    Ckboard  = 97u8 # Checker board (stipple)
    Degree   = 102u8 # Degree symbol
    Plminus  = 103u8 # Plus/minus symbol
    Bullet   = 126u8 # Bullet

    # Returns the value of `self` logical OR'ed with `Attribute::AltCharSet`.
    def toBorderChar : LibNCurses::ChType
      Attribute::AltCharSet.value | self.value
    end
  end

  BUTTON1_RELEASED = 0x1
  BUTTON1_PRESSED = 0x2
  BUTTON1_CLICKED = 0x4
  BUTTON1_DOUBLE_CLICKED = 0x8
  BUTTON1_TRIPLE_CLICKED = 0x10

  BUTTON2_RELEASED = 0x40
  BUTTON2_PRESSED = 0x80
  BUTTON2_CLICKED = 0x100
  BUTTON2_DOUBLE_CLICKED = 0x200
  BUTTON2_TRIPLE_CLICKED = 0x400

  BUTTON3_RELEASED = 0x1000
  BUTTON3_PRESSED = 0x2000
  BUTTON3_CLICKED = 0x4000
  BUTTON3_DOUBLE_CLICKED = 0x8000
  BUTTON3_TRIPLE_CLICKED = 0x10000

  BUTTON_CTRL = 0x1000000
  BUTTON_SHIFT = 0x2000000
  BUTTON_ALT = 0x4000000

  ALL_MOUSE_EVENTS = 0x7FFFFFF
  REPORT_MOUSE_POSITION = 0x8000000

  enum Color : Int32 # These were originally constants
    Black   = 0
    Red     = 1
    Green   = 2
    Yellow  = 3
    Blue    = 4
    Magenta = 5
    Cyan    = 6
    White   = 7
  end

  enum KeyCode : Int32
    Break     = 0o401
    SReset    = 0o530
    Reset     = 0o531
    Down      = 0o402
    Up        = 0o403
    Left      = 0o404
    Right     = 0o405
    Home      = 0o406
    Backspace = 0o407
    F0        = 0o410
    F1        = 0o411
    F2        = 0o412
    F3        = 0o413
    F4        = 0o414
    F5        = 0o415
    F6        = 0o416
    F7        = 0o417
    F8        = 0o420
    F9        = 0o421
    F10       = 0o422
    F11       = 0o423
    F12       = 0o424
    Dl        = 0o510
    Il        = 0o511
    Dc        = 0o512
    Ic        = 0o513
    Eic       = 0o514
    Clear     = 0o515
    Eos       = 0o516
    Eol       = 0o517
    Sf        = 0o520
    Sr        = 0o521
    Npage     = 0o522
    Ppage     = 0o523
    STab      = 0o524
    CTab      = 0o525
    CaTab     = 0o526
    Enter     = 0o527
    Print     = 0o532
    Ll        = 0o533
    A1        = 0o534
    A3        = 0o535
    B2        = 0o536
    C1        = 0o537
    C3        = 0o540
    BTab      = 0o541
    Beg       = 0o542
    Cancel    = 0o543
    Close     = 0o544
    Command   = 0o545
    Copy      = 0o546
    Create    = 0o547
    End       = 0o550
    Exit      = 0o551
    Find      = 0o552
    Help      = 0o553
    Mark      = 0o554
    Message   = 0o555
    Move      = 0o556
    Next      = 0o557
    Open      = 0o560
    Options   = 0o561
    Previous  = 0o562
    Redo      = 0o563
    Reference = 0o564
    Refresh   = 0o565
    Replace   = 0o566
    Restart   = 0o567
    Resume    = 0o570
    Save      = 0o571
    SBeg      = 0o572
    SCancel   = 0o573
    SCommand  = 0o574
    SCopy     = 0o575
    SCreate   = 0o576
    Sdc       = 0o577
    Sdl       = 0o600
    Select    = 0o601
    SEnd      = 0o602
    SEol      = 0o603
    SExit     = 0o604
    SFind     = 0o605
    SHelp     = 0o606
    SHome     = 0o607
    Sic       = 0o610
    SLeft     = 0o611
    SMessage  = 0o612
    SMove     = 0o613
    SNext     = 0o614
    SOptions  = 0o615
    SPrevious = 0o616
    SPrint    = 0o617
    SRedo     = 0o620
    SReplace  = 0o621
    SRight    = 0o622
    SRsume    = 0o623
    SSave     = 0o624
    SSuspend  = 0o625
    SUndo     = 0o626
    Suspend   = 0o627
    Undo      = 0o630
    Mouse     = 0o631
    Resize    = 0o632
    Event     = 0o633
  end

  # XSI attributes.  In the ncurses implementation, they are identical to the
  # `A_` attributes.
  @[Flags]
  enum Attribute : UInt32
    Attributes = 0xFFFFFF00
    Normal     = 0x00000000
    Standout   = 0x00010000
    Underline  = 0x00020000
    Reverse    = 0x00040000
    Blink      = 0x00080000
    Dim        = 0x00100000
    Bold       = 0x00200000
    AltCharSet = 0x00400000
    Invis      = 0x00800000
    Protect    = 0x01000000
    Horizontal = 0x02000000 # XSI Curses attr -- not yet used
    Left       = 0x04000000 # XSI Curses attr -- not yet used
    Low        = 0x08000000 # XSI Curses attr -- not yet used
    Right      = 0x10000000 # XSI Curses attr -- not yet used
    Top        = 0x20000000 # XSI Curses attr -- not yet used
    Vertical   = 0x40000000 # XSI Curses attr -- not yet used
  end

  # XSI attributes.  In the ncurses implementation, they are identical to the
  # `A_` attributes.
  @[Flags]
  enum WaAttribute : UInt32
    Attributes = 0xFFFFFF00
    Normal     = 0x00000000
    Standout   = 0x00010000
    Underline  = 0x00020000
    Reverse    = 0x00040000
    Blink      = 0x00080000
    Dim        = 0x00100000
    Bold       = 0x00200000
    AltCharSet = 0x00400000
    Invis      = 0x00800000
    Protect    = 0x01000000
    Horizontal = 0x02000000 # XSI Curses attr -- not yet used
    Left       = 0x04000000 # XSI Curses attr -- not yet used
    Low        = 0x08000000 # XSI Curses attr -- not yet used
    Right      = 0x10000000 # XSI Curses attr -- not yet used
    Top        = 0x20000000 # XSI Curses attr -- not yet used
    Vertical   = 0x40000000 # XSI Curses attr -- not yet used
  end

  def getwch : Tuple(UInt32?, Int32)
    result = c_get_wch(out ch)
    if result == ERR
      {nil, ERR}
    else
      {ch, result}
    end
  end

  def wgetwch(win : WindowPtr) : Tuple(UInt32?, CInt32)
    result = c_wget_wch(win, out ch)
    if result == ERR
      {nil, ERR}
    else
      {ch, result}
    end
  end

  def mvgetwch(y : Int32, x : Int32) : Tuple(UInt32?, Int32)
    result = c_mvget_wch(y, x, out ch)
    if result == ERR
      {nil, ERR}
    else
      {ch, result}
    end
  end

  def mvwgetwch(win : WindowPtr, y : Int32, x : Int32) : Tuple(UInt32?, Int32)
    result = c_mvwget_wch(win, y, x, out ch)
    if result == ERR
      {nil, ERR}
    else
      {ch, result}
    end
  end

  def mousemask(newMask : UInt32) : UInt32
    LibNCurses.c_mousemask(newMask, out oldMask)
    oldMask
  end

  def getmouse : Tuple(UInt64, Int32, Int32, Int32, Int16)
    result = LibNCurses.c_getmouse(out ptr)
    if result == ERR
      raise "Error in curses call getmouse"
    end

    evt = ptr.unsafe_as(Pointer(LibNCurses::MEvent))
    {evt.value.bstate, evt.value.x, evt.value.y, evt.value.z, evt.value.id}
  end

  @[AlwaysInline]
  def border(ls : Int, rs : Int, ts : Int, bs : Int,
             tl : Int, tr : Int, bl : Int, br : Int) : Int32
    LibNCurses.c_border(ls, rs, ts, bs, tl, tr, bl, br)
  end

  @[AlwaysInline]
  def wborder(win : WindowPtr, ls : Int, rs : Int, ts : Int, bs : Int,
              tl : Int, tr : Int, bl : Int, br : Int) : Int32
    LibNCurses.c_wborder(win, ls, rs, ts, bs, tl, tr, bl, br)
  end

  @[AlwaysInline]
  def box(win : WindowPtr, verCh : Int, horCh : Int) : Int32
    LibNCurses.box(win, verCh, horCh)
  end

  @[AlwaysInline]
  def border(ls : Acs, rs : Acs, ts : Acs, bs : Acs,
             tl : Acs, tr : Acs, bl : Acs, br : Acs) : Int32
    altChrSet : UInt32 = Attribute::AltCharSet.value
    border(altChrSet | ls.value,
           altChrSet | rs.value,
           altChrSet | ts.value,
           altChrSet | bs.value,
           altChrSet | tl.value,
           altChrSet | tr.value,
           altChrSet | bl.value,
           altChrSet | br.value)
  end

  @[AlwaysInline]
  def wborder(win : WindowPtr, ls : Acs, rs : Acs, ts : Acs, bs : Acs,
              tl : Acs, tr : Acs, bl : Acs, br : Acs) : Int32
    altChrSet : UInt32 = Attribute::AltCharSet.value
    wborder(win,
            altChrSet | ls.value,
            altChrSet | rs.value,
            altChrSet | ts.value,
            altChrSet | bs.value,
            altChrSet | tl.value,
            altChrSet | tr.value,
            altChrSet | bl.value,
            altChrSet | br.value)
  end

  @[AlwaysInline]
  def box(win : WindowPtr, verCh : Acs, horCh : Acs) : Int32
    altChrSet : UInt32 = Attribute::AltCharSet.value
    box(win, altChrSet | verCh.value, altChrSet | horCh.value)
  end

  @[AlwaysInline]
  def hline(ch : Int, n : Int) : Int32
    LibNCurses.c_hline(ch, n)
  end

  @[AlwaysInline]
  def vline(ch : Int, n : Int) : Int32
    LibNCurses.c_vline(ch, n)
  end

  @[AlwaysInline]
  def whline(win : WindowPtr, ch : Int, n : Int) : Int32
    LibNCurses.c_whline(win, ch, n)
  end

  @[AlwaysInline]
  def wvline(win : WindowPtr, ch : Int, n : Int) : Int32
    LibNCurses.c_wvline(win, ch, n)
  end

  @[AlwaysInline]
  def mvhline(y : Int, x : Int, ch : Int, n : Int) : Int32
    LibNCurses.c_mvhline(y, x, ch, n)
  end

  @[AlwaysInline]
  def mvvline(y : Int, x : Int, ch : Int, n : Int) : Int32
    LibNCurses.c_mvvline(y, x, ch, n)
  end

  @[AlwaysInline]
  def mvwhline(win : WindowPtr, y : Int, x : Int, ch : Int, n : Int) : Int32
    LibNCurses.c_mvwhline(win, y, x, ch, n)
  end

  @[AlwaysInline]
  def mvwvline(win : WindowPtr, y : Int, x : Int, ch : Int, n : Int) : Int32
    LibNCurses.c_mvwvline(win, y, x, ch, n)
  end

  @[AlwaysInline]
  def hline(ch : Acs, n : Int) : Int32
    hline(Attribute::AltCharSet.value | ch.value, n)
  end

  @[AlwaysInline]
  def vline(ch : Acs, n : Int) : Int32
    vline(Attribute::AltCharSet.value | ch.value, n)
  end

  @[AlwaysInline]
  def whline(win : WindowPtr, ch : Acs, n : Int) : Int32
    whline(win, Attribute::AltCharSet.value | ch.value, n)
  end

  @[AlwaysInline]
  def wvline(win : WindowPtr, ch : Acs, n : Int) : Int32
    wvline(win, Attribute::AltCharSet.value | ch.value, n)
  end

  @[AlwaysInline]
  def mvhline(y : Int, x : Int, ch : Acs, n : Int) : Int32
    mvhline(y, x, Attribute::AltCharSet.value | ch.value, n)
  end

  @[AlwaysInline]
  def mvvline(y : Int, x : Int, ch : Acs, n : Int) : Int32
    mvvline(y, x, Attribute::AltCharSet.value | ch.value, n)
  end

  @[AlwaysInline]
  def mvwhline(win : WindowPtr, y : Int, x : Int, ch : Acs, n : Int) : Int32
    mvwhline(win, y, x, Attribute::AltCharSet.value | ch.value, n)
  end

  @[AlwaysInline]
  def mvwvline(win : WindowPtr, y : Int, x : Int, ch : Acs, n : Int) : Int32
    mvwvline(win, y, x, Attribute::AltCharSet.value | ch.value, n)
  end

  {% begin %}
    {% for fn in LibNCurses.methods %}
      {% unless fn.name.stringify.starts_with?("c_") %}
        @[AlwaysInline]
        def {{fn.name}}({{*fn.args}})
          LibNCurses.{{fn.name}}({{*fn.args.map(&.name)}})
        end
      {% end %}
    {% end %}
  {% end %}

  {% begin %}
    {% for i in [[:getyx, :getcury, :getcurx],
                 [:getparyx, :getary, :getparx],
                 [:getmaxyx, :getmaxy, :getmaxx],
                 [:getbegyx, :getbegy, :getbegx]] %}
      @[AlwaysInline]
      def {{i[0].id}}(win : WindowPtr) : Tuple(Int32, Int32)
        %y = {{i[1].id}}(win)
        %x = {{i[2].id}}(win)
        { %y, %x }
      end
    {% end %}
  {% end %}
end
