##
# spec/lib/skn_utils/circular_linked_list_spec.rb
#

RSpec.describe SknUtils::Lists::CircularLinkedList, "Circular LinkedList " do

  context "Initialization" do
    it "can be initialized without params" do
      expect(subject).to be
    end
    it "can insert the first value" do
      expect(subject.empty?).to be true
      expect(subject.insert(101)).to eq(1)
    end
    it "can be cleared" do
      subject.insert(101)
      expect(subject.clear).to eq(1)
    end
    it "can be initialized with one or more initial values" do
      list = described_class.new(10,100,100)
      expect(list.current).to eq(10)
    end
    it "is initially empty?" do
      expect(subject.empty?).to be true
    end
  end

  context "Navigation" do
    let(:list) { described_class.new(10,20, 30, 40, 50, 60, 70, 80, 90, 100) }

    it "#first returns the first value" do
      expect(list.first).to eq(10)
    end
    it "#next returns the second value" do
      expect(list.first).to eq(10)
      expect(list.next).to eq(20)
    end
    it "#current returns the first value " do
      expect(list.current).to eq(10)
    end
    it "#prev returns the prior value" do
      expect(list.prev).to eq(100)
    end
    it "#last returns the last value" do
      expect(list.last).to eq(100)
    end
    it "#nth(6) returns the sixth value" do
      expect(list.first).to eq(10)
      expect(list.nth(6)).to eq(60)
      expect(list.nth(-2)).to eq(40)
    end
    it "#at_index(6) returns the sixth value" do
      expect(list.at_index(6)).to eq(60)
    end

  end
  context "Insertions" do
    it "#insert(value) indicates a value was added" do
      bsize = subject.size
      expect(subject.insert(110)).to eq(bsize + 1)
    end
    it "#prepend(value) indicates a value was added" do
      bsize = subject.size
      expect(subject.prepend(110)).to eq(bsize + 1)
    end
    it "#append(value) indicates a value was added" do
      bsize = subject.size
      expect(subject.append(110)).to eq(bsize + 1)
    end
    it "#insert_before(pvalue,value) indicates a value was added" do
      subject.insert(120)
      bsize = subject.size
      expect(subject.insert_before(120, 110)).to eq(bsize + 1)
      expect(subject.to_a).to eq([110,120])
    end
    it "#insert_after(value) indicates a value was added" do
      subject.insert(120)
      bsize = subject.size
      expect(subject.insert_after(120, 125)).to eq(bsize + 1)
      expect(subject.to_a).to eq([120,125])
    end
  end

  context "Removals" do
    let(:list) { described_class.new(10,20, 30, 40, 50, 60, 70, 80, 90, 100) }

    it "#remove(value) removes first occurance of that value" do
      bsize = list.size
      expect(list.remove(30)).to eq(bsize - 1)
      expect(list.to_a).to eq([10,20, 40, 50, 60, 70, 80, 90, 100])
    end

    it "#clear removes all elements from list" do
      expect(list.clear).to eq(10)
      expect(list.empty?).to be true
    end
  end

  context "Enumeration" do
    let(:list) { described_class.new(10,20, 30, 40, 50, 60, 70, 80, 90, 100) }
    it "#each works as expected when block is provided" do
      x = []
      list.each {|r| x << r}
      expect(x).to be_a(Array)
      expect(x).to eq([10,20, 30, 40, 50, 60, 70, 80, 90, 100])
    end
    it "#each works as expected when no block is offered" do
      expect(list.each).to be_a(Enumerator)
      expect(list.each.first).to eq(10)
    end
    it "#to_a returns the contents of linkedlist as an Array" do
      base = list.to_a
      expect(base).to be_a(Array)
      expect(base).to eq([10,20, 30, 40, 50, 60, 70, 80, 90, 100])
    end
  end

  context "Edge cases " do
    let(:list) { described_class.new(10,20, 30, 40, 50, 60, 70, 80, 90, 100) }

    it "#at_index(-999) fails and returns the current element. " do
      expect(list.at_index(-999)).to eq(10)
    end
    it "#at_index(0) fails and returns the current element. " do
      expect(list.at_index(0)).to eq(10)
    end
    it "#at_index(999) fails and returns the current element. " do
      expect(list.at_index(999)).to eq(10)
    end
    it "#at_index(n) returns the proper element. " do
      expect(list.at_index(1)).to eq(10)
      expect(list.at_index(list.size / 2)).to eq(50)
      expect(list.at_index(list.size)).to eq(100)
    end
    it "#at_index(n) returns the proper element for linkedlist with one element. " do
      only = described_class.new(55)
      expect(only.at_index(1)).to eq(55)
      expect(only.at_index(10)).to eq(55)
      expect(only.at_index(-10)).to eq(55)
    end

    it "#nth(-999) fails and returns current initialization value." do
      expect(list.nth(-999)).to eq(10)
    end
    it "#nth(0) returns current value, or last initialization value." do
      expect(list.nth(0)).to eq(10)
    end
    it "#nth(999) fails and returns current initialization value." do
      expect(list.nth(999)).to eq(10)
    end
    it "#current equals first initialization value." do
      expect(list.current).to eq(10)
    end
    it "#next after initialization returns correctly. " do
      expect(list.next).to eq(20)
      expect(list.next).to eq(30)
      expect(list.next).to eq(40)
    end
    it "#prev after first returns proper sequence of values. " do
      expect(list.first).to eq(10)
      expect(list.prev).to eq(100)
      expect(list.prev).to eq(90)
    end
    it "#first, #next, #current, #prev, #nth, and #last return same value after initialization with one value. " do
      only = described_class.new(55)
      expect(only.first).to eq(55)
      expect(only.next).to eq(55)
      expect(only.prev).to eq(55)
      expect(only.last).to eq(55)
      expect(only.current).to eq(55)
      expect(only.nth(1)).to eq(55)
      expect(only.nth(11)).to eq(55)
    end
    it "#first, #next, #current, #prev, #nth, and #last return same value after initialization with no values. " do
      only = described_class.new
      expect(only.first).to be nil
      expect(only.next).to be nil
      expect(only.prev).to be nil
      expect(only.last).to be nil
      expect(only.current).to be nil
      expect(only.nth(1)).to be nil
      expect(only.nth(-1)).to be nil
    end
    it "#prepend enables navigation methods normal operations. " do
      only = described_class.new
      only.prepend(55)
      expect(only.first).to eq(55)
      expect(only.next).to eq(55)
      expect(only.prev).to eq(55)
      expect(only.last).to eq(55)
      expect(only.current).to eq(55)
      expect(only.nth(1)).to eq(55)
      expect(only.nth(11)).to eq(55)
    end
    it "#append enables navigation methods normal operations. " do
      only = described_class.new
      only.append(55)
      expect(only.first).to eq(55)
      expect(only.next).to eq(55)
      expect(only.prev).to eq(55)
      expect(only.last).to eq(55)
      expect(only.current).to eq(55)
      expect(only.nth(1)).to eq(55)
      expect(only.nth(11)).to eq(55)
    end
    it "#insert_before enables navigation methods normal operations. " do
      only = described_class.new
      only.insert_before(nil, 55)
      expect(only.first).to eq(55)
      expect(only.next).to eq(55)
      expect(only.prev).to eq(55)
      expect(only.last).to eq(55)
      expect(only.current).to eq(55)
      expect(only.nth(1)).to eq(55)
      expect(only.nth(11)).to eq(55)
    end
    it "#insert_after enables navigation methods normal operations. " do
      only = described_class.new
      only.insert_after(nil, 55)
      expect(only.first).to eq(55)
      expect(only.next).to eq(55)
      expect(only.prev).to eq(55)
      expect(only.last).to eq(55)
      expect(only.current).to eq(55)
      expect(only.nth(1)).to eq(55)
      expect(only.nth(11)).to eq(55)
    end
    it "#remove does not make navigation methods unstable if only element. " do
      only = described_class.new(55)
      only.remove(55)
      expect(only.first).to be nil
      expect(only.next).to be nil
      expect(only.prev).to be nil
      expect(only.last).to be nil
      expect(only.current).to be nil
      expect(only.nth(1)).to be nil
      expect(only.nth(-1)).to be nil
    end
  end

  context "Sort Feature" do
    let(:num_list)   { described_class.new(100, 50, 10, 40, 80, 30, 60, 90, 70, 20, 110) }
    let(:alpha_list) { described_class.new('Z', 'K', 'S', 'n', 's', 'z', 'k', 'N', 'o', 'A') }
    let(:hash_list)  { described_class.new({key: 'Z'}, {key: 'K'}, {key: 'S'}, {key: 'n'}, {key: 's'},
                                           {key: 'z'}, {key: 'k'}, {key: 'N'}, {key: 'o'}, {key: 'A'}
                                          ) {|a| a[:key]}
    }

    it "#sort! redefines numeric list in asending order" do
      expect(num_list.sort!).to eq(11)
      expect(num_list.to_a).to eq([10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110])
    end
    it "#sort!(:desc) redefines numeric list in descending order" do
      expect(num_list.sort!(:desc)).to eq(11)
      expect(num_list.to_a).to eq([110, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10])
    end
    it "#sort! redefines alpha numeric list in asending order" do
      expect(alpha_list.sort!).to eq(10)
      expect(alpha_list.to_a).to eq(["A", "K", "N", "S", "Z", "k", "n", "o", "s", "z"])
    end
    it "#sort!(:desc) redefines alpha numeric list in descending order" do
      expect(alpha_list.sort!(:desc)).to eq(10)
      expect(alpha_list.to_a).to eq(["z", "s", "o", "n", "k", "Z", "S", "N", "K", "A"])
    end
    it "#sort!() redefines hash object values in default order" do
      expect(hash_list.sort!).to eq(10)
      expect(hash_list.to_a).to eq([{:key=>"A"}, {:key=>"K"}, {:key=>"N"}, {:key=>"S"}, {:key=>"Z"},
                                    {:key=>"k"}, {:key=>"n"}, {:key=>"o"}, {:key=>"s"}, {:key=>"z"}])
    end
    it "#sort!() lambda overrides sort_condifiton and sorts hash object values in custom order" do
      expect(hash_list.sort!() {|a,b| a[:key] <= b[:key] }).to eq(10)
      expect(hash_list.to_a).to eq([{:key=>"z"}, {:key=>"s"}, {:key=>"o"}, {:key=>"n"}, {:key=>"k"},
                                    {:key=>"Z"}, {:key=>"S"}, {:key=>"N"}, {:key=>"K"}, {:key=>"A"}])
    end
  end

end