#make sure you compile iwth -cw not -cwb
#this project used to have async helpers until i found @caolan's
#nimble project

failCount = 0
passCount = 0
count = 0
failedMessages = []

class AssertionError extends Error
  constructor: (options) ->
    @name = 'AssertionError'
    @message = options.message
    @actual = options.actual
    @expected = options.expected
    @operator = options.operator

  toString: () =>
    "test"
    [@name + ':', @message].join ' '

    # wait for
define "drews-mixins", ->
  _ = require "underscore";
  exports = {}
  exports.asyncEx = (len, cb) ->
    _.wait len, -> cb null, len 

  exports.asyncFail = (len, cb) ->
    _.wait len, -> cb len
    
  exports.doneMaker = () ->
    allDoneCallback = ->
    results = []
    allDone = (cb) ->
      allDoneCallback = cb
    id = _.uniqueId()
    length = 0
    doneLength = 0
    live = true
    done = () ->
      myLength = length
      length++ 
      return do (myLength) -> (err, result) ->
        if live is false then return
        doneLength++
        if err then allDoneCallback err, results
        results[myLength] = result
        if doneLength is length
          allDoneCallback null, results
          live = false
    [done, allDone]
          
  
  #think about old wrapper objects sometime

  # some backbone.js-like events, consider using node.js like ones 
  # compare backbones events with nodes events
  # https://github.com/joyent/node/blob/master/lib/events.js
  # https://github.com/documentcloud/backbone/blob/master/backbone.js
  # https://github.com/maccman/spine/blob/master/spine.js


  exports.on = (obj, ev, callback) ->
    calls = obj._callbacks || obj._callbacks = {}
    list = calls[ev] || (calls[ev] = [])
    list.push callback
    obj._events = obj._callbacks
    obj
  exports.removeListener = (obj, ev, callback) ->
    if (!ev)
      obj._callbacks = {}
      obj._events = obj._callbacks
    else if calls = obj._callbacks
      if !callback
        calls[ev] = []
      else
        list = calls[ev]
        if !list then return obj
        for item, i in list
          if callback == list[i]
            list.splice i, 1 #spine.js
            #list[i] = null #backbone.js
            # then backbone clearns the nulls later
            # node.js copies the array when triggering 
            # so the once isn't a problem
            break
    obj
  #TODO async events? wait 0, ->
  trigger = (obj, eventName, args...) ->
    both = 2
    id = _.uniqueId()
    if !(calls = obj._callbacks) then return obj
    while both--
      ev = if both then eventName else  "all"
      list = calls[ev]
      
      if list=calls[ev]
        # then next line coppies the array
        # so it doesn't get shrinked by a once
        # backbone.js has maybe a more efficient way
        # where unbind sets it to null, and here it slices them
        # if they are null
        list = list.slice() #stole this from node.js events
        for item, i in list
          callback = list[i]
          if not callback

          else
            args = if both then args else args.unshift(eventName)
            # maby have obj as the first param?
            callback.apply obj, args
  exports.trigger = trigger
  exports.emit = exports.trigger
  
  exports.addListener = exports.on
  exports.unbind = exports.removeListener
  exports.once = (obj, ev, callback) ->
    g = (args...) ->
      _.removeListener obj, ev, g
      callback.apply obj, args 
    _.addListener obj, ev, g
  
  exports.graceful = (errorFunc, callback) ->
    if _.isArray errorFunc
      extraArgs = _.s errorFunc, 1
      errorFunc = errorFunc[0]
    else
      extraArgs = []
    makeHandler = (func) ->
      (err, results...) ->
        if err
          #return errorFunc null, err, extraArgs...
          return errorFunc.apply null, null, null
        func results...
    if callback
      makeHandler callback
    else
      makeHandler
    

  #simple substring/slice functionality
  # for arrays and strings
  # very similar to php's subst and php's array_slice 
  exports.s = (val, start, end) ->
    need_to_join = false
    ret = []
    if _.isString val
      val = val.split ""
      need_to_join = true
    
    if start >= 0
    else
      start = val.length + start
    
    if _.isUndefined(end)
      ret = val.slice start
    else
      if end < 0
        end = val.length + end
      else
        end = end + start
      ret = val.slice start, end

    if need_to_join
      ret.join ""
    else
      ret

  exports.startsWith = (str, with_what) ->
    _.s(str, 0, with_what.length) == with_what
  
  exports.rnd = (low, high) -> Math.floor(Math.random() * (high-low+1)) + low

  exports.time = () ->
    (new Date()).getTime()

  exports.replaceBetween = (str, start, between, end) ->
    pos = str.indexOf start
    if pos is -1 then return str
    endpos = str.indexOf end, pos + start.length
    if endpos is -1 then return str
    return _.s(str, 0, pos + start.length) + between + _.s(str, endpos)
  exports.trimLeft = (obj) ->
    obj.toString().replace(/^\s+/, "")
  exports.trimRight = (obj) ->
    obj.toString().replace(/\s+$/, "")
  exports.isNumeric = (str) ->
    if _.isNumber(str)
      return true
    if _.s(str, 0, 1) == "-"
      return true
    if _.s(str, 0, 1).match(/\d/)
      return true
    else
      return false

  exports.capitalize = (str) ->
    str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();

  # this is just a wrapper for setTimeout.
  # it puts the miliseconds first to it's more
  # natural to write in coffeescript

  exports.wait = (miliseconds, func) ->
    setTimeout func, miliseconds
  times = (numb, func) ->
    for i in [1..numb]
      func i

  exports.interval = (miliseconds, func) ->
    setInterval func, miliseconds

  exports.compareArrays = (left, right) ->
    #not yet optimized
    inLeftNotRight = []
    inRightNotLeft = []
    inBoth = []
    for item in left
      if item in right
        inBoth.push item
      else
        inLeftNotRight.push item

    for item in right
      if item not in left
        inRightNotLeft.push item 

    return [inLeftNotRight, inRightNotLeft, inBoth]

  exports.pacManMapMaker = (left, right, top, bottom) ->
    1
    # todo make a little map maker 

  exports.populateArray = (obj, key, value) ->
    if not _.isArray obj[key]
      obj[key] = []
    obj[key].push value

  setLocation = (stuff, cb) ->
  log = (args...) -> console.log args... 
  exports.log = log

  hosty = null 
      
    
  postMessageHelper = (yourWin, origin, methods={}) ->
    self = {}
    host = {}
    # methods that can be called on me
    self.addMethods = (fns) ->
      _.extend methods, fns 
    self.addMethods
      bind: (event, callback) ->
      #?????
    events = {}
    callbacks = {} 
    #origin = yourWin.location.origin # cant do
    #origin = _origin # or something

    #???
    #???    
    self.trigger = ->
    self.write = -> #sream data
    # then need a self.ondata self.onend callbacks
    # trigger self, "data"
    # trigger self, "end"
    #  or something else
    self.trigger = (event, params...) ->

    # self.bind not done yet    
    self.bind = (event, callback) ->
      id = _.uuid() 
      subscribe = 
        channel: event
        id: id
      subscribeString = JSON.stringify subscribe
      events[event] ||= []
      events[event].push callback
      yourWin.postMessage subscribeString, origin
      
    self.call = (method, params..., callback) ->    
      id = _.uuid()
      request =
        method: method
        params: params
        id: id
      requestString = JSON.stringify request
      callbacks[id] = callback
      yourWin.postMessage requestString, origin  
    $(window).bind "message", (e) ->
      e = e.originalEvent #only needed for jQuery
      if e.origin != origin and origin != "*" 
        return
      message = JSON.parse e.data
      if "result" of message
        # i called another window & they're responding
        {id, error, result} = message
        callbacks[id]? error, result
      else if "method" of message
        # another window is calling me
        {method, params, id} = message
        # by defualt makeing it async
        methods[method]? params..., (err=null, result=null) ->
          response =
            error: err
            result: result
            id: id
          responseString = JSON.stringify response
          #e.source.postMessage
          yourWin.postMessage responseString, origin
    self
      
    # win is an iframe.contentWindow or other window
      
  exports.postMessageHelper = postMessageHelper
  exports.uuid = () ->
    #http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
    `'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {var r = Math.random()*16|0,v=c=='x'?r:r&0x3|0x8;return v.toString(16);});` 
    
  #maybe to one for add to array 
  addToObject = (obj, key, value) ->
    obj[key] = value
  addToObjectMaker = (obj) ->
    (key, value) ->
      addToObject obj, key, value
  exports.addToObjectMaker = addToObjectMaker

  jsonHttpMaker = (method) ->
    http = (args..., callback) ->
      [url, args, contentType] = args
      #TODO: why does the {} work?
      data = JSON.stringify args || {}
      $.ajax 
        url: "#{url}"
        type: method || "POST"
        contentType: 'application/json' || contentType
        data: data
        dataType: 'json'
        processData: false
        success: (data) -> callback null, data
        error: (data) -> 
          callback JSON.parse data.responseText
  exports.jsonPost = jsonHttpMaker "POST"
  exports.jsonGet = jsonHttpMaker "GET"
  exports.jsonHttpMaker = jsonHttpMaker
  # get = ajaxMaker "get"
  # asyncTests = (batches, tests) ->
  #   before = addToObjectMaker()
  #   test = addToObjectMaker()
  #   prepareTests = () ->
  #     _.series batches
  ###    
  do ->
    giveBackTheCard = takeACard()
    giveBackTheCard()
  ###

  exports.getAssertCount = -> count
  exports.getFailCount = -> failCount
  exports.getPassCount =  -> passCount
  exports.setAssertCount = (newCount) -> count = newCount
  exports.setPassCount = (newCount) -> passCount = newCount
  exports.setFailCount = (newCount) -> failCount = newCount
  exports.getFailedMessages = () -> failedMessages

    
  exports.assertFail = (actual, expected, message, operator, stackStartFunction) ->
    failCount++
    count++
    failedMessages.push message
    e = 
      message: message
      actual: actual
      expected: expected
      operator: operator
      stackStartFunction: stackStartFunction
    console.log e
    #throw new AssertionError e
  exports.assertPass = (actual, expected, message, operator, stackStartFunction) ->
    passCount++
    count++
  exports.assertOk = (value, message) ->
    if !!!value
      _.assertFail value, true, message, '==', exports.assertOk
    else
      _.assertPass value, true, message, "==", _.assertOk 
  exports.assertEqual = (actual, expected, message) ->
    if `actual != expected`
      _.assertFail actual, expected, message, '==', exports.assertEqual
    else
      _.assertPass actual, expected, message, "==", exports.assertEqual
  exports.assertNotEqual = (actual, expected, message) ->
    if `actual == expected`
      _.assertFail actual, expected, message, '!=', exports.assertNotEqual
    else
      _.assertPass actual, expected, message, '!=', exports.assertNotEqual


  _.mixin exports
  return exports
#root._ if root._ is defined in parent script 
