#
# Copyright (c) 2012,2015 LAAS/CNRS
# All rights reserved.
#
# Permission to use, copy, modify, and distribute this software for any purpose
# with or without   fee is hereby granted, provided   that the above  copyright
# notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS  SOFTWARE INCLUDING ALL  IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR  BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR  ANY DAMAGES WHATSOEVER RESULTING  FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
# OTHER TORTIOUS ACTION,   ARISING OUT OF OR IN    CONNECTION WITH THE USE   OR
# PERFORMANCE OF THIS SOFTWARE.
#
#                                            Anthony Mallet on Sat Apr  7 2012
#

namespace eval session {
  variable id		0
  variable sessions	[dict create]


  # --- start --------------------------------------------------------------
  #
  proc start { channel params } {
    variable id
    variable sessions

    set s [get $params]
    if {$s == ""} {
      set s s[incr id]
      while {[dict exists $sessions $s]} {
        set s s[incr id]
      }
    }
    if {[dict exists $sessions $s]} {
      error "session $s exists"
    }

    infox "$channel: new session $s"
    dict set sessions $s [dict create events {}]
    return $s
  }
  namespace export start


  # --- get ----------------------------------------------------------------
  #
  proc get { params } {
    if {[catch {dict get $params s} s]} {
      return ""
    }
    return $s
  }


  # --- stop ---------------------------------------------------------------
  #
  proc stop { channel params } {
    variable sessions

    set s [get $params]
    if {$s == ""} { error "no session given" }
    if {![dict exists $sessions $s]} {
      error "session $s does not exist"
    }

    infox "$channel: closing session $s"
    event $s quit
    dict unset sessions $s
    return
  }
  namespace export stop


  # --- kill ---------------------------------------------------------------
  #
  proc kill {} {
    variable sessions

    dict for {s d} $sessions {
      infox "closing session $s"
      event $s quit
      dict unset sessions $s
    }
  }


  # --- clean --------------------------------------------------------------
  #
  proc clean { channel } {
    variable sessions

    dict for {s d} $sessions {
      if {![catch {dict get $d listen} l]} {
        if {$l == $channel} {
          infox "$channel: terminating $l listener"
          dict unset sessions $s listen
        }
      }
    }
  }


  # --- listen -------------------------------------------------------------
  #
  proc listen { channel params } {
    variable sessions

    set s [get $params]
    if {$s == ""} return
    if {![dict exists $sessions $s]} {
      error "session $s does not exist"
    }
    if {![catch {dict get $sessions $s listen} l]} {
      infox "$channel: terminating $l listener on session $s"
      server terminate $l
    }

    infox "$channel: listening session $s"
    dict set sessions $s listen $channel

    server stream $channel
    push $s
    return
  }
  namespace export listen


  # --- event --------------------------------------------------------------
  #
  proc event { s {event ""} {data ""} } {
    variable sessions

    if {$s == ""} { error "no session" }
    if {[catch {dict get $sessions $s} d]} {
      error "session $s does not exist"
    }

    infox "$s: event: $event; data: $data"
    dict lappend d events [list $event $data]
    dict set sessions $s $d

    push $s
    return
  }


  # --- broadcast ----------------------------------------------------------
  #
  proc broadcast { {event ""} {data ""} } {
    variable sessions

    dict for {s d} $sessions {
      event $s $event $data
    }
  }


  # --- push ---------------------------------------------------------------
  #
  proc push { s } {
    variable sessions

    if {[catch {dict get $sessions $s listen} l]} return

    set list [dict get $sessions $s events]
    while {[llength $list]} {
      set list [lassign $list e]
      lassign $e event data
      if {[catch {
        infox "$s: pushing to $l event $event" 1
        set content "event: $event\n"
        foreach i [split $data "\n"] {
          append content "data: $i\n"
        }
        puts $l $content
      } m]} {
        warnx "$l: $m"
        lappend list $e
        dict unset sessions $s listen
        server terminate $l
        break
      }
    }
    dict set sessions $s events $list
    catch { flush $l }
    return
  }


  # create 'session' command
  namespace ensemble create
}
