!-- gdCommon.f90
!-- 4 Mar 92, Richard Maine: Version 1.0.

module gd_common

  !-- Utility module for use in gd_read.
  !-- Provides functionality common to several gd_read submodules.
  !-- Routines, data, and types to manage the list of signals
  !-- and data sources.
  !-- 19 Oct 91, Richard Maine.

  use precision
  use sysdep_io
  use string
  use gd_tree

  implicit none
  private

  integer, public, parameter :: signal_name_len = 16

  integer, public, parameter :: no_source_code = 0
  integer, public, parameter :: file_source_code = 1
  integer, public, parameter :: filter_source_code = 2
  integer, public, parameter :: calc_source_code = 3

  !-- gd_source_type has generic information about a data source.
  !-- This is accessible outside of gd_read.
  type, public :: gd_source_type
    integer :: source_code
         !-- An integer code for the source type.
    integer :: source_number
         !-- The source number.  A single numbering sequence is
         !-- currently shared by all source codes.
    character*(file_name_len) :: source_name
         !-- Source name.  Often a file name.
    character*80 :: source_description
         !-- One-line description of the source.
    real(r_kind) :: source_dt
         !-- Nominal source sample interval.  0 if unknown or undefined.
         !-- Value for files reflects any file thinning.
    integer :: n_source_signals
         !-- Number of signals available from this source.
         !-- Note there may be more signals defined than are available.
         !-- This total includes only the available signals.
    integer :: source_signal_offset
         !-- Offset in the gd signal list of the signals from this source.
  end type

  !-- gd_signal_type has information about a get_data signal.
  type, public :: gd_signal_type
    integer :: signal_number  !-- Signal number in the get_data list.
    character*(signal_name_len) :: signal_name
         !-- Unique signal name.
    integer :: source_number
         !-- The source number for this signal.
         !-- For filter signals, this is the number of the associated file,
         !-- because that's what we always seem to need.
         !-- If we ever need the filter source number as distinct from
         !-- the file source number, we'll need a change.
    integer :: source_code
         !-- An integer code for the source type.
    integer :: source_signal_number
         !-- Index in the list of signals from this source.
         !-- For filter signals, refers to the associated file signal list,
         !-- rather than the filter signal list.
    real(r_kind) :: skew, adjusted_skew
         !-- Time skew for this signal.  Irrelevant for calc signals.
         !-- For hold-last-value signals, the adjusted skew includes
         !-- both the given skew plus an allowance for the time tolerance;
         !-- for other signals it is the same as the skew.
    logical :: interpolate
         !-- False means hold last value.  Irrelevant for calc signals.
    logical :: used
         !-- Is this signal used?
    logical :: synced
         !-- Does this signal need to be synced?
         !-- A filter input may be used without needing to be synced.
    real(r_kind) :: data
         !-- Signal data value.
  end type

  type signal_ptr_type
    type(gd_signal_type), pointer :: gd_signal
  end type

  type signal_list_node_type
    type(gd_signal_type) :: signal
    type(signal_list_node_type), pointer :: prev_node
  end type

  type, public :: gd_signal_list_type
    private
    integer :: n_signals
    type(tree_type) :: tree
    type(signal_list_node_type), pointer :: last_node
  end type

  type gd_source_list_node_type
    type(gd_source_type) :: source
    type(gd_source_list_node_type), pointer :: prev_node
  end type

  type, public :: gd_source_list_type
    private
    integer :: n_sources
    type(gd_source_list_node_type), pointer :: last_node
  end type

  !-- This dummy entry is returned when searches fail.
  type(gd_signal_type), target, save :: no_signal

  !-- Public procedures.
  public init_signal_list, release_signal_list, add_gd_signal
  public find_gd_signal, get_signal_array, get_signal_names, signal_list_size
  public init_source_list, release_source_list
  public add_gd_source, get_source_array, source_list_size

contains

  subroutine init_signal_list (signal_list)

    !-- Initialize an empty signal list.
    !-- 19 Oct 91, Richard Maine.

    !-------------------- interface.
    type(gd_signal_list_type), intent(out) :: signal_list

    !-------------------- executable code.

    no_signal%signal_number = 0
    no_signal%signal_name = ''
    no_signal%source_number = 0
    no_signal%source_code = no_source_code
    no_signal%source_signal_number = 0
    no_signal%skew = 0.
    no_signal%adjusted_skew = 0.
    no_signal%interpolate = .false.
    no_signal%used = .false.
    no_signal%synced = .false.
    no_signal%data = 0.

    signal_list%n_signals = 0
    call initialize_tree(signal_list%tree)
    nullify(signal_list%last_node)
    return
  end subroutine init_signal_list

  subroutine release_signal_list (signal_list)

    !-- Release space in the signal list.
    !-- 19 Oct 91, Richard Maine.

    !-------------------- interface.
    type(gd_signal_list_type), intent(inout) :: signal_list

    !-------------------- local.
    type(signal_list_node_type), pointer :: node

    !-------------------- executable code.

    do while(associated(signal_list%last_node))
      node => signal_list%last_node
      signal_list%last_node => node%prev_node
      deallocate(node)
    end do
    call deallocate_tree(signal_list%tree)
    return
  end subroutine release_signal_list

  function signal_list_size (signal_list)

    !-- Return the number of signals in a list.
    !-- 1 Sep 92, Richard Maine.

    !-------------------- interface.
    type(gd_signal_list_type), intent(in) :: signal_list
         !-- The list of get_data signal records.
    integer :: signal_list_size  !-- Number of signals in the list.

    !-------------------- executable code.

    signal_list_size = signal_list%n_signals
    return
  end function signal_list_size

  subroutine add_gd_signal (signal_list, signal_name, source, gd_signal, &
    source_name, error)

    !-- Add a new name to the get_data signal list.
    !-- Modify the name to be unique if needed.
    !-- If this routine fails (presumably by running out of memory)
    !-- then the signal list will likely be left with spurious
    !-- nodes for the preceding signals of the same source.
    !-- We probably ought to either figure out how to delete the
    !-- spurious nodes or declare the gd_read connection unuseable.
    !-- However, odds are that things will bomb later in any case
    !-- if we are that short on memory.
    !-- 19 Oct 91, Richard Maine.

    !-------------------- interface.
    type(gd_signal_list_type), intent(inout) :: signal_list
         !-- The list of get_data signal records.
    character*(*), intent(in) :: signal_name
         !-- Name of the new signal to be added.
    type(gd_source_type), intent(inout) :: source
         !-- Source record for the source of this signal.
    type(gd_signal_type), pointer :: gd_signal
         !-- Record of information about the signal.  Intent(out)
         !-- Default values will be filled in on exit.
    character*(*), intent(in) :: source_name
         !-- Name of the source.  Used in messages.
    logical, intent(out) :: error

    !-------------------- local.
    logical :: dup
    integer :: number, iostat
    character, parameter :: sharp = '#'
    character :: new_name*(signal_name_len)
    integer, pointer :: int_array(:)
    type(signal_ptr_type) :: signal_ptr
    type(signal_list_node_type), pointer :: signal_list_node

    !-------------------- executable code.

    nullify(signal_list_node)
    allocate(signal_list_node, stat=iostat)
    if (iostat /= 0) goto 8000

    gd_signal => signal_list_node%signal
    signal_ptr%gd_signal => gd_signal

    !-- Try to add the name as is (unless it is blank).
    dup = signal_name == ''
    if (.not. dup) then
      call add_tree_data(signal_name, transfer(signal_ptr,int_array), &
           signal_list%tree, dup, error)
      if (error) goto 8000
    end if

    !-- Generate a new name to avoid duplication.
    new_name = signal_name
    number = 1
    loop: do while(dup)
      number = number + 1
      new_name = sharp // trim(adjustl(int_string(number))) // sharp // &
           signal_name
      call add_tree_data(new_name, transfer(signal_ptr,int_array), &
           signal_list%tree, dup, error)
      if (error) goto 8000
      if (.not. dup) call write_error_msg('Duplicate name ' // signal_name // &
         ' from ' // source_name // ' changed to ' // new_name)
    end do loop

    !-- Add to linked list of signals and to source signal count.
    if (source%n_source_signals == 0) &
         source%source_signal_offset = signal_list%n_signals
    source%n_source_signals = source%n_source_signals + 1
    signal_list%n_signals = signal_list%n_signals + 1
    signal_list_node%prev_node => signal_list%last_node
    signal_list%last_node => signal_list_node

    gd_signal%signal_name = new_name
    gd_signal%signal_number = signal_list%n_signals
    gd_signal%source_number = source%source_number
    gd_signal%source_code = source%source_code
    gd_signal%source_signal_number = source%n_source_signals
    gd_signal%skew = 0.
    gd_signal%adjusted_skew = 0.
    gd_signal%interpolate = .false.
    gd_signal%used = .false.
    gd_signal%synced = .false.
    gd_signal%data = 0.
    error = .false.
    return

    !-- Error exit.
    8000 continue
    if (associated(signal_list_node)) deallocate(signal_list_node)
    error = .true.
    return
  end subroutine add_gd_signal

  subroutine find_gd_signal (signal_name, signal_list, gd_signal)

    !-- Find a get_data signal record by name.
    !-- 22 Oct 91, Richard Maine.

    !-------------------- interface.
    character*(*), intent(in) :: signal_name  !-- name of the signal sought.
    type(gd_signal_list_type), intent(in) :: signal_list
         !-- List of signal records to search.
    type(gd_signal_type), pointer :: gd_signal
         !-- Record of data about the specified signal.
         !-- If not found, returns pointer to dummy no_signal record.

    !-------------------- local.
    integer, pointer :: int_array(:)
    type(signal_ptr_type) :: signal_ptr

    !-------------------- executable code.

    call get_tree_data(signal_name, signal_list%tree, int_array)
    if (associated(int_array)) then
      signal_ptr = transfer(int_array, signal_ptr)
      gd_signal => signal_ptr%gd_signal
    else
      gd_signal => no_signal
    end if
    return
  end subroutine find_gd_signal

  subroutine get_signal_array (signal_list, signal)

    !-- Get the get_data signal records in an array.
    !-- 22 Oct 91, Richard Maine.

    !-------------------- interface.
    type(gd_signal_list_type), intent(in) :: signal_list
         !-- List of get_data signal records.
    type(gd_signal_type), intent(out) :: signal(0:)

    !-------------------- local.
    type(signal_list_node_type), pointer :: node

    !-------------------- executable code.

    node => signal_list%last_node
    do while (associated(node))
      signal(node%signal%signal_number) = node%signal
      node => node%prev_node
    end do
    signal(0) = no_signal
    return
  end subroutine get_signal_array

  subroutine get_signal_names (signal_list, signal_names)

    !-- Get the get_data signal names in a vector.
    !-- 22 Oct 91, Richard Maine.

    !-------------------- interface.
    type(gd_signal_list_type), intent(in) :: signal_list
         !-- List of get_data signal records.
    character*(*), intent(out) :: signal_names(:)

    !-------------------- local.
    type(signal_list_node_type), pointer :: node

    !-------------------- executable code.

    node => signal_list%last_node
    do while (associated(node))
      signal_names(node%signal%signal_number) = node%signal%signal_name
      node => node%prev_node
    end do
    return
  end subroutine get_signal_names

  subroutine init_source_list (source_list)

    !-- Initialize an empty source list.
    !-- 17 Sept 92, Richard Maine.

    !-------------------- interface.
    type(gd_source_list_type), intent(out) :: source_list

    !-------------------- executable code.

    source_list%n_sources = 0
    nullify(source_list%last_node)
    return
  end subroutine init_source_list

  subroutine release_source_list (source_list)

    !-- Release space in the source list.
    !-- 17 Sept 92, Richard Maine.

    !-------------------- interface.
    type(gd_source_list_type), intent(inout) :: source_list

    !-------------------- local.
    type(gd_source_list_node_type), pointer :: node

    !-------------------- executable code.

    do while(associated(source_list%last_node))
      node => source_list%last_node
      source_list%last_node => node%prev_node
      deallocate(node)
    end do
    return
  end subroutine release_source_list

  function source_list_size (source_list)

    !-- Return the number of sources in a list.
    !-- 1 Sep 92, Richard Maine.

    !-------------------- interface.
    type(gd_source_list_type), intent(in) :: source_list
         !-- The list of get_data source records.
    integer :: source_list_size  !-- Number of sources in the list.

    !-------------------- executable code.

    source_list_size = source_list%n_sources
    return
  end function source_list_size

  subroutine add_gd_source (source_list, source_code, source_name, &
       source_description, source_dt, source, error)

    !-- Add a new entry to the get_data source list.
    !-- 17 Sept 92, Richard Maine.

    !-------------------- interface.
    type(gd_source_list_type), intent(inout) :: source_list
         !-- The list of get_data sources.
    integer, intent(in) :: source_code
         !-- Source code value for this source.
    character*(*), intent(in) :: source_name
         !-- Name of the source.
    character*(*), intent(in) :: source_description
         !-- One-line description of the source.
    real(r_kind), intent(in) :: source_dt
         !-- Nominal sample interval of the source.  (0 if not applicable).
    type(gd_source_type), pointer :: source
         !-- Record of information about the source.  Intent(out)
         !-- Default values will be filled in on exit.
    logical, intent(out) :: error

    !-------------------- local.
    integer :: iostat
    type(gd_source_list_node_type), pointer :: source_list_node

    !-------------------- executable code.

    nullify(source_list_node)
    allocate(source_list_node, stat=iostat)
    if (iostat /= 0) goto 8000

    source => source_list_node%source

    !-- Add to linked list.
    source_list_node%prev_node => source_list%last_node
    source_list%last_node => source_list_node

    source_list%n_sources = source_list%n_sources + 1

    source%source_code = source_code
    source%source_number = source_list%n_sources
    source%source_name = source_name
    source%source_description = source_description
    source%source_dt = source_dt
    source%n_source_signals = 0
    source%source_signal_offset = 0

    error = .false.
    return

    !-- Error exit.
    8000 continue
    if (associated(source_list_node)) deallocate(source_list_node)
    error = .true.
    return
  end subroutine add_gd_source

  subroutine get_source_array (source_list, source)

    !-- Get the get_data source records in an array.
    !-- 22 Oct 91, Richard Maine.

    !-------------------- interface.
    type(gd_source_list_type), intent(in) :: source_list
         !-- List of get_data source records.
    type(gd_source_type), intent(out) :: source(:)

    !-------------------- local.
    type(gd_source_list_node_type), pointer :: node

    !-------------------- executable code.

    node => source_list%last_node
    do while (associated(node))
      source(node%source%source_number) = node%source
      node => node%prev_node
    end do
    return
  end subroutine get_source_array

end module gd_common
