require File.expand_path(File.join('.','spec_helper'), File.dirname(__FILE__))
include NumRu

dir = File.expand_path(File.join('..','data'), File.dirname(__FILE__))
GribData = {
  File.join(dir, 'regular_gaussian_surface.grib1') =>
  {"2t"=>{:dims=>["lon","lat"], :shape=>[128,64], :val=>{[0,0]=>2.4552479553e2}}
  },
  File.join(dir, 'regular_gaussian_surface.grib2') =>
  {"2t"=>{:dims=>["lon","lat"], :shape=>[128,64], :val=>{[0,0]=>2.4552479553e2}}
  },
  File.join(dir, 'regular_gaussian_model_level.grib1') =>
  {"t"=>{:dims=>["lon","lat"], :shape=>[128,64], :val=>{[0,0]=>1.9907820129e2}}
  },
  File.join(dir, 'regular_gaussian_model_level.grib2') =>
  {"t"=>{:dims=>["lon","lat"], :shape=>[128,64], :val=>{[0,0]=>1.9907820129e2}}
  },
  File.join(dir, 'regular_gaussian_pressure_level.grib1') =>
  {"t"=>{:dims=>["lon","lat"], :shape=>[128,64], :val=>{[0,0]=>2.4746011353e2}}
  },
  File.join(dir, 'regular_gaussian_pressure_level.grib2') =>
  {"t"=>{:dims=>["lon","lat"], :shape=>[128,64], :val=>{[0,0]=>2.4746011353e2}}
  },
  File.join(dir, 'regular_gaussian_pressure_level_constant.grib1') =>
  {"t"=>{:dims=>["lon","lat"], :shape=>[128,64], :val=>{[0,0]=>1.0}}
  },
  File.join(dir, 'regular_gaussian_pressure_level_constant.grib2') =>
  {"t"=>{:dims=>["lon","lat"], :shape=>[128,64], :val=>{[0,0]=>1.0}}
  },
  File.join(dir, 'regular_latlon_surface.grib1') =>
  {"2t"=>{:dims=>["lon","lat"], :shape=>[16,31], :val=>{[0,0]=>279.0}}
  },
  File.join(dir, 'regular_latlon_surface.grib2') =>
  {"2t"=>{:dims=>["lon","lat"], :shape=>[16,31], :val=>{[0,0]=>279.0}}
  },
  File.join(dir, 'regular_latlon_surface_constant.grib1') =>
  {"2t"=>{:dims=>["lon","lat"], :shape=>[16,31], :val=>{[0,0]=>1.0}}
  },
  File.join(dir, 'regular_latlon_surface_constant.grib2') =>
  {"2t"=>{:dims=>["lon","lat"], :shape=>[16,31], :val=>{[0,0]=>1.0}}
  },
  File.join(dir, 'tp_ecmwf.grib') =>
  {"tp"=>{:dims=>["lon","lat","step"], :shape=>[16,31,4], :val=>{[0,0,0]=>2.0546913147e-3}}
  }
}
fname_not_exist = 'not_exist'

describe NumRu::Grib do
  describe 'Grib.is_a_Grib?' do
    it 'returns whether the specific file is a GRIB1/2 file' do
      GribData.each do |fname, hash|
        expect(NumRu::Grib.is_a_Grib?(fname)).to eq true
      end
    end
  end

  describe 'Grib.open' do
    before { @files = [] }
    after { @files.each{|file| file.close} }
    it 'returns Grib object' do
      GribData.each do |fname, hash|
        file = NumRu::Grib.open(fname)
        expect(file).to be_instance_of(NumRu::Grib)
        @files.push file
      end
    end
    it 'raise exception when file name which does not exist is given' do
      expect { NumRu::Grib.open(fname_not_exist) }.to raise_error(RuntimeError)
    end
  end

  describe '#close' do
    before do
      @files = []
      GribData.each{|fname,hash| @files.push NumRu::Grib.open(fname) }
    end
    it do
      @files.each do |file|
        expect {file.close}.not_to raise_error
      end
    end
  end

  describe 'instance methods' do
    before do
      @files = {}
      GribData.each{|fname,hash| @files[fname] = NumRu::Grib.open(fname) }
    end
    after do
      @files.each{|fname,file| file.close}
    end
    it '#path returns path' do
      @files.each do |fname,file|
        expect( file.path ).to eq fname
      end
    end
    it '#var_names returns Array of variable names' do
      @files.each do |fname, file|
        expect( file.var_names ).to eq GribData[fname].keys
      end
    end
    it '#var returns GribVar object' do
      @files.each do |fname, file|
        GribData[fname].each do |vname, hash|
          expect( file.var(vname) ).to be_instance_of NumRu::GribVar
        end
      end
    end
  end
end

describe NumRu::GribVar do
  before do
    @files = []
    @vars = Hash.new
    GribData.each do |fname,vars|
      file = NumRu::Grib.open(fname)
      @files.push file
      vars.each{|vname, hash| @vars[file.var(vname)] = hash}
    end
  end
  after { @files.each{|file| file.close}}
  it '#rank returns rank' do
    @vars.each{|var,hash| expect(var.rank).to eq hash[:shape].length}
  end
  it '#total returns total length' do
    @vars.each{|var,hash| expect(var.total).to eq hash[:shape].inject(1,:*)}
  end
  it '#dim_names returns Array of dimension names' do
    @vars.each{|var,hash| expect(var.dim_names).to eq hash[:dims]}
  end
  it '#shape returns Array of shape' do
    @vars.each{|var,hash| expect(var.shape).to eq hash[:shape]}
  end
  it '#dim returns GribDim' do
    @vars.each do |var,hash|
      hash[:dims].each{|dname| expect(var.dim(dname)).to be_instance_of NumRu::GribDim}
    end
  end
  it '#att_names returns Array of attribute names' do
    @vars.each{|var,hash| expect(var.att_names).to be_instance_of Array}
  end
  it '#att returns attribute value' do
    @vars.each do |var,hash|
      var.att_names.each{|aname| expect(var.att(aname)).to_not eq nil }
    end
  end
  it '#typecode returns type code' do
    @vars.each{|var,hash| expect(var.typecode).to be_instance_of Fixnum}
  end
  it '#missing_value returns missing value' do
    @vars.each{|var,hash| expect(var.missing_value).to be_kind_of Numeric}
  end
  it '#get returns value' do
    @vars.each do |var,hash|
      hash[:val].each do |idx,val|
        v = var.get[*idx]
        expect((v-val).abs/((v+val)/2)).to be < 1e-10
      end
    end
  end
end

describe NumRu::GribDim do
  before do
    @files = []
    @dims = Hash.new
    GribData.each do |fname, vars|
      file = NumRu::Grib.open(fname)
      @files.push file
      vars.each do |vname, hash|
        var = file.var(vname)
        hash[:dims].each_with_index do |dname,i|
          @dims[var.dim(dname)] = hash[:shape][i]
        end
      end
    end
  end
  after do
    @files.each{|file| file.close}
  end
  it '#length returns length' do
    @dims.each{|dim,len| expect(dim.length).to eq len}
  end
  it '#val returns value' do
    @dims.each do |dim,len|
      expect(dim.val).to be_instance_of NArray
      expect(dim.val.length).to eq len
    end
  end
  it '#typecode returns typecode' do
    @dims.each{|dim,len| expect(dim.typecode).to be_instance_of Fixnum}
  end
  it '#att_names returns Array of attribute names' do
    @dims.each{|dim,len| expect(dim.att_names).to be_instance_of Array}
  end
  it '#att returns attributes value' do
    @dims.each do |dim,len|
      dim.att_names.each{|aname| expect(dim.att(aname)).to_not eq nil }
    end
  end

end
