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

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|
        NumRu::Grib.is_a_Grib?(fname).should be_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)
        file.should be_a_kind_of(NumRu::Grib)
        @files.push file
      end
    end
    it 'raise exception when file name which does not exist is given' do
      proc { NumRu::Grib.open(fname_not_exist)}.should raise_error
    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|
        proc {file.close}.should_not 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 {|fname,file| file.path.should == fname}
    end
    it '#var_names returns Array of variable names' do
      @files.each {|fname, file| file.var_names.should == GribData[fname].keys}
    end
    it '#var returns GribVar object' do
      @files.each do |fname, file|
        GribData[fname].each do |vname, hash|
          file.var(vname).should be_a_kind_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| var.rank.should == hash[:shape].length}
  end
  it '#total returns total length' do
    @vars.each{|var,hash| var.total.should == hash[:shape].inject(1,:*)}
  end
  it '#dim_names returns Array of dimension names' do
    @vars.each{|var,hash| var.dim_names.should == hash[:dims]}
  end
  it '#shape returns Array of shape' do
    @vars.each{|var,hash| var.shape.should == hash[:shape]}
  end
  it '#dim returns GribDim' do
    @vars.each do |var,hash|
      hash[:dims].each{|dname| var.dim(dname).should be_a_kind_of NumRu::GribDim}
    end
  end
  it '#att_names returns Array of attribute names' do
    @vars.each{|var,hash| var.att_names.should be_a_kind_of Array}
  end
  it '#att returns attribute value' do
    @vars.each do |var,hash|
      var.att_names.each{|aname| var.att(aname).should_not == nil }
    end
  end
  it '#typecode returns type code' do
    @vars.each{|var,hash| var.typecode.should be_a_kind_of Fixnum}
  end
  it '#missing_value returns missing value' do
    @vars.each{|var,hash| var.missing_value.should be_a_kind_of Numeric}
  end
  it '#get returns value' do
    @vars.each do |var,hash|
      hash[:val].each do |idx,val|
        v = var.get[*idx]
        ((v-val).abs/((v+val)/2)).should < 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| dim.length.should == len}
  end
  it '#val returns value' do
    @dims.each do |dim,len|
      dim.val.should be_a_kind_of NArray
      dim.val.length.should == len
    end
  end
  it '#typecode returns typecode' do
    @dims.each{|dim,len| dim.typecode.should be_a_kind_of Fixnum}
  end
  it '#att_names returns Array of attribute names' do
    @dims.each{|dim,len| dim.att_names.should be_a_kind_of Array}
  end
  it '#att returns attributes value' do
    @dims.each do |dim,len|
      dim.att_names.each{|aname| dim.att(aname).should_not == nil }
    end
  end

end
