//
// Copyright (c) 2008, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   12 Sep 08  Brian Frank  Creation
//

using gfx
using fwt

**
** History maintains the most recent navigation history
** of the entire application.
**
@Serializable { collection = true }
class History
{

  **
  ** Convenience for loading from "session/history"
  **
  static History load()
  {
    return Flux.loadOptions(Flux.pod, "session/history", History#)
  }

  **
  ** Convenience for save to "session/history".
  ** Return this.
  **
  This save()
  {
    Flux.saveOptions(Flux.pod, "session/history", this)
    return this
  }

  **
  ** Log navigation to the specified resource
  ** into the history.  Return this.
  **
  This push(Resource r)
  {
    // don't log flux: uris
    if (r.uri.scheme == "flux") return this

    // map resource to item
    item := HistoryItem
    {
      uri = r.uri
      iconUri = r.icon.uri
      time = DateTime.now
    }

    // push as most recent (remove old record for uri if found)
    dup := items.findIndex |HistoryItem i->Bool| { return i.uri == r.uri }
    if (dup != null) list.removeAt(dup)
    while (items.size >= max) list.removeAt(-1)
    list.insert(0, item)

    return this
  }

  **
  ** Get a readonly copy of all the items in the history.
  ** The first item is the most recent navigation and the last
  ** item is the oldest navigation.
  **
  HistoryItem[] items()
  {
    return list.ro
  }

  **
  ** Add a new history item to the end of the history.
  ** This method is typically only used for serialization.
  ** See `push` to log navigation of a Uri.  Return this.
  **
  This add(HistoryItem item)
  {
    if ((Obj?)item.uri == null || (Obj?)item.time == null)
      throw ArgErr("Invalid item: " + item)
    list.add(item)
    return this
  }

  **
  ** Iterate the history items from most recent to oldest.
  **
  Void each(|HistoryItem item| f)
  {
    list.each(f)
  }

  @Transient private Int max := 40
  @Transient private HistoryItem[] list := HistoryItem[,]
}

**************************************************************************
** HistoryItem
**************************************************************************

**
** HistoryItem stores information about navigation to a specific uri.
**
@Serializable
const class HistoryItem
{
  **
  ** Default constructor with it-block
  **
  new make(|This|? f := null)
  {
    if (f != null) f(this)
  }

  **
  ** Uri of resource.
  **
  const Uri uri := ``

  **
  ** Last time of access.
  **
  const DateTime time := DateTime.now

  **
  ** Uri for icon of resource or null.
  **
  const Uri? iconUri

  **
  ** Return uri string.
  **
  override Str toStr() { return uri.toStr }
}

**************************************************************************
** HistoryPicker
**************************************************************************

class HistoryPicker : EdgePane
{
  new make(HistoryItem[] items, Bool fullPath, |HistoryItem, Event| onAction)
  {
    model := HistoryPickerModel(items, fullPath)
    center = Table
    {
      it.headerVisible = false
      it.model = model
      it.onAction.add |Event e|
      {
        onAction(model.items[e.index], e)
      }
      it.onKeyDown.add |Event e|
      {
        code := e.keyChar
        if (code >= 97 && code <= 122) code -= 32
        code -= 65
        if (code >= 0 && code < 26 && code < model.numRows)
          onAction(model.items[code], e)
      }
    }
  }
}

internal class HistoryPickerModel : TableModel
{
  new make(HistoryItem[] items, Bool fullPath)
  {
    this.items = items
    this.fullPath = fullPath
    icons = items.map |HistoryItem item->Image|
    {
      Image(item.iconUri, false) ?: def
    }
  }

  override Int numCols() { return 2 }
  override Int numRows() { return items.size }
  override Int? prefWidth(Int col)
  {
    switch (col)
    {
      case 0: return fullPath ? 450 : 250
      case 1: return 24
      default: return null
    }
  }
  override Image? image(Int col, Int row) { return col==0 ? icons[row] : null }
  override Font? font(Int col, Int row) { return col==1 ? accFont : null }
  override Color? fg(Int col, Int row)  { return col==1 ? accColor : null }
  override Str text(Int col, Int row)
  {
    switch (col)
    {
      case 0:  uri := items[row].uri; return fullPath ? uri.toStr : uri.name
      case 1:  return (row < 26) ? (row+65).toChar : ""
      default: return ""
    }
  }
  HistoryItem[] items
  Image[] icons
  Image def := Flux.icon(`/x16/file.png`)
  Font accFont := Desktop.sysFont.toSize(Desktop.sysFont.size-1)
  Color accColor := Color("#666")
  Bool fullPath
}