;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
define new-rename-window(listbox item-index field)
  define win
    flexgui:parse-layout
      window-frame:
        style: 'normal-window
        title-bar: text: string-append("rename " field)
        min-width: 128
        pref-width: 440
        on-key-down:
          lambda (self evt)
            if sdl:names-equal?("escape" evt[keysym:])
              then
                self[close-window:]
                evt[eat!:]
        on-broadcast:
          lambda (self evt btype)
            if {evt[source:] eq? listbox}
              then
                evt[eat!:]
                self[close-window:]
        ;
        v-container: inset: 2
          spring: default:
          h-container:
            label:
              hotkey: "M-e"
              text: "nam~e:"
              no-expand: x:
            editline:
              id: "name"
              text:
                procedure:
                  lambda (self)
                    define item listbox[item: item-index]
                    if {field eq? 'title}
                      item[title:]
                      item[author:]
          ;
          spring: default:
          padding: default:
          h-container:
            spring: default:
            button: text: "O~k" pref-width: 42
              hotkey: "M-k"
              default: #t
              action:
                lambda (self)
                  define item listbox[item: item-index]
                  define game-id item[game-id:]
                  define text self[find-widget: "name"][text:]
                  if {not empty-string?(text)}
                    then
                      assert {fixnum? game-id} "invalid game id"
                      cond
                        item[author-item?:]
                          assert {field eq? 'author}
                        item[game-item?:]
                          assert {field eq? 'title}
                        else error("wtf?!")
                      if {field eq? 'title}
                        {item[title:] := text}
                        {item[author:] := text}
                      define stmt cave-db[statement:
                        (if {field eq? 'title}
                          "UPDATE games SET title=:value WHERE game_id=:game_id"
                          "UPDATE games SET author=:value WHERE game_id=:game_id")]
                      {stmt[":game_id"] := game-id}
                      {stmt[":value"] := text}
                      stmt[execute:]
                      stmt[close:]
                      self[close-window:]
            spring: default:
  ;;
  flexgui:append-window win
  flexgui:center-window win


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
define new-remark-window(listbox item-index)
  define win
    flexgui:parse-layout
      window-frame:
        style: 'normal-window
        title-bar: text: "Edit Game User Remark"
        min-width: 128
        pref-width: 440
        on-key-down:
          lambda (self evt)
            if sdl:names-equal?("escape" evt[keysym:])
              then
                evt[eat!:]
                self[close-window:]
        on-broadcast:
          lambda (self evt btype)
            if {evt[source:] eq? listbox}
              then
                evt[eat!:]
                self[close-window:]
        ;
        v-container: inset: 2
          spring: default:
          h-container:
            label:
              hotkey: "M-r"
              text: "~remark:"
              no-expand: x:
            editline:
              id: "remark"
              text:
                procedure:
                  lambda (self)
                    define item listbox[item: item-index]
                    assert item[game-item?:]
                    get-game-remarks item[game-id:]
          ;
          spring: default:
          padding: default:
          h-container:
            spring: default:
            button: text: "O~k" pref-width: 42
              hotkey: "M-k"
              default: #t
              action:
                lambda (self)
                  define item listbox[item: item-index]
                  assert item[game-item?:]
                  define game-id item[game-id:]
                  assert {fixnum? game-id} "invalid game id"
                  define text self[find-widget: "remark"][text:]
                  set-game-remarks game-id text
                  {item[comment:] := text}
                  self[close-window:]
            spring: default:
  ;;
  flexgui:append-window win
  flexgui:center-window win


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
define new-delimiter-item()
  method-lambda delimiter-item
    author-item?: (self)
      #f
    game-item?: (self)
      #f
    ;;
    height: (self)
      3
    ;;
    selectable?: (self)
      #f
    ;;
    paint: (self widget x y wdt index selected)
      lgfx:fill-rect x {y + 1} wdt 1 #@color:#0ff
    ;;
    handle-event: (self widget evt)
      #void
    ;;
    game-id: (self)
      #f


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
define new-author-item(game-id author my-index)
  define game-count 0
  define expanded #t
  ;;
  method-lambda author-item
    author-item?: (self)
      #t
    game-item?: (self)
      #f
    ;;
    height: (self)
      lgfx:text-height()
    ;;
    selectable?: (self)
      #t
    ;;
    paint: (self widget x y wdt index selected)
      define ntext string-append(" (" number->string(game-count) ")")
      define hgt self[height:]
      cond
        selected
          lgfx:fill-rect x y wdt hgt widget[style-color-prop: "back-selection"]
          inc! x 2
          inc! y lgfx:text-ascender()
          lgfx:print-str x y author widget[style-color-prop: "text-selection"]
          lgfx:print-str {x + lgfx:text-width(author)} y ntext widget[style-color-prop: "text-selection"]
        else
          inc! x 2
          inc! y lgfx:text-ascender()
          lgfx:print-str x y author #@color:#fff
          lgfx:print-str {x + lgfx:text-width(author)} y ntext #@color:#f80
    ;;
    %update-expanded: (self widget)
      define idx my-index
      define left game-count
      define item
      if {positive? left}
        player-prop-set! string-append("expanded:" author) expanded
      while {positive? left}
        inc! idx
        {item := widget[item: idx]}
        {item[%expanded:] := expanded}
        widget[item-refresh: idx]
        dec! left
    ;;
    expanded-set!: (self widget value)
      assert {boolean? value}
      if {expanded <> value}
        then
          {expanded := value}
          self[%update-expanded: widget]
    ;;
    handle-event: (self widget evt)
      if (and evt[alive?:]
              {evt[type:] eq? 'KEY-DOWN}
              widget[active?:])
        cond
          sdl:names-equal?("kp-plus" evt[keysym:])
            evt[eat!:]
            {self[expanded: widget] := #t}
          sdl:names-equal?("kp-minus" evt[keysym:])
            evt[eat!:]
            {self[expanded: widget] := #f}
          sdl:names-equal?("space" evt[keysym:])
            evt[eat!:]
            {self[expanded: widget] := {not expanded}}
          else #void
    ;;
    game-id: (self)
      game-id
    ;;
    author: (self)
      author
    ;;
    author-set!: (self value)
      assert {string? value} "author should be a string"
      {author := value}
    ;;
    inc-games!: (self)
      inc! game-count


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
define new-game-item(game-id author title comment expanded)
  if {not empty-string?(comment)}
    {comment := string-append("\x11:\x02" comment)}
  ;;
  method-lambda game-item
    author-item?: (self)
      #f
    game-item?: (self)
      #t
    ;;
    selectable?: (self)
      expanded
    ;;
    height: (self)
      if expanded
        lgfx:text-height()
        0
    ;;
    paint: (self widget x y wdt index selected)
      if expanded
        then
          define hgt self[height:]
          if selected
            lgfx:fill-rect x y wdt hgt widget[style-color-prop: "back-selection"]
          inc! x 8
          dec! wdt 8
          inc! y lgfx:text-ascender()
          define epos {lgfx:text-width(title) + 6}
          define rempos max(epos {wdt - lgfx:text-width(comment) - 1})
          cond
            selected
              define cc widget[style-color-prop: "text-selection"]
              lgfx:print-str x y title cc
              lgfx:print-str {x + rempos} y comment cc
            else
              define stl
              {stl := widget[style-color-prop: (if even?(index) "text-even" "text-odd")]}
              lgfx:print-str x y title stl
              lgfx:print-str {x + rempos} y comment #@color:#0c0
    ;;
    handle-event: (self widget evt)
      #void
    ;;
    game-id: (self)
      game-id
    ;;
    author: (self)
      author
    ;;
    author-set!: (self value)
      assert {string? value} "author should be a string"
      {author := value}
    ;;
    title: (self)
      title
    ;;
    title-set!: (self value)
      assert {string? value} "title should be a string"
      {title := value}
    ;;
    comment: (self)
      comment
    ;;
    comment-set!: (self value)
      assert {string? value} "comment should be a string"
      {comment := value}
    ;;
    %expanded-set!: (self value)
      assert {boolean? value}
      {expanded := value}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define selected-game-id #f)

define new-game-select-window()
  flexgui:parse-layout
    window-frame:
      style: 'normal-window
      title-bar: text: "Game List"
      activate-widget: "lbox-0" ;; activate this widget when the window is created
      ;pref-width: 400
      pref-width: flexgui:lgfx:window-width
      pref-height: flexgui:lgfx:window-height
      v-container: inset: 2
        h-container:
          label: text: "File nam~e:"
            activate-id: "fname-editor"
            hotkey: "M-e"
          padding: size: 2
          editline: flex: 1
            id: "fname-editor"
            read-only: #t
            on-broadcast:
              lambda (self evt btype)
                define lbox self[find-widget: "lbox-0"]
                if {evt[source:] eq? lbox}
                  then
                    case btype
                      (current-item-set!:)
                        define tbox self[find-widget: "tbox-0"]
                        define item lbox[item: evt[user-data:]]
                        define game-id item[game-id:]
                        if {{fixnum? game-id} and self[active-window?:]}
                          player-prop-set! "selected-game" game-id
                        if item[game-item?:]
                          then
                            assert {fixnum? game-id}
                            {self[text:] := get-game-file-name(game-id)}
                            define story get-game-story(game-id)
                            define remark get-game-remark(game-id)
                            cond
                              {{not empty-string?(story)} and {not empty-string?(remark)}}
                                {tbox[text:] := string-append(story "\n\n==================================\n\n" remark)}
                              {not empty-string?(story)}
                                {tbox[text:] := story}
                              {not empty-string?(remark)}
                                {tbox[text:] := string-append("==================================\n\n" remark)}
                              else
                                {tbox[text:] := ""}
                          else
                            {tbox[text:] := ""}
                            {self[text:] := ""}
                      else #void
        ;
        padding: default:
        h-container:
          h-container:
            v-scrollbar:
              host-id: "tbox-0"
            textbox: flex: 1
              id: "tbox-0"
              hotkey: "M-t"
              pref-height: {{lgfx:text-height() * 12} + 4}
        ;
        padding: default:
        h-container: flex: 1
          h-container:
            flex: 1
            v-scrollbar:
              host-id: "lbox-0"
            listbox: flex: 1
              id: "lbox-0"
              hotkey: "M-b"
              action:
                lambda (self)
                  ;new-message-window("wow!")
                  define item self[item: self[current-item:]]
                  if item[game-item?:]
                    then
                      gset! selected-game-id item[game-id:]
                      gset! selected-cave-index #f
                      gset! action-replay-data #nil
                      gset! action-replay-otypes #nil
                      open-cave-selector(selected-game-id self)
          ;
          padding: default:
          v-container:
            label: text: "EDIT"
            padding: default:
            button: text: "~author"
              hotkey: "M-a"
              action:
                lambda (self)
                  define listbox self[find-widget: "lbox-0"]
                  listbox[activate-self!:]
                  define item listbox[item: listbox[current-item:]]
                  if {item[game-item?:] or item[author-item?:]}
                    new-rename-window listbox listbox[current-item:] 'author
            ;
            padding: default:
            button: text: "t~itle"
              hotkey: "M-i"
              action:
                lambda (self)
                  define listbox self[find-widget: "lbox-0"]
                  listbox[activate-self!:]
                  define item listbox[item: listbox[current-item:]]
                  if item[game-item?:]
                    new-rename-window listbox listbox[current-item:] 'title
            ;
            padding: default:
            button: text: "~remark"
              hotkey: "M-r"
              action:
                lambda (self)
                  define listbox self[find-widget: "lbox-0"]
                  listbox[activate-self!:]
                  define item listbox[item: listbox[current-item:]]
                  if item[game-item?:]
                    new-remark-window listbox listbox[current-item:]
            padding: default:
            spring: default:
        ;
        ;spring: default:
        padding: default:
        h-container:
          button: text: "~Options"
            hotkey: "M-o"
            action:
              lambda (self)
                new-options-window()
          spring: default:
          padding: default:
          button: text: "~Quit"
            hotkey: "M-q"
            escape: #t
            action:
              lambda (self)
                (sdl:post-quit-event)


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
define fill-listbox(win lbid)
  define lbox win[find-widget: lbid]
  assert {not-null? lbox}
  lbox[clear-items:]
  define stmt cave-db[statement: #"
    SELECT
        game_id AS game_id
      , caves AS caves
      , title AS title
      , author AS author
    FROM games
    WHERE invalid = 0
    ORDER BY author, title
    "#]
  ;;
  define stmt-rem props-db[statement: #"
    SELECT text AS text
    FROM player_remarks_game
    WHERE game_id=:game_id
    LIMIT 1
  "#]
  ;;
  define prev-author #nil
  define last-author-idx 0
  define expanded #t
  ;;
  define saved-game-id player-prop-ref("selected-game" 0)
  ;printf "idx=%o\n" idx
  define sel-index -1
  define author
  define game-id
  define item
  define rem ""
  lbox[begin-bulk-operation:]
  while stmt[step:]
    {game-id := stmt["game_id"]}
    {author := stmt["author"]}
    if {not {prev-author eqv? author}}
      then
        {prev-author := author}
        if {positive? lbox[item-count:]}
          lbox[append-item: new-delimiter-item()]
        {last-author-idx := lbox[item-count:]}
        {item := new-author-item(game-id author last-author-idx)}
        lbox[append-item: item]
        {expanded := player-prop-ref-bool(string-append("expanded:" author) #t)}
        if {not expanded}
          {item[expanded: lbox] := #f}
    if {saved-game-id = game-id}
      {sel-index := lbox[item-count:]}
    {stmt-rem[":game_id"] := game-id}
    {rem := ""}
    while stmt-rem[step:]
      {rem := stmt-rem["text"]}
    lbox[append-item: new-game-item(game-id author stmt["title"] rem expanded)]
    lbox[item: last-author-idx][inc-games!:]
  if {sel-index >= 0}
    lbox[current-item-set!: sel-index]
  lbox[end-bulk-operation:]
  stmt[close:]
  stmt-rem[close:]
