require "numru/gphys/gphys"
require "numru/gphys/varraymm5"

=begin
=module NumRu::GPhys::MM5_IO

helps read(/write) MM5-formatted data

==Module functions
---open(file, varname)
    GPhys constructor from MM5.

    ARGUMENTS
    * file (MM5data or String): file to read. If string,
      it must be the name (path) of a MM5 file. 
    * varname (String): name of the varible in the file, for which
      a GPhys object is constructed.

    RETURN VALUE
    * a GPhys

    EXAMPLE
    * Suppose that you have a file MMOUT_DOMAIN1 in the currentdirectly,
      and it contains a variable "T". The following creates a GPhys
      object representing the variable in the file.

        require "numru/gphys"
        require "numru/gphys/gphys_mm5_io"
        include NumRu
        temp = GPhys::MM5_IO.open("MMOUT_DOMAIN1","T")

---write(file, gphys, name=nil)

    writes a GPhys object into a MM5 file. -- !!not implemented yet!!

=end

module NumRu
  class GPhys
    module MM5_IO

      module_function

      def open(file, varname)
        if file.is_a?(String)
          file = MM5Data.open(file)
        elsif ! file.is_a?(MM5Data)
          raise ArgumentError, "1st arg must be a MM5Data or a file name"
        end

        mm5var = file.var(varname)
        data = VArrayMM5.new(mm5var)

	case mm5var.ordering
	when "YXP "
	  if mm5var.staggering == "D"
	    axposnames = ["xdot","ydot","PRESSURE","t"]
	  else
            axposnames = ["x","y","PRESSURE","t"]
	  end
          rank = 4
	when "YXS "
	  if mm5var.staggering == "D"
	    axposnames = ["xdot","ydot","SIGMAH","t"]
	  else
            axposnames = ["x","y","SIGMAH","t"]
	  end
          rank = 4
	when "YXW "
	  if mm5var.staggering == "D"
	    axposnames = ["xdot","ydot","sigmafull","t"]
	  else
            axposnames = ["x","y","sigmafull","t"]
	  end
          rank = 4
	when "YX  "
	  if mm5var.staggering == "D"
	    axposnames = ["xdot","ydot","t"]
	  else
	    axposnames = ["x","y","t"]
	  end
          rank = 3
	when "CA  "
          axposnames = ["landusecategory","landuse2","t"]
          rank = 3
        when "XSB "
	  if mm5var.staggering == "D"
            axposnames = ["xdot","SIGMAH","boundary","t"]
          else
            axposnames = ["x","SIGMAH","boundary","t"]
          end
          rank = 4
        when "YSB "
	  if mm5var.staggering == "D"
            axposnames = ["ydot","SIGMAH","boundary","t"]
          else
            axposnames = ["y","SIGMAH","boundary","t"]
          end
          rank = 4
        when "XWB "
	  if mm5var.staggering == "D"
            axposnames = ["xdot","sigmafull","boundary","t"]
          else
            axposnames = ["x","sigmafull","boundary","t"]
          end
          rank = 4
        when "YWB "
	  if mm5var.staggering == "D"
            axposnames = ["ydot","sigmafull","boundary","t"]            
          else
            axposnames = ["y","sigmafull","boundary","t"]
          end
          rank = 4
        when "S   "
	  axposnames = ["z"]
	  rank = 1
        when "P   "
	  axposnames = ["PRESSURE"]
	  rank = 1
	else
	end
        bare_index = [ false ] * rank # will be true if coord var is not found

        axes = Array.new
        for i in 0...rank
          axpos = VArrayMM5.new( file.var(axposnames[i]) )
          cell_center = true
          cell = false

          axis = Axis.new(cell,bare_index[i])
          if !cell
            axis.set_pos( axpos )
          else
            if cell_center
              if varray_cell_bounds
                axis.set_cell(axpos, varray_cell_bounds).set_pos_to_center
              else
                p "cell bounds are guessed"
                axis.set_cell_guess_bounds(axpos).set_pos_to_center
              end
            else  # then it is cell_bounds
              if varray_cell_center
                axis.set_cell(varray_cell_center, axpos).set_pos_to_bounds
              else
                p "cell center is guessed"
                axis.set_cell_guess_center(axpos).set_pos_to_bounds
              end
            end
          end
          
          #p "yet-to-be-defined: method to define aux coord vars"
          
          axes[i] = axis
        end

        grid = Grid.new( *axes )

        GPhys.new(grid,data)
      end

      def write(file, gphys, name=nil)
      #  if file.is_a?(String)
      #    file = MM5data.open(file,"w")
      #  elsif ! file.is_a?(MM5data)
      #    raise ArgumentError, "1st arg must be a GrADS_Gridded or a file name"
      #  end
      #  VArrayMM5.write_control(file, gphys)
      #  VArrayMM5.write_binary(file, gphys.data)
      end

      def is_a_MM5?(filename)
        mm5 = false
        begin
          file = MM5Data.open(filename)
          file.bh_read
          filetype = file.bh.bhi[0,0]
          if filetype > 0 and filetype < 12 
            mm5 = true
          end
        ensure
          file.close
        end
        return mm5
      end

      def var_names(file)
        case file
        when String
          file = MM5Data.open(file)
        when MM5Data
        else
          raise ArgumentError, "arg must be a MM5Data or a file name"
        end
        return file.var_names
      end
      
      def var_names_except_coordinates(file)
        return var_names(file)
      end
    end      # module MM5_IO

    #===================================================================

    module IO

      module_function

      ## // additional file type selector -->
      MM5 = "MM5"
      @@iomdl =     {NETCDF => GPhys::NetCDF_IO,
                     GRADS => GPhys::GrADS_IO,
                     GRIB => GPhys::Grib_IO,
                     MM5 => GPhys::MM5_IO}
      @@mm5_pattern = [/\_DOMAIN/]

      def file2type(file)
 	case file
	when Array, NArray
	  return file2type(file[0])   # inspect the first element (ignoring the rest)
	when NetCDF
	  return NETCDF
	when GrADS_Gridded
	  return GRADS
        when MM5Data
          return MM5
	when Regexp
	  return NETCDF     # So far, only NetCDF_IO supports Regexp. 
	when *@@nc_pattern
	  return NETCDF
	when *@@grad_pattern
	  return GRADS
	when *@@grib_pattern
          return GRIB
	when String
          return nil until File.exist?(file)
          return NETCDF if NetCDF_IO.is_a_NetCDF?(file)
          return GRADS if GrADS_IO.is_a_GrADS?(file)
          return GRIB if Grib_IO.is_a_Grib?(file)
          return MM5 if MM5_IO.is_a_MM5?(file)
	end
        return nil
      end
      types = ['nc','grad','grib','mm5']
      types.each{|c|
	eval <<-EOS
        def add_#{c}_pattern(*regexps)
          regexps.each{ |regexp|
            raise TypeError,"Regexp expected" unless Regexp===regexp
            @@#{c}_pattern.push(regexp)
          }
          nil
        end
	EOS
      }

      types.each{|c|
	eval <<-EOS
        def set_#{c}_pattern(*regexps)
          regexps.each{ |regexp|
            raise TypeError,"Regexp expected" unless Regexp===regexp
          }
          @@#{c}_pattern = regexps
          nil
        end
	EOS
      }
      ## <-- additional file type selector //

    end      # module IO
  end      #class GPhys
end      #module NumRu

######################################################
if $0 == __FILE__
   include NumRu

   #begin
   #  file = GrADS_Gridded.open("../../testdata/T.jan.ctl")
   #rescue
   #  file = GrADS_Gridded.open("../../../testdata/T.jan.ctl")
   #end
   #temp = GPhys::GrADS_IO.open(file,"T")
   #p temp.name, temp.shape_current
   #temp2 = temp[true,true,2,0]
   #p temp2.name, temp2.shape_current
   #
   #temp_xmean = temp.average(0)
   #p temp.val
   #
   #temp_edy = ( temp - temp_xmean )
   #p '$$$',temp_edy.name,temp_edy.val[0,true,true,0]
   #p '@@@',temp
   #p '///',temp.copy
   #p '+++',temp2
   #
   #puts "\n** test write (tmp.nc) **"
   #require "numru/gphys/gphys_netcdf_io"
   #file2 = NetCDF.create('tmp.nc')
   #p v = temp_edy.axis(0).pos[0..-2].copy.rename('lonlon')
   #temp_edy.axis(0).set_aux('test',v)
   #temp_edy.axis(0).set_aux('test2',(v/2).rename('lonlon2'))
   #temp_edy.axis(0).set_aux('test2',(v/2).rename('lonlon3')[0..-2])
   #GPhys::NetCDF_IO.write(file2,temp_edy)
   #file2.close
   #file3 = NetCDF.create('tmp2.nc')
   #GPhys::NetCDF_IO.write(file2,temp_xmean)
   #file3.close
   #
   #puts "\n** test write (tmp.ctl) **"
   #file1 = GrADS_Gridded.create("tmp2.ctl")
   #file1.put_att("big_endian",true)
   #file1.put_att("dset","tmp2.dr")
   #file1.put_att("title","test control file")
   #file1.put_att("undef",-999.0)
   #GPhys::GrADS_IO.write(file1,temp_edy)

end
