require 'spec_helper'

describe Rspreadsheet::Cell do
  before do 
    book1 = Rspreadsheet.new
    @sheet1 = book1.create_worksheet
    book2 = Rspreadsheet.new($test_filename)
    @sheet2 = book2.worksheets(1)
  end
  it 'contains good row and col coordinates' do
    @cell = @sheet1.cell(1,3)
    @cell.rowi.should == 1
    @cell.coli.should == 3
    @cell.coordinates.should == [1,3]
    @cell = @sheet2.cell(7,2)
    @cell.rowi.should == 7
    @cell.coli.should == 2
    @cell.coordinates.should == [7,2]
  end
  it 'can be referenced by more vars and both are synchronized' do
    @cell = @sheet1.cell(1,1)
    @sheet1[1,1] = 'novinka'
    @cell.value.should == 'novinka'
  end
  it 'can be modified by more ways and all are identical' do
    @cell = @sheet1.cell(2,2)
    @sheet1[2,2] = 'zaprve'
    @cell.value.should == 'zaprve'
    @sheet1.cell(2,2).value = 'zadruhe'
    @cell.value.should == 'zadruhe'
    @sheet1.B2 = 'zatreti'
    @cell.value.should == 'zatreti'
    @sheet1.row(2).cell(2).value = 'zactvrte'
    @cell.value.should == 'zactvrte'
    @sheet1.rows[1].cell(2).value = 'zactvrte s arrayem'
    @cell.value.should == 'zactvrte s arrayem'
    @sheet1.row(2)[2] = 'zapate'
    @cell.value.should == 'zapate'
    @sheet1.rows[1][2] = 'zaseste'  ## tohle je divoka moznost
    @cell.value.should == 'zaseste'
    
    @sheet1.A11 = 'dalsi test'
  end
  it 'can include links' do
    @sheet2.A12.should == '[http://example.org/]'
    @sheet2.cell(12,2).valuexmlfindall('.//text:a').size.should eq 0
    @sheet2.cell(12,1).valuexmlfindall('.//text:a').size.should eq 1
    @sheet2.cell(12,1).valuexmlfindfirst('.//text:a').attributes['href'].should eq 'http://example.org/'
  end
  it 'contains good row and col coordinates even after table:number-columns-repeated cells' do
    @cell = @sheet2.cell(13,5)
    @cell.value.should == 'afterrepeated'
    @cell.rowi.should == 13
    @cell.coli.should == 5
  end
  it 'reports good range of coordinates for repeated cells' do
    @cell = @sheet2.cell(13,2)
    @cell.range.should == (1..4)
    @cell.mode.should == :repeated
  end
  it 'returns nil on negative and zero cell indexes or raises exception depending on configuration' do
    pom = Rspreadsheet.raise_on_negative_coordinates
    # default is to raise error
    expect {@sheet2.cell(0,5) }.to raise_error
  
    # return nil if configured to do so
    Rspreadsheet.raise_on_negative_coordinates = false
    @sheet2.cell(0,5).should be_nil
    @sheet2.cell(2,-5).should be(nil)
    @sheet2.cell(-2,-5).should be(nil)
    
    Rspreadsheet.raise_on_negative_coordinates = pom  # reset the setting back
  end
  it 'has nonempty parents' do
    @cell = @sheet2.cell(13,5)
    @cell.row.should_not be_nil
    @cell.worksheet.should_not be_nil

    @cell = @sheet1.cell(2,2)
    @cell.row.should_not be_nil
    @cell.worksheet.should_not be_nil
  end
  it 'handles relative correctly' do
    @sheet2.cell(3,3).relative(-1,+2).coordinates.should == [2,5]
    @sheet2.cell(3,3).relative(0,0).coordinates.should == [3,3]
  end
  it 'is automatically "unrepeated" on value assignement' do
    @cell = @sheet2.cell(13,2)
    @cell.is_repeated?.should == true
    @cell.value = 'cokoli'
    @cell.is_repeated?.should == false
    @cell.value.should == 'cokoli'
    @sheet2.cell(13,1).should_not == 'cokoli'
    @sheet2.cell(13,3).should_not == 'cokoli'
    @sheet2.cell(13,4).should_not == 'cokoli'
  end
  it 'returns correct type for the cell' do
    @sheet2.cell(1,2).type.should eq :string
    @sheet2.cell(2,2).type.should eq :datetime
    @sheet2.cell(3,1).type.should eq :float
    @sheet2.cell(3,2).type.should eq :percentage
    @sheet2.cell(4,2).type.should eq :string
    @sheet2.cell('B22').type.should eq :currency
    @sheet2.cell('B23').type.should eq :currency
    @sheet2.cell(200,200).type.should eq :unassigned
  end
  it 'returns value of correct type' do
    @sheet2[1,2].should be_kind_of(String)
    @sheet2[2,2].should be_kind_of(DateTime)  # maybe date
    @sheet2[3,1].should be_kind_of(Float)
    @sheet2[3,2].should be_kind_of(Float)
    @sheet2.cell(3,2).type.should eq :percentage
    @sheet2.cell(3,2).guess_cell_type.should eq :percentage
    @sheet2.cell(3,2).guess_cell_type(1).should eq :percentage
    @sheet2[3,2]=0.1
    @sheet2.cell(3,2).type.should eq :percentage
    @sheet2[4,2].should be_kind_of(String)
  end
  it 'is the same object no matter how you access it' do
    @cell1 = @sheet2.cell(5,5)
    @cell2 = @sheet2.rows(5).cell(5)
    @cell1.should equal(@cell2)
  end
  it 'splits correctly cells if written in the middle of repeated group' do
    @cell = @sheet2.cell(4,6)
    @cell.range.should == (4..7)
    @cell.value.should == 7
    
    @cell.value = 'nebesa'
    @cell.range.should == (6..6)
    @cell.value.should == 'nebesa'
    
    @cellA = @sheet2.cell(4,5)
    @cellA.range.should == (4..5)
    @cellA.value.should == 7
    
    @cellB = @sheet2.cell(4,7)
    @cellB.range.should == (7..7)
    @cellB.value.should == 7
  end
  it 'inserts correctly cell in the middle of repeated group' do
    @cell = @sheet2.cell(4,6)
    @cell.range.should == (4..7)
    @cell.value.should == 7
    @cell.coli.should == 6
    
    @sheet2.insert_cell_before(4,6)
    @cell.coli.should == 7
    
    @cellA = @sheet2.cell(4,5)
    @cellA.range.should == (4..5)
    @cellA.value.should == 7
    
    @cellB = @sheet2.cell(4,7)
    @cellB.range.should == (7..8)
    @cellB.value.should == 7
    
    @cell = @sheet2.cell(16,4)
    @cell.range.should == (1..7)
    @cell.value.should == nil
    
    @sheet2.rows(15).range.should == (14..18)
    @sheet2.rows(16).range.should == (14..18)
    @sheet2.rows(17).range.should == (14..18)
    @sheet2.insert_cell_before(16,3)
    @sheet2.cell(16,3).value = 'baf'
    @sheet2.cell(17,3).value.should_not == 'baf'
    @sheet2.rows(15).range.should == (14..15)
    @sheet2.rows(16).range.should == (16..16)
    @sheet2.rows(17).range.should == (17..18)
    
    @cellA = @sheet2.cell(16,1)
    @cellA.range.should == (1..2)
    @cellA.value.should be_nil
    
    @cellB = @sheet2.cell(16,5)
    @cellB.range.should == (4..8)
    @cellB.value.should be_nil

  end
  it 'inserted has correct class' do # based on real error
    @sheet2.insert_cell_before(1,1)
    @sheet2.rows(1).cell(1).should be_kind_of(Rspreadsheet::Cell)
  end
  it 'method cells without arguments returns array of cells' do
    @a = @sheet2.rows(1).cells
    @a.should be_kind_of(Array)
    @a.each { |item| item.should be_kind_of(Rspreadsheet::Cell)}
  
  end
  it 'changes coordinates when row inserted above' do
    @sheet1.cell(2,2).detach
    @cell = @sheet1.cell(2,2)
    @cell.rowi.should == 2
    @sheet1.add_row_above(1)
    @cell.rowi.should == 3
  end
  it 'switches to invalid_reference cell when deleted' do
    @sheet1[2,5] = 'nejaka data'
    @cell = @sheet1.cell(2,2)
    @cell.value = 'data'
    @cell.invalid_reference?.should be false
    @cell.delete
    @cell.invalid_reference?.should be true
    expect { @cell.rowi }.to raise_error
    expect { @cell.address }.to raise_error
    
    @sheet1.cell(2,2).type.should == :empty
    @sheet1.cell(3,2).type.should == :unassigned
  end
  it 'switches to invalid_reference cell when its row is deleted' do
    @cell = @sheet1.cell(6,2)
    @cell.value = 'data'
    @cell.rowi.should == 6
    @sheet1.rows(6).delete
    expect { @cell.rowi }.to raise_error
    @cell.invalid_reference?.should be true
  end
  it 'has inspect method returning something good' do
    @cell = @sheet1.cell(6,2)
    @cell.value = 'abcde'
    expect(@cell.inspect).to include('abcde','::Cell','6','2','row')
  end
  it 'stores date correctly' do
    @cell = @sheet1.cell(1,1)
    @cell.value= Date.parse('2014-01-02')
    @cell.value.year.should eq 2014
    @cell.value.month.should eq 1
    @cell.value.day.should eq 2
  end
  it 'stores time correctly' do
    @cell = @sheet1.cell(1,1)
    @cell.value= Time.parse('2:42 pm')
    @cell.value.hour.should eq 14
    @cell.value.min.should eq 42
    @cell.value.sec.should eq 0
  end
  it 'parse_time_value converts correcty different time values' do
    dyear = 1899; dmonth = 12; dday = 30
    Rspreadsheet::Cell.parse_time_value('PT923451H33M00S').should == Time.new(2005,5,5,3,33,00,0)
    Rspreadsheet::Cell.parse_time_value('PT1H33M00S').should == Time.new(dyear,dmonth,dday,1,33,00,0)
    Rspreadsheet::Cell.parse_time_value('11:50').should == Time.parse('11:50')
  end
  it 'handles time of day correctly on assignement' do
    @sheet1.A11 = Rspreadsheet::Tools.new_time_value(2,13,27)
    @sheet1.A12 = @sheet1.A11
    @sheet1.A12.should == @sheet1.A11
    @sheet1.cell('A12').type.should == :time
  end
  it 'can read various types of times' do
    expect {@cell = @sheet2.cell('D22'); @cell.value }.not_to raise_error
    @cell.value.hour.should == 2
    @cell.value.min.should == 22
    expect {@cell = @sheet2.cell('D23'); @cell.value }.not_to raise_error
    @cell.value.should == Time.new(2005,5,5,3,33,0,0)
  end
  it 'handles dates and datetimes correctly on assignement' do
    @sheet1.A11 = DateTime.new(2011,1,1,2,13,27,0)
    @sheet1.A12 = @sheet1.A11
    @sheet1.A12.should == @sheet1.A11
    @sheet1.cell('A12').type.should == :datetime
    @sheet1.A11 = Date.new(2012,2,2)
    @sheet1.A12 = @sheet1.A11
    @sheet1.A12.should == @sheet1.A11
    @sheet1.cell('A12').type.should == :datetime
  end
  it 'can read various types of dates' do
    expect {@cell = @sheet2.cell('F22'); @cell.value }.not_to raise_error
    @cell.value.should == DateTime.new(2006,6,6,3,44,21,0)
  end
  it 'can be addressed by even more ways and all are identical' do
    @cell = @sheet1.cell(2,2)
    @sheet1.cell('B2').value = 'zaseste'
    @sheet1.cell('B2').value.should == 'zaseste'
    @cell.value.should == 'zaseste'
    @sheet1.cell(2,'B').value.should == 'zaseste'
    @sheet1.cell(2,'B').value = 'zasedme'
    @cell.value.should == 'zasedme'
    @sheet1['B2'].should == 'zasedme'
    @sheet1['B2'] = 'zaosme'
    @cell.value.should == 'zaosme'
    
    @sheet2.cell('F2').should be @sheet2.cell(2,6)
    @sheet2.cell('BA177').should be @sheet2.cell(177,53)
    @sheet2.cell('ADA2').should be @sheet2.cell(2,781)
  end
  it 'remembers formula when set' do
    @cell = @sheet1.cell(1,1)
    @cell.formula.should be_nil
    @cell.formula='=1+5'
    @cell.formula.should eq '=1+5'
  end
  it 'unsets cell type when formula set - we can not guess it correctly' do
    @cell = @sheet1.cell(1,1)
    @cell.value = 'ahoj'
    @cell.type.should eq :string
    @cell.formula='=1+5'
    typ = @cell.xmlnode.nil? ? 'N/A' : @cell.xmlnode.attributes['value-type']
    @cell.type.should_not eq :string
    @cell.type.should eq :empty
  end
  it 'wipes out formula after assiging value' do
    @cell = @sheet1.cell(1,1)
    @cell.formula='=1+5'
    @cell.formula.should_not be_nil
    @cell.value = 'baf'
    @cell.type.should eq :string
    @cell.formula.should be_nil
  end
  it 'works well with currency types' do
    @usdcell = @sheet2.cell('B22')
    @usdcell.type.should eq :currency
    @usdcell.value.should == -147984.84
    @usdcell.format.currency.should == 'USD'
    
    @czkcell = @sheet2.cell('B23')
    @czkcell.value.should == 344.to_d
    @czkcell.format.currency.should == 'CZK'
    
    @czkcell.value = 200.to_d
    @czkcell.value.should == 200.to_d
    @czkcell.format.currency.should == 'CZK'
  end
  it 'preserves currency format when float is assingned to it' do
    @cell = @sheet2.cell('B23')
    @cell.type.should eq :currency
    @cell.format.currency.should == 'CZK'
    
    @cell.value = 666.66.to_d
    @cell.value.should == 666.66.to_d
    @cell.type.should eq :currency
    @cell.format.currency.should == 'CZK'
  end
  
  it 'gracefully accepts nil in assignement' do
    expect {
      @sheet2.cell('B1').value = nil
      @sheet2.cell('B2').value = nil
      @sheet2.cell('B3').value = nil
      @sheet2.cell('B22').value = nil
      @sheet2.cell('D22').value = nil
      @sheet2.cell('F22').value = nil    
    }.not_to raise_error
  end
  it 'can be inserted before existing cell and this one is shifted right' do
    @sheet1.B2 = 'test'
    @sheet1.B2.should == 'test'
    inscell = @sheet1.insert_cell_before(2,2)
    inscell.value = 'new'
    @sheet1.B2.should == 'new'
    @sheet1.C2.should == 'test'
    inscell = @sheet1.insert_cell_before(2,4) # should not move cells with data
    @sheet1.C2.should == 'test' 
  end

  it 'Does not ignore cells covered by other merged cells (issue 42)' do
    sheet = Rspreadsheet.new('./spec/testfile-issue-42-43.ods').sheet(1)
    sheet.C1.should == 'week1'
    sheet.C2.should == 'week2'
    sheet.C3.should == 'week3'
    sheet.C4.should == 'week4'
    sheet.C7.should == 'week7'        
  end
  it 'Does not ignore rows repeated on every page = header rows (issue 43)' do
    sheet = Rspreadsheet.new('./spec/testfile-issue-42-43.ods').sheet(2)
    sheet.A1.should == 'Schedule'
    sheet.B2.should == 'Course'
    sheet.A3.should == 'Teacher'
  end
  it 'responds by exception to unusual cases' do
    cell = @sheet1.cell('A1')
    cell.define_singleton_method(:mode) { :intenionally_unusual_mode_just_for_test }
    expect { cell.value }.to raise_error(/Unknown cell mode/)
  end
end
