;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
define new-cave-item(game-id cave-id author title story remark)
  define comment ""
  define replay-id 0
  define outcome "dunno"
  ;;
  method-lambda game-item
    selectable?: (self)
      #t
    ;;
    height: (self)
      lgfx:text-height()
    ;;
    paint: (self widget x y wdt index selected)
      define hgt self[height:]
      if selected
        lgfx:fill-rect x y wdt hgt widget[style-color-prop: "back-selection"]
      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
          if even?(index)
            {stl := widget[style-color-prop: "text-even"]}
            {stl := widget[style-color-prop: "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
    ;;
    cave-id: (self)
      cave-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"
      if {not empty-string?(comment)}
        {comment := string-append("\x11:\x02" value)}
        {comment := ""}
    ;;
    replay-id: (self)
      replay-id
    ;;
    replay-id-set!: (self value)
      {replay-id := value}
    ;;
    outcome: (self)
      outcome
    ;;
    outcome-set!: (self value)
      {outcome := value}
    ;;
    story: (self)
      story
    ;;
    remark: (self)
      remark


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
define new-cave-rename-window(listbox item-index cave-id)
  define win
    flexgui:parse-layout
      window-frame:
        id: "cave-rename"
        style: 'normal-window
        title-bar: text: string-append("Rename Cave")
        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: listbox[item: item-index][title:]
          ;
          spring: default:
          padding: default:
          h-container:
            spring: default:
            button: text: "O~k" pref-width: 42
              hotkey: "M-k"
              default: #t
              action:
                lambda (self)
                  define text self[find-widget: "name"][text:]
                  if {not {empty-string? text}}
                    then
                      define stmt cave-db[statement:
                        "UPDATE caves SET title=:value WHERE cave_id=:cave_id"]
                      {stmt[":cave_id"] := cave-id}
                      {stmt[":value"] := text}
                      stmt[execute:]
                      stmt[close:]
                      ;; update listbox
                      define item listbox[item: item-index]
                      {item[title:] := text}
                      self[close-window:]
            spring: default:
  ;;
  flexgui:append-window win
  flexgui:center-window win


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define selected-cave-index #f)
(define action-replay-data #nil)
(define action-replay-otypes #nil)

define new-cave-select-window(main-listbox)
  define skip-blur-close #f
  flexgui:parse-layout
    window-frame:
      style: 'normal-window
      title-bar: text: "Game List"
      activate-widget: "lbox-cave" ;; activate this widget when the window is created
      ;pref-width: 400
      ;pref-width: flexgui:lgfx:window-width
      ;pref-height: flexgui:lgfx:window-height
      on-window-blur:
        lambda (self evt)
          if {{not skip-blur-close} and {null? flexgui:find-window-by-id("cave-rename")}}
            self[close-window:]
      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? main-listbox}
            then
              evt[eat!:]
              self[close-window:]
      ;
      v-container: inset: 2
        button:
          text: "renam~e cave"
          hotkey: "M-e"
          action:
            lambda (self)
              define listbox self[find-widget: "lbox-cave"]
              define cave-index listbox[current-item:]
              define item listbox[item: cave-index]
              define cave-id item[cave-id:]
              listbox[activate-self!:]
              {skip-blur-close := #t}
              new-cave-rename-window listbox listbox[current-item:] cave-id
              {skip-blur-close := #f}
        ;
        padding: default:
        h-container: flex: 1
          h-container:
            flex: 1
            v-scrollbar:
              host-id: "lbox-cave"
            listbox: flex: 1
              id: "lbox-cave"
              hotkey: "M-b"
              action:
                lambda (self)
                  gset! selected-cave-index self[current-item:]
                  gset! action-replay-data #nil
                  gset! action-replay-otypes #nil
                  self[desktop:][close-all-windows:]
        ;
        padding: default:
        v-container:
          ;label:
          ;  id: "cave-title"
          ;  text: ""
          ;  raw-text: #t
          ;  no-expand: y:
          ;padding: default:
          label:
            id: "cave-outcome"
            text: "not played"
            raw-text: #t
            ;no-expand: y:
          padding: default:
          button:
            id: "replay-button"
            text: "no replay"
            action:
              lambda (self)
                define listbox self[find-widget: "lbox-cave"]
                define cave-index listbox[current-item:]
                define item listbox[item: cave-index]
                define replay-id item[replay-id:]
                ;printf "rid: %o\n" replay-id
                if {positive? replay-id}
                  then
                    gset! selected-cave-index cave-index
                    gset! action-replay-data get-cave-replay-data(replay-id)
                    gset! action-replay-otypes get-cave-replay-otypes(replay-id)
                    self[desktop:][close-all-windows:]
        ;
        padding: default:
        h-container:
          h-container:
            v-scrollbar:
              host-id: "tbox-info"
            textbox: flex: 1
              id: "tbox-info"
              hotkey: "M-i"
              on-broadcast:
                lambda (self evt btype)
                  define listbox self[find-widget: "lbox-cave"]
                  if {evt[source:] eq? listbox}
                    case btype
                      (current-item-set!:)
                        define cave-index listbox[current-item:]
                        define item listbox[item: cave-index]
                        define story item[story:]
                        define remark item[remark:]
                        define outcome item[outcome:]
                        define oc-label self[find-widget: "cave-outcome"]
                        {oc-label[text:] := outcome}
                        cond
                          {outcome = "not played"} {oc-label[style-prop: text-color:] := #@color:#fff}
                          {outcome = "died"}       {oc-label[style-prop: text-color:] := #@color:#f00}
                          {outcome = "aborted"}    {oc-label[style-prop: text-color:] := #@color:#f0f}
                          {outcome = "skipped"}    {oc-label[style-prop: text-color:] := #@color:#0aa}
                          {outcome = "finished"}   {oc-label[style-prop: text-color:] := #@color:#0f0}
                          else {oc-label[style-prop: text-color:] := #@color:#fff}
                        cond
                          {{not empty-string?(story)} and {not empty-string?(remark)}}
                            {self[text:] := string-append(story "\n\n==================================\n\n" remark)}
                          {not empty-string?(story)}
                            {self[text:] := story}
                          {not empty-string?(remark)}
                            {self[text:] := string-append("==================================\n\n" remark)}
                          else
                            {self[text:] := ""}
                        ;; replay button
                        define rbb self[find-widget: "replay-button"]
                        define replay-id item[replay-id:]
                        if {positive? replay-id}
                          then
                            {rbb[text:] := "~replay"}
                            {rbb[hotkey:] := "M-r"}
                          else
                            {rbb[text:] := "no replay"}
                            {rbb[hotkey:] := ""}
                      else #void


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
define open-cave-selector(game-id main-listbox)
  define set-cave-outcome-and-replay(game-id cave-index level-index item)
    define replay-id 0
    define outcome-str "not played"
    define stmt-outcome props-db[statement: #"
      SELECT
          stat_id AS stat_id
        , outcome AS outcome
      FROM cave_stats
      WHERE game_id=:game_id
        AND cave_index=:cave_index
        AND level_index=:level_index
        AND outcome <> -5 /* ignore quickloads */
      ORDER BY stat_id
      "#]
    define rid
    define outcome
    {stmt-outcome[":game_id"] := game-id}
    {stmt-outcome[":cave_index"] := cave-index}
    {stmt-outcome[":level_index"] := level-index}
    while stmt-outcome[step:]
      ;printf "cave #%o: outcome=%o\n" cave-index stmt-outcome["outcome"]
      {outcome := stmt-outcome["outcome"]}
      cond
        {outcome = -2}
          if (or {outcome-str = "not played"}
                 {outcome-str = "died"}
                 {outcome-str = "aborted"})
            {outcome-str := "skipped"}
        {outcome = -1}
          if (or {outcome-str = "not played"}
                 {outcome-str = "aborted"})
            {outcome-str := "skipped"}
        {outcome = 0}
          if {outcome-str = "not played"}
            {outcome-str := "aborted"}
        {outcome = 1}
          {outcome-str := "finished"}
          ;printf "stat-id=%o; rid=%o\n" stmt-outcome["stat_id"] get-cave-replay-id(stmt-outcome["stat_id"])
          {rid := get-cave-replay-id(stmt-outcome["stat_id"])}
          if {positive? rid}
            {replay-id := rid}
    stmt-outcome[close:]
    {item[outcome:] := outcome-str}
    if {positive? replay-id}
      {item[replay-id:] := replay-id}
    outcome-str
  ;;
  gset! selected-cave-index #f
  gset! action-replay-data #nil
  gset! action-replay-otypes #nil
  ;;
  define stmt-caves cave-db[statement: #"
    SELECT
        caves.cave_id AS cave_id
      , caves.cave_index AS cave_index
      , caves.title AS title
      , caves.author AS author
      , caves.story AS story
      , caves.remark AS remark
    FROM caves
    WHERE game_id=:game_id
      AND level_index=0
    ORDER BY cave_index
    "#]
  define stmt-rem props-db[statement: #"
    SELECT text AS text
    FROM player_remarks_cave
    WHERE cave_id=:cave_id
    LIMIT 1
  "#]
  ;
  define win new-cave-select-window(main-listbox)
  define listbox win[find-widget: "lbox-cave"]
  assert {not-null? listbox}
  listbox[clear-items:]
  listbox[begin-bulk-operation:]
  ;;
  ;; build cave list
  define sel-index -1
  define sel-skipped-index -1
  define sel-aborted-index -1
  define sel-died-index -1
  ;;
  define item
  define title
  define oc-str
  define index -1
  ;printf "game-id=%o\n" game-id
  {stmt-caves[":game_id"] := game-id}
  while stmt-caves[step:]
    inc! index
    assert {index = stmt-caves["cave_index"]} string-append("missing cave #" number->string(index))
    {title := stmt-caves["title"]}
    if {empty-string? title}
      {title := string-append("Cave #" number->string({index + 1}))}
    {item := new-cave-item(game-id stmt-caves["cave_id"] stmt-caves["author"] title
                           stmt-caves["story"] stmt-caves["remark"])}
    ;; get remark
    {stmt-rem[":cave_id"] := stmt-caves["cave_id"]}
    while stmt-rem[step:]
      {item[comment:] := stmt-rem["text"]}
    ;; fill other fields
    {oc-str := set-cave-outcome-and-replay(game-id index 0 item)}
    ;; done with the item
    listbox[append-item: item]
    ;; select default cave
    if {{negative? sel-index} and {oc-str = "not played"}}
      {sel-index := index}
    if {oc-str = "aborted"}
      {sel-aborted-index := index}
    if {oc-str = "skipped"}
      {sel-skipped-index := index}
    if {oc-str = "died"}
      {sel-died-index := index}
  ;;
  stmt-caves[close:]
  stmt-rem[close:]
  ;;
  cond
    {sel-index >= 0}
      {index := sel-index}
      while {{positive? index} and
             {listbox[item: {index - 1}][outcome:] = "aborted"}}
        dec! index
      if {index >= 0}
        {sel-index := index}
    {sel-aborted-index >= 0}
      {sel-index := sel-aborted-index}
    {sel-skipped-index >= 0}
      {sel-index := sel-skipped-index}
    {sel-died-index >= 0}
      {sel-index := sel-died-index}
    else
      {sel-index := 0}
  ;;
  listbox[end-bulk-operation:]
  listbox[current-item-set!: max(0 sel-index)]
  ;;
  flexgui:append-window win
  flexgui:center-window win
