+# --------------------------------------------------------------------
+# A sequence class vaguely inspired by Clojure's sequences.
+#
+# Copyright (c) 2010 Olaf Delgado-Friedrichs (odf@github.com)
+# --------------------------------------------------------------------
+
+
 if typeof(require) != 'undefined'
   require.paths.unshift __dirname
   { recur, resolve } = require('trampoline')


  forced: -> Sequence.conj @first(), (=> @rest()?.forced()), 'forced'

  @memo: (name, f) -> @::[name] = -> x = f.apply(this); (@[name] = -> x)()

  @memo 'size', ->
    step = (s, n) -> if s then recur -> step s.rest(), n + 1 else n
    resolve step this, 0

  @memo 'last', ->
    step = (s) -> if s.rest() then recur -> step s.rest() else s.first()
    resolve step this

  @conj: (first, rest, mode) ->
    r = rest() if mode == 'forced'
    new Sequence {
      first: -> first
      rest:
        switch mode
          when 'stored' then -> val = rest(); (@rest = -> val)()
          when 'forced' then -> r
          else               -> rest()
    }

  @from: (start) -> Sequence.conj start, => @from start+1

  @range: (start, end) -> @from(start).take end - start + 1


# --------------------------------------------------------------------
# Exporting.

      @first = source.first
      @rest  = source.rest

  @conj: (first, rest, mode) ->
    r = rest() if mode == 'forced'
    new Sequence {
      first: -> first
      rest:
        switch mode
          when 'stored' then -> val = rest(); (@rest = -> val)()
          when 'forced' then -> r
          else               -> rest()
    }

  @from: (start) -> Sequence.conj start, => @from start+1

  @range: (start, end) -> @take__ @from(start), end - start + 1

  @::S = this

  @memo: (name, f) ->
    @[name]        = (seq) -> f.call(this, new Sequence(seq))
    @["#{name}__"] = (seq) -> f.call(this, seq)
    @::[name]      = -> x = f.call(@S, this); (@[name] = -> x)()

  @method: (name, f) ->
    @[name]        = (seq, args...) -> f.call(this, new Sequence(seq), args...)
    @["#{name}__"] = (seq, args...) -> f.call(this, seq, args...)
    @::[name]      = (args...) -> f.call(@S, this, args...)

  @operator: (name, f) ->
    @[name]        = (seq, other, args...) ->
      f(new Sequence(seq), new Sequence(other), args...)
    @["#{name}__"] = (seq, other, args...) ->
      f.call(this, seq, other, args...)
    @::[name]      = (other, args...) ->
      f.call(@S, this, new Sequence(other), args...)

  @memo 'size', (seq) ->
    step = (s, n) -> if s then recur -> step s.rest(), n + 1 else n
    resolve step seq, 0

  @memo 'last', (seq) ->
    step = (s) -> if s.rest() then recur -> step s.rest() else s.first()
    resolve step seq

  @method 'take', (seq, n) ->
    if seq and n > 0
      Sequence.conj seq.first(), => @take__ seq.rest(), n-1
    else
      null

  @method 'takeWhile', (seq, pred) ->
    if seq and pred(seq.first())
      Sequence.conj seq.first(), => @takeWhile__ seq.rest(), pred
    else
      null

  @method 'drop', (seq, n) ->
    step = (s, n) -> if s and n > 0 then recur -> step(s.rest(), n - 1) else s
    resolve step seq, n

  @method 'dropWhile', (seq, pred) ->
    step = (s) -> if s and pred s.first() then recur -> step s.rest() else s
    resolve step seq

  @method 'get', (seq, n) => @drop__(seq, n)?.first() if n >= 0

  select: (pred) ->
    if pred @first()
