React = require 'react'
ReactDOM = require 'react-dom'
TestUtils = require 'react-addons-test-utils'
Utils = require('../src/utils')
moment = require 'moment'
TextInput2 = React.createFactory require('../src/components/text_input_2')
ValidationContext = require '../src/context_wrapper'

describe 'Age Format', ->
  it 'should return age from datestring when months are not included', ->

    # need these variables since formatAge uses today's year to format age
    displayDate = '1910-12-31'
    displayYear = 2016
    displayAgeForYear2016 = 105
    now = new Date().getFullYear()
    displayAgeForYear2016 += now - displayYear

    Utils.formatAge(displayDate).should.equal("#{displayAgeForYear2016} YO")

describe 'NHS ID number', ->

  it 'should be formatted with 3 3 4 w/ spaces', ->
    Utils.formatNHS('1515151522').should.equal('151 515 1522')

  it 'should handle various string lengths', ->
    Utils.formatNHS('1').should.equal('1')
    Utils.formatNHS('12').should.equal('12')
    Utils.formatNHS('123').should.equal('123')
    Utils.formatNHS('1234').should.equal('123 4')
    Utils.formatNHS('12345').should.equal('123 45')
    Utils.formatNHS('123456').should.equal('123 456')
    Utils.formatNHS('1234567').should.equal('123 456 7')
    Utils.formatNHS('12345678').should.equal('123 456 78')
    Utils.formatNHS('123456789').should.equal('123 456 789')
    Utils.formatNHS('1234567890').should.equal('123 456 7890')

  it 'should handle whitespace and strip non digits', ->
    Utils.formatNHS('1 234 567 8 9 0').should.equal('123 456 7890')
    Utils.formatNHS('123-456-7890').should.equal('123 456 7890')
    Utils.formatNHS('A123-456:7890A').should.equal('123 456 7890')


describe 'Phone Number Format', ->
  
  it 'should create a phone number with (xxx) xxx-xxxx for 10 digit numbers', ->
    Utils.formatPhoneNumber('(555) 333-4444').should.equal('(555) 333-4444')
    Utils.formatPhoneNumber('555-333-4444').should.equal('(555) 333-4444')
    Utils.formatPhoneNumber('555.333.4444').should.equal('(555) 333-4444')
    Utils.formatPhoneNumber('5553334444').should.equal('(555) 333-4444')

  it 'should create a phone number with xxx-xxxx for 7 digit numbers', ->
    Utils.formatPhoneNumber('333-4444').should.equal('333-4444')
    Utils.formatPhoneNumber('333.4444').should.equal('333-4444')
    Utils.formatPhoneNumber('3334444').should.equal('333-4444')

  it 'should NOT format number that are not 7 or 10 digits', ->
    Utils.formatPhoneNumber('13334444').should.equal('13334444')
    Utils.formatPhoneNumber('113334444').should.equal('113334444')
    Utils.formatPhoneNumber('334444').should.equal('334444')
    Utils.formatPhoneNumber('33-4444').should.equal('33-4444')
    Utils.formatPhoneNumber('33-4444-55555').should.equal('33-4444-55555')

describe 'Source Format', ->

  it 'should create a string: Org - Facility - System', ->
    input = 
      orgName: 'Org' 
      facilityName: 'Facility'
      systemName: 'System'

    Utils.formatSource(input).should.equal('Org - Facility - System')


  it 'should omit hyphens and spaces for a facility that is an empty string', ->
    input = 
      orgName: 'Org' 
      facilityName: ''
      systemName: 'System'

    Utils.formatSource(input).should.equal('Org - System')

  it 'should omit hyphens and spaces for a system that is an empty string', ->
    input = 
      orgName: 'Org' 
      facilityName: 'Facility'
      systemName: ''

    Utils.formatSource(input).should.equal('Org - Facility')

  it 'should omit hyphens and spaces for a facility that is null/undefined', ->
    input = 
      orgName: 'Org' 
      systemName: 'System'

    Utils.formatSource(input).should.equal('Org - System')

  it 'should omit hyphens and spaces for a system that is null/undefined', ->
    input = 
      orgName: 'Org' 
      facilityName: 'Facility'


    Utils.formatSource(input).should.equal('Org - Facility')

  it 'should omit leading hyphen and spaces for an org that is null/undefined', ->
    input = 
      facilityName: 'Facility'
      systemName: 'System'

    Utils.formatSource(input).should.equal('Facility - System')

describe 'Reduced File Name Format', ->

  it 'should return the whole filename if filename length is less than target length', ->

    targetLength = 3

    Utils.formatFileName('to', targetLength).should.equal('to')

  it 'should return reduced filename with if filename length is equal to target length', ->

    targetLength = 10

    Utils.formatFileName('1234567890', targetLength).should.equal('1234567890')

  it 'should return reduced filename with 4 characters from the end of the filename', ->
    # target length is 10 and file name length is 14
    targetLength = 10

    Utils.formatFileName('12345678901234', targetLength).should.equal('123…1234')

  it 'should return reduced filename with 6 characters from the end of the filename', ->
    # target length is 19 and file name length is 20
    targetLength = 19

    Utils.formatFileName('12345678901234567890', targetLength).should.equal('1234567890…567890')




describe 'File Ext Parse', ->

  it 'should return the extension from a file name when extension exists', ->

    Utils.parseFileExtension('filename.ext').should.equal('ext')

  it 'should return the empty string from a file name when no extension exists', ->

    Utils.parseFileExtension('filename').should.equal('')

describe 'Match object Ids', ->

  obj1 = {id:1}
  obj2 = {id:2}
  obj3 = {guid: 3}
  obj4 = {guid: 4}
  obj5 = {id:1, guid:3}
  obj6 = {id:2, guid:3}

  it 'should return the false if objects have different ids', ->

    matchTest = Utils.idsMatch(obj1, obj2)

    expect(matchTest).to.equal(false)

  it 'should return the true if objects have same ids', ->

    matchTest = Utils.idsMatch(obj2, obj2)

    expect(matchTest).to.equal(true)

  it 'should return the false if objects have different guids', ->

    matchTest = Utils.idsMatch(obj3, obj4)

    expect(matchTest).to.equal(false)

  it 'should return the true if objects have same guids', ->

    matchTest = Utils.idsMatch(obj4, obj4)

    expect(matchTest).to.equal(true)

  it 'should return the false if objects have different ids and same guids', ->

    matchTest = Utils.idsMatch(obj5, obj6)

    expect(matchTest).to.equal(false)

describe 'Is Number', ->

  it 'should return the false if not number', ->

    numberTest = Utils.isNumber('string')

    expect(numberTest).to.equal(false)

  it 'should return the true if number', ->

    numberTest = Utils.isNumber(44)

    expect(numberTest).to.equal(true)

  it 'should return the true if number is wrapped in quotes', ->

    numberTest = Utils.isNumber('44.22222222')

    expect(numberTest).to.equal(true)

describe 'Measure DOM Property', ->
  
  it 'should return the request DOM property of the passed element', ->
    measurer = document.createElement 'div'
    measurer.id = 'measurer'
    document.body.appendChild measurer

    el = document.createElement 'span'
    el.innerText = 'some text to make this el have width'

    width = Utils.measureDOMProp el, 'offsetWidth'

    document.body.removeChild measurer

    expect(width).to.not.equal(0)

describe 'Form Data Builder', ->
  component = null
  wrapper = null
  handleChange = sinon.spy()
  changeArgs = null
  output = 
    firstName: 'Mike'
    lastName: 'Shmo'
    addresses: [
      {
        type: 'Work'
        zip: '12345'
      }
      {
        type: 'Home'
        zip: '01234'
      }
    ]
    emails: [
      'right@email.com'
      'my2@email2.com'
    ]
    currentStateId: 2
    customStateArray: [ 
      {
        id: 1
        value: 'starting state value'
      }
      {
        id: 2
        value: 'this value was just updated'
      }
      {
        id: 3
        value: 'yet another value'
      }
    ]

  {div} = React.DOM

  before ->
    component = React.createClass
      getInitialState: ->
        addresses: [
          {
            type: 'Work'
            zip: null
          }
        ]
        emails: [
          'wrong@email.com'
          'my2@email2.com'
        ]
        currentStateId: 2
        customStateArray: [ 
          {
            id: 1
            value: 'starting state value'
          }
          {
            id: 2
            value: 'another value'
          }
          {
            id: 3
            value: 'yet another value'
          }
        ]

      handleChange: ->
        data = Utils.buildFormData(@, @state)
        @setState data, =>
          @props.onChange(data)
        
      render: ->
        div {
          key: 'container'
        }, [
          ValidationContext {
            key: 'context'
          }, [
            TextInput2 {
              key: 1
              ref: 'firstName'
              value: 'Joe'
              onChange: @handleChange
            }
            TextInput2 {
              key: 2
              ref: 'lastName'
              value: 'Shmo'
              onChange: @handleChange
            }
            TextInput2 {
              key: 3
              ref: 'addresses.[{type:Home}].zip'
              value: '01234'
              onChange: @handleChange
            }
            TextInput2 {
              key: 4
              ref: 'addresses.[{type:Work}].zip'
              value: '12345'
              onChange: @handleChange
            }
            TextInput2 {
              key: 5
              ref: 'emails.[0]'
              value: 'right@email.com'
              onChange: @handleChange
            }
            # We don't want these refs to be tracked in our object
            TextInput2 {
              key: 6
              ref: '!dontChangeMe'
              value: 'unrelated ref'
              onChange: @handleChange
            }
            div {
              key: 7
              ref: 'noGetValueMethod'
            }
            # Uses state variables to figure out which collection item to update
            TextInput2 {
              key: 8
              ref: 'customStateArray.[{id:$currentStateId}].value'
              value: 'this value was just updated'
              onChange: @handleChange
            }
          ]
        ]

    wrapper = TestUtils.renderIntoDocument React.createElement(component, { onChange: handleChange })

    # Fire a change on the first text input (first name)
    inputEl = TestUtils.scryRenderedDOMComponentsWithClass wrapper, 'text-input'
    # Set the new value on the input
    ReactDOM.findDOMNode(inputEl[0]).value = 'Mike'
    # Fire the change event
    TestUtils.Simulate.change(inputEl[0])
    # The spy change arguments
    changeArgs = handleChange.args[0][0]

  it 'should have matching string values for non-nested refs', ->
    expect(changeArgs.firstName).to.equal(output.firstName)
    expect(changeArgs.lastName).to.equal(output.lastName)

  it 'should update a flat array at the proper index', ->
    expect(changeArgs.emails[0]).to.equal(output.emails[0])
    expect(changeArgs.emails[1]).to.equal(output.emails[1])

  it 'should update a collection item that matches the key:value pair', ->
    expect(changeArgs.addresses[0].zip).to.equal(output.addresses[0].zip)
    expect(changeArgs.addresses[1].zip).to.equal(output.addresses[1].zip)

  it 'should ignore refs prefixed with (!) and refs without getValue defined', ->
    expect(output.dontChangeMe).to.equal(undefined)
    expect(output.noGetValueMethod).to.equal(undefined)

  it 'should pull variables from component state when prefixed with ($) inside collection searches', ->
    expect(changeArgs.customStateArray[1].value).to.equal(output.customStateArray[1].value)
