! netcdf_file.f90 - object-oriented netCDF file interface (file)
! vi: set sw=4:
! Copyright (C) TOYODA Eizi, 2000.  All rights reserved.
!
! 2000-01-10 豊田英司	初版
! 2000-01-13 豊田英司	型名の整理
! 2000-01-15 豊田英司	スタイルを変える
! 2000-01-17 豊田英司	モードごとの型を NC_FILE に統合
! 2000-02-22 豊田英司	可変長文字列採用
! 2000-03-18 豊田英司	CVS 管理を開始

module netcdf_file

    use netcdf_v3
    use netcdf_error
    use iso_varying_string
    implicit none
    private

    character(len=*), parameter:: RCSID = &
	& "$Id: netcdf_file.f90,v 1.11 2000/05/31 10:22:05 eizi Exp $"

    public:: NC_FILE, NC_FILE_ERROR

    type NC_FILE
	integer::		id
	logical::		writable
	logical::		defmode
    end type

    type(NC_FILE), parameter:: NC_FILE_CLOSED = NC_FILE(0, .FALSE., .FALSE.)
    type(NC_FILE), parameter:: NC_FILE_ERROR = NC_FILE(-1, .FALSE., .FALSE.)

    !
    ! --- 作る、開く、閉じる ---
    !

    public:: Create, Open, Close

    interface Create;  module procedure NetcdfCreateFile;  end interface
    interface Close;  module procedure NetcdfCloseFile;  end interface
    interface Open
	module procedure NetcdfOpenFile
	module procedure NetcdfReopenFile
    end interface

    !
    ! --- モード変更 ---
    ! 

    public:: DefineMode, Datamode

    interface DefineMode
	module procedure NetcdfFileSetDefineMode
    end interface

    interface DataMode
	module procedure NetcdfFileSetDataMode
    end interface

    !
    ! --- 情報の取得 ---
    !

    public:: Id, Name, Filename, VariablesNumber, operator(==)
    public:: operator(.error.), operator(.writable.)

    interface Id;  module procedure NetcdfFileId;  end interface

    interface Name;  module procedure NetcdfFileFilename;  end interface
    interface Filename;  module procedure NetcdfFileFilename;  end interface

    interface VariablesNumber
	module procedure NetcdfFileNVars
    end interface

    interface operator(==)
	module procedure NetcdfFileEquiv
    end interface

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

    interface operator(.writable.)
	module procedure NetcdfFileIsWritable
    end interface

contains

    ! エラーチェック
    !
    logical function NetcdfFileError(file)
    	type(NC_FILE), intent(in)::	file
    continue
	NetcdfFileError = (file%id < 0)
    end function

    !
    ! --- 作成する/開く/閉じる ---
    !

    subroutine NetcdfCreateFile(file, path, overwrite, fail)
	use netcdf_filename
	type(NC_FILE), intent(out):: file
	character(len=*), intent(in):: path
	logical, intent(in), optional:: overwrite
	logical, intent(out), optional:: fail
	integer:: status, mode
    continue
	mode = NF_CLOBBER
	if (present(overwrite)) then
	    if (.not. overwrite) mode = NF_NOCLOBBER
	endif
	status = nf_create(path, mode, file%id)
	if (status == NF_NOERR) then
	    file%defmode = .TRUE.
	    file%writable = .TRUE.
	    call StoreFilename(file%id, path, writable=.TRUE.)
	else
	    call NetcdfSaveError(status, 'NetcdfCreateNewFile', path)
	    file = NC_FILE_ERROR
	endif
	if (present(fail)) then
	    fail = (status /= NF_NOERR)
	else
	    call NetcdfAssert()
	endif
    end subroutine

    subroutine NetcdfOpenFile(file, path, writable, fail)
	use netcdf_filename
	type(NC_FILE), intent(out):: file
	character(len=*), intent(in):: path
	logical, optional, intent(in):: writable
	logical, optional, intent(out):: fail
	integer:: mode, status, sought_id
    continue
	file%defmode = .FALSE.
	file%writable = .FALSE.
	if (present(writable)) then
	    if (writable) file%writable = writable
	endif
	mode = NF_NOWRITE
	if (file%writable) mode = ior(mode, NF_WRITE)
	file%id = IdByFilename(var_str(path))
	if (file%id > 0) then
	    if (CompatibleMode(file%id, file%writable)) then
		call StoreFilename(file%id, path, file%writable)
		return
	    endif
	endif
	status = nf_open(path, mode, file%id)
	if (status == NF_NOERR) then
	    call StoreFilename(Id(file), path, writable=file%writable)
	else
	    call NetcdfSaveError(status, 'NetcdfOpenFile', path)
	    file = NC_FILE_ERROR
	endif
	if (present(fail)) then
	    fail = (status /= NF_NOERR)
	else
	    call NetcdfAssert()
	endif
    end subroutine

    subroutine NetcdfReopenFile(file, oldfile)
	use netcdf_filename
	type(NC_FILE), intent(inout):: file
	type(NC_FILE), intent(in):: oldfile
    continue
	file = oldfile
	call StoreFilename(Id(file), char(Name(file)), (.writable. file))
    end subroutine

    subroutine NetcdfCloseFile(file, fail)
	use netcdf_filename
	type(NC_FILE), intent(inout):: file
	logical, intent(out), optional:: fail
	type(VARYING_STRING):: file_name
	integer:: status
	logical:: to_close
    continue
	file_name = Name(file)
	call DisposeFilename(file%id, end=to_close)
	if (.not. to_close) then
	    if (present(fail)) fail = .FALSE.
	    file = NC_FILE_CLOSED
	    return
	endif
	call DataMode(file)
	status = nf_close(Id(file))
	call NetcdfSaveError(status, 'NetcdfCloseFile', file_name)
	if (present(fail)) then
	    fail = (status /= NF_NOERR)
	else
	    call NetcdfAssert()
	endif
	file = NC_FILE_CLOSED
    end subroutine

    !
    ! --- モード変更 ---
    !

    subroutine NetcdfFileSetDefineMode(file, fail)
	type(NC_FILE), intent(inout):: file
	logical, intent(out), optional:: fail
	integer:: stat
    continue
!	if (file%defmode) then
!	    if (present(fail)) fail = .FALSE.
!	    call NetcdfSaveError(NF_EINDEFINE, 'DefineMode', Name(file))
!	    return
!	endif
	stat = nf_redef(Id(file))
	if (stat == NF_NOERR .or. stat == NF_EINDEFINE) then
	    file%defmode = .TRUE.
	    if (present(fail)) fail = .FALSE.
	else
	    call NetcdfSaveError(stat, 'DefineMode', Name(file))
	    if (present(fail)) fail = .TRUE.
	    call NetcdfAssert()
	endif
    end subroutine

    subroutine NetcdfFileSetDataMode(file, fail)
	type(NC_FILE), intent(inout):: file
	logical, intent(out), optional:: fail
	integer:: stat
    continue
!	if (.not. file%defmode) then
!	    if (present(fail)) fail = .FALSE.
!	    call NetcdfSaveError(NF_ENOTINDEFINE, 'DataMode', Name(file))
!	    return
!	endif
	stat = nf_enddef(Id(file)) 
	if (stat == NF_NOERR .or. stat == NF_ENOTINDEFINE) then
	    file%defmode = .FALSE.
	    if (present(fail)) fail = .FALSE.
	else
	    call NetcdfSaveError(stat, 'DataMode', Name(file))
	    if (present(fail)) fail = .TRUE.
	    call NetcdfAssert()
	endif
    end subroutine

    !
    ! --- ファイルに関する情報 ---
    !

    ! ID の取得
    !
    integer function NetcdfFileId(file) result(result)
	type(NC_FILE), intent(in)::	file
    continue
	result = file%id
    end function

    logical function NetcdfFileIsWritable(file) result(result)
	type(NC_FILE), intent(in):: file
    continue
	result = file%writable
    end function

    ! 変数の数の取得
    !
    integer function NetcdfFileNVars(file) result(result)
	type(NC_FILE), intent(in)::	file
	integer::			status
    continue
	status = nf_inq_nvars(Id(file), result)
	call NetcdfSaveError(status, 'NetcdfFileNVars', file%id)
	if (status /= NF_NOERR) result = -1
    end function

    ! ファイル名の取得
    !
    function NetcdfFileFilename(file) result(result)
	use netcdf_filename
	type(VARYING_STRING)::		result
    	type(NC_FILE), intent(in)::	file
    continue
    	result = FilenameById(file%id)
    end function

    ! 同値判定
    !
    logical function NetcdfFileEquiv(lhs_file, rhs_file) result(result)
    	type(NC_FILE), intent(in)::	lhs_file, rhs_file
    continue
	result = (lhs_file%id == rhs_file%id)
    end function


end module
