require 'test_helper'

class ActsAsNestedIntervalTest < ActiveSupport::TestCase
  def test_modular_inverse
    assert_equal [nil, 1, 5, nil, 7, 2, nil, 4, 8], (0...9).map { |k| k.inverse(9) }
  end

  def test_create_root
    earth = Region.create name: "Earth"
    assert_equal [0, 1], [earth.lftp, earth.lftq]
    assert_equal [1, 1], [earth.rgtp, earth.rgtq]
    assert_equal 1.0 * 0 / 1, earth.lft
    assert_equal 1.0 * 1 / 1, earth.rgt
    assert_equal [earth], Region.roots
  end

  def test_create_first_child
    earth = Region.new name: "Earth"
    oceania = Region.new name: "Oceania", parent: earth
    oceania.save!
    assert_equal [1, 2], [oceania.lftp, oceania.lftq]
    assert_equal [1, 1], [oceania.rgtp, oceania.rgtq]
    assert_equal 1.0 * 1 / 2, oceania.lft
    assert_equal 1.0 * 1 / 1, oceania.rgt
  end

  def test_create_second_child
    earth = Region.create name: "Earth"
    oceania = Region.create name: "Oceania", parent: earth
    australia = Region.create name: "Australia", parent: oceania
    new_zealand = Region.create name: "New Zealand", parent: oceania
    assert_equal [2, 3], [australia.lftp, australia.lftq]
    assert_equal [1, 1], [australia.rgtp, australia.rgtq]
    assert_equal 1.0 * 2 / 3, australia.lft
    assert_equal 1.0 * 1 / 1, australia.rgt
    assert_equal [3, 5], [new_zealand.lftp, new_zealand.lftq]
    assert_equal [2, 3], [new_zealand.rgtp, new_zealand.rgtq]
    assert_equal 1.0 * 3 / 5, new_zealand.lft
    assert_equal 1.0 * 2 / 3, new_zealand.rgt
  end

  def test_append_child
    earth = Region.create name: "Earth"
    oceania = Region.new name: "Oceania"
    earth.children << oceania
    assert_equal [1, 2], [oceania.lftp, oceania.lftq]
    assert_equal [1, 1], [oceania.rgtp, oceania.rgtq]
    assert_equal 1.0 * 1 / 2, oceania.lft
    assert_equal 1.0 * 1 / 1, oceania.rgt
  end

  def test_ancestors
    earth = Region.create name: "Earth"
    oceania = Region.create name: "Oceania", parent: earth
    australia = Region.create name: "Australia", parent: oceania
    new_zealand = Region.create name: "New Zealand", parent: oceania
    assert_equal [], earth.ancestors
    assert_equal [earth], oceania.ancestors
    assert_equal [earth, oceania], australia.ancestors
    assert_equal [earth, oceania], new_zealand.ancestors
  end

  def test_descendants
    earth = Region.create name: "Earth"
    oceania = Region.create name: "Oceania", parent: earth
    australia = Region.create name: "Australia", parent: oceania
    new_zealand = Region.create name: "New Zealand", parent: oceania
    assert_equal [oceania, australia, new_zealand], earth.descendants.sort_by(&:id)
    assert_equal [australia, new_zealand], oceania.descendants.sort_by(&:id)
    assert_equal [], australia.descendants.sort_by(&:id)
    assert_equal [], new_zealand.descendants.sort_by(&:id)
  end

  def test_preorder
    earth = Region.create name: "Earth"
    oceania = Region.create name: "Oceania", parent: earth
    antarctica = Region.create name: "Antarctica", parent: earth
    australia = Region.create name: "Australia", parent: oceania
    new_zealand = Region.create name: "New Zealand", parent: oceania
    assert_equal [earth, oceania, australia, new_zealand, antarctica], Region.preorder
  end

  def test_depth
    earth = Region.create name: "Earth"
    oceania = Region.create name: "Oceania", parent: earth
    australia = Region.create name: "Australia", parent: oceania
    new_zealand = Region.create name: "New Zealand", parent: oceania
    assert_equal 0, earth.depth
    assert_equal 1, oceania.depth
    assert_equal 2, australia.depth
    assert_equal 2, new_zealand.depth
  end

  def test_move
    connection = Region.connection
    earth = Region.create name: "Earth"
    oceania = Region.create name: "Oceania", parent: earth
    australia = Region.create name: "Australia", parent: oceania
    new_zealand = Region.create name: "New Zealand", parent: oceania
    assert_raise ActiveRecord::RecordInvalid do
      oceania.parent = oceania
      oceania.save!
    end
    assert_raise ActiveRecord::RecordInvalid do
      oceania.parent = australia
      oceania.save!
    end
    pacific = Region.create name: "Pacific", parent: earth
    assert_equal [1, 3], [pacific.lftp, pacific.lftq]
    assert_equal [1, 2], [pacific.rgtp, pacific.rgtq]
    assert_equal 1.0 * 1 / 3, pacific.lft
    assert_equal 1.0 * 1 / 2, pacific.rgt
    oceania.parent = pacific
    oceania.save!
    assert_equal [0, 1], [earth.lftp, earth.lftq]
    assert_equal [1, 1], [earth.rgtp, earth.rgtq]
    assert_equal 1.0 * 0 / 1, earth.lft
    assert_equal 1.0 * 1 / 1, earth.rgt
    assert_equal [1, 3], [pacific.lftp, pacific.lftq]
    assert_equal [1, 2], [pacific.rgtp, pacific.rgtq]
    assert_equal 1.0 * 1 / 3, pacific.lft
    assert_equal 1.0 * 1 / 2, pacific.rgt
    assert_equal [2, 5], [oceania.lftp, oceania.lftq]
    assert_equal [1, 2], [oceania.rgtp, oceania.rgtq]
    assert_equal 1.0 * 2 / 5, oceania.lft
    assert_equal 1.0 * 1 / 2, oceania.rgt
    australia.reload
    assert_equal [3, 7], [australia.lftp, australia.lftq]
    assert_equal [1, 2], [australia.rgtp, australia.rgtq]
    assert_equal 1.0 * 3 / 7, australia.lft
    assert_equal 1.0 * 1 / 2, australia.rgt
    new_zealand.reload
    assert_equal [5, 12], [new_zealand.lftp, new_zealand.lftq]
    assert_equal [3, 7], [new_zealand.rgtp, new_zealand.rgtq]
    assert_equal 1.0 * 5 / 12, new_zealand.lft
    assert_equal 1.0 * 3 / 7, new_zealand.rgt
  end

  def test_move_from_left_to_right
    earth = Region.create name: 'earth'
    europe = Region.create name: 'europe', parent: earth
    america = Region.create name: 'america', parent: earth
    usa = Region.create name: 'usa', parent: america
    texas = Region.create name: 'texas', parent: usa
    houston = Region.create name: 'houston', parent: texas
    texas.parent = europe # oh noes, natzis gaining ground
    texas.save!
    assert_equal 0, houston.reload.descendants.count
    assert_equal 1, texas.reload.descendants.count
    assert_equal 2, europe.reload.descendants.count
    assert_equal 1, america.reload.descendants.count
    assert_equal 0, usa.reload.descendants.count
    assert_equal 5, earth.reload.descendants.count
  end

  def test_database_precision
    root = Region.create name: 'root'
    l1=Region.create(name: 'l1', parent: root)
    l2=Region.create(name: 'l2', parent: l1)
    l3=Region.create(name: 'l3', parent: l2)
    l4=Region.create(name: 'l4', parent: l3)
    l3.parent = l1
    l3.save!
    #make sure database calculates with the same precision as ruby
    #l3.rgt is calculated by ruby, but l4.rgt is calculated by the database
    #the difference is only visible with rgt as double
    assert_equal 2, Region.where("abs(rgt - (select rgt from regions where id =#{l3.id})) < 1e-16").count
  end

  def test_destroy
    earth = Region.create name: "Earth"
    oceania = Region.create name: "Oceania", parent: earth
    australia = Region.create name: "Australia", parent: oceania
    new_zealand = Region.create name: "New Zealand", parent: oceania
    assert_raise ActiveRecord::DeleteRestrictionError do
      oceania.destroy
    end
  end

  def test_scope
    earth = Region.create name: "Earth"
    oceania = Region.create name: "Oceania", parent: earth
    krypton = Region.create name: "Krypton", fiction: true
    assert_equal [earth], oceania.ancestors
    assert_equal [], krypton.descendants
  end

  def test_limits
    region = Region.create name: ""
    22.times do
      Region.create name: "", parent: region
      region = Region.create name: "", parent: region
    end
    region.descendants
  end

  def test_virtual_root_order
    Region.virtual_root = true
    r1 = Region.create name: "1"
    r2 = Region.create name: "2"
    r3 = Region.create name: "3"
    assert r3.rgt <= r2.lft
    assert r2.rgt <= r1.lft
  end
  
  def test_virtual_root_allocation
    Region.virtual_root = true
    r1 = Region.create name: "Europe"
    r2 = Region.create name: "Romania", :parent => r1
    r3 = Region.create name: "Asia"
    r4 = Region.create name: "America"
    assert_equal [["Europe", 1.0/2, 1.0], ["Romania", 2.0/3, 1.0],
      ["Asia", 1.0/3, 1.0/2], ["America", 1.0/4, 1.0/3]],
      Region.preorder.map { |r| [r.name, r.lft, r.rgt] }
  end
  
  def test_rebuild_nested_interval_tree
    Region.virtual_root = true
    r1 = Region.create name: "Europe"
    r2 = Region.create name: "Romania", parent: r1
    r3 = Region.create name: "Asia"
    r4 = Region.create name: "America"
    Region.rebuild_nested_interval_tree!
    assert_equal [["Europe", 0.5, 1.0], ["Romania", 2.0/3, 1.0],
      ["Asia", 1.0/3, 1.0/2], ["America", 1.0/4, 1.0/3]],
      Region.preorder.map { |r| [r.name, r.lft, r.rgt] }
  end
  
  def test_root_update_keeps_interval
    Region.virtual_root = true
    r1 = Region.create name: "Europe"
    r2 = Region.create name: "Romania", parent: r1
    r3 = Region.create name: "Asia"
    r4 = Region.create name: "America"
    lftq = r4.lftq
    r4.name = 'USA'
    r4.save
    assert_equal lftq, r4.lftq
  end
  
  def test_move_to_root_recomputes_interval
    Region.virtual_root = true
    r1 = Region.create name: "Europe"
    r2 = Region.create name: "Romania", parent: r1
    r3 = Region.create name: "Asia"
    r4 = Region.create name: "America"
    lftq = r2.lftq
    r2.parent = nil
    r2.save
    assert_not_equal lftq, r2.lftq
  end
  
end
