! netcdf_variable.f90 - object-oriented netCDF IF (basic variable)
! vi: set sw=4:
! Copyright (C) TOYODA Eizi, 2000.  All rights reserved.
!
! 2000-01-11 豊田英司	初版

module netcdf_variable
    use dc_string
    use netcdf_v3
    use netcdf_error
    use netcdf_file
    use netcdf_dimension
    private

    public:: NC_VARIABLE, NC_VARIABLE_ERROR

    type NC_VARIABLE
	type(NC_FILE)::		file
	integer::		id
    end type

    !
    ! --- 変数の取得と利用 ----
    !

    public:: Variable, Fullname, Name, Type, Dimensions, Id
    public:: DimensionsNumber, AttributesNumber, operator(.error.)

    interface Variable
	module procedure NetcdfNewVariable
	module procedure NetcdfNewVariableByDims
	module procedure NetcdfFindVariable
	module procedure NetcdfFindVariableId
    end interface

    interface Fullname
	module procedure NetcdfVariableFullname
    end interface

    interface Name
	module procedure NetcdfVariableName
    end interface

    interface Type
	module procedure NetcdfVariableType
    end interface

    interface Dimensions
	module procedure NetcdfVariableDims
    end interface

    interface DimensionsNumber
	module procedure NetcdfVariableNDims
    end interface

    interface AttributesNumber
	module procedure NetcdfVariableNAtts
    end interface

    interface operator(.error.)
	module procedure NetcdfVariableError
    end interface

    interface Id
	module procedure NetcdfVariableId
    end interface

contains

    type(NC_VARIABLE) function NC_VARIABLE_ERROR() result(result)
	result = NC_VARIABLE(NC_FILE_ERROR, -1)
    end function

    !
    ! --- ファイルに変数を作成 ---
    !

    function NetcdfNewVariable(file, name, type, dims) result(result)
	use netcdf_filename
	type(NC_VARIABLE)::			result
	type(NC_FILE), intent(inout)::		file
	character(len=*), intent(in)::		name
	integer, intent(in)::			type
	integer, intent(in), optional::		dims(:)
	integer::				status, varid
    continue
	result = NC_VARIABLE_ERROR()
	call DefineMode(file)
	if (present(dims)) then
	    status = nf_def_var(Id(file), name, type, size(dims), &
		& dims, varid)
	else
	    status = nf_def_var(Id(file), name, type, 0, (/0/), varid)
	endif
	if (status == NF_NOERR) then
	    result = NC_VARIABLE(file, varid)
	else
	    call NetcdfSaveError(status, 'NetcdfNewVariable', &
		& Filename(file))
	endif
    end function

    function NetcdfNewVariableByDims(file, name, type, dims) result(result)
	use netcdf_filename
	type(NC_VARIABLE)::			result
	type(NC_FILE), intent(inout)::		file
	character(len=*), intent(in)::		name
	type(NC_DIMENSION), intent(in)::	dims(:)
	integer, intent(in)::			type
	integer, allocatable::			dimids(:)
    continue
	allocate(dimids(size(dims)))
	dimids = dims(:)%id
	result = Variable(file, name, type, dimids)
	deallocate(dimids)
    end function

    !
    ! --- ファイルから変数関係情報の取得 ---
    !

    function NetcdfFindVariable(file, name) result(result)
	use netcdf_filename
	type(NC_VARIABLE)::			result
	type(NC_FILE), intent(in)::		file
	character(len=*), intent(in)::		name
	integer::				status
    continue
	status = nf_inq_varid(Id(file), name, result%id)
	if (status /= NF_NOERR) then
	    call NetcdfSaveError(status, &
		& 'NetcdfFindVariable', Filename(file))
	    result = NC_VARIABLE_ERROR()
	    return
	endif
	result%file = file
    end function

    function NetcdfFindVariableId(file, id) result(result)
	type(NC_VARIABLE)::			result
	type(NC_FILE), intent(in)::		file
	integer, intent(in)::			id
    continue
	if (id > VariablesNumber(file)) then
	    result = NC_VARIABLE_ERROR();  return
	endif
	result%id = id
	result%file = file
    end function

    !
    ! --- 変数の情報取得 ---
    !

    logical function NetcdfVariableError(var) result(result)
    	type(NC_VARIABLE), intent(in)::	var
    continue
    	result = (var%id < 0)
    end function

    integer function NetcdfVariableId(var) result(result)
    	type(NC_VARIABLE), intent(in)::	var
    continue
    	result = var%id
    end function

    !
    ! --- 変数の利用 ---
    !

    ! 変数に対応する次元のリストを NC_DIMENSION 構造体の配列ポインタで返す.
    ! 配列の長さは自動的に決定される。
    ! 時限を持たない変数に対して呼び出した場合は空ポインタを返す。
    ! 呼び出し元は返却値を開放しなければならない。
    !
    function NetcdfVariableDims(var) result(result)
	type(NC_DIMENSION), pointer:: result(:)
	type(NC_VARIABLE), intent(in)::	var
	character(len=NF_MAX_NAME):: name
	integer:: status, n_dims
	integer:: n_atts, type
	integer:: dimids(NF_MAX_VAR_DIMS)
    continue
	status = nf_inq_var(Id(var%file), var%id, &
	    & name, type, n_dims, dimids, n_atts)
	if (status /= NF_NOERR) then
	    call NetcdfSaveError(status, 'NetcdfVariableDims', &
		& var%id)
	    nullify(result)
	    return
	endif
	if (n_dims < 0) then
	    nullify(result)
	    return
	endif
	allocate(result(1: n_dims))
	result%file = var%file
	result(:)%id = dimids(1: n_dims)
    end function

    integer function NetcdfVariableNAtts(var) result(result)
	type(NC_VARIABLE), intent(in)::	var
	integer::			status
    continue
	status = nf_inq_varnatts(Id(var%file), var%id, result)
	call NetcdfSaveError(status, 'NetcdfVariableNAtts', var%id)
	if (status /= NF_NOERR) result = -1
    end function

    integer function NetcdfVariableNDims(var) result(result)
	type(NC_VARIABLE), intent(in)::	var
	integer::			status
    continue
	status = nf_inq_varndims(Id(var%file), var%id, result)
	call NetcdfSaveError(status, 'NetcdfVariableNDims', var%id)
	if (status /= NF_NOERR) result = -1
    end function

    integer function NetcdfVariableType(var) result(result)
	type(NC_VARIABLE), intent(in)::	var
	integer::			status
    continue
	status = nf_inq_vartype(Id(var%file), var%id, result)
	call NetcdfSaveError(status, 'NetcdfVariableType', var%id)
	if (status /= NF_NOERR) result = 0
    end function

    function NetcdfVariableName(var) result(result)
	type(VARYING_STRING)::			result
	type(NC_VARIABLE), intent(in)::		var
	character(len=NF_MAX_NAME)::		buffer
	integer:: 				status
    continue
	status = nf_inq_varname(Id(var%file), var%id, buffer)
	if (status /= NF_NOERR) then
	    call NetcdfSaveError(status, 'NetcdfVariableName', &
		& var%id)
	    result = ' (error)'
	endif
	result = trim(buffer)
    end function

    function NetcdfVariableFullname(var) result(result)
	type(VARYING_STRING)::			result
	type(NC_VARIABLE), intent(in)::		var
    continue
	result = Filename(var%file) // '#' // Name(var)
    end function

end module
