! crutsfiles.f90
! module in which standard save to and load from CRU ts file routines are held
! permitted: .cts .hdr .src .nrm (anything permitted for LoadCTS only)
! contains: LoadCTS,SaveCTS,MakeStnInfo,CombineStn
! data missing values: it is very important that all missing values in the files be set to -9999
! converted to use on beo1 with Portland Group compiler 

module CRUtsFiles

use FileNames

implicit none

contains

!*******************************************************************************
! all the arrays are alloc within the routine
! StnLocal added 1.5.02 = a9 local stn identifier field that prev was in StnInfo(*,8)
! Source (legacy) added 1.5.02 for master code files: NOT with Hulme & NmlData,requires HeadOnly
! SrcCode,SrcSuffix,SrcDate added 24.10.02 for revised master code files
! if Extra is set, the NStn is expanded by the size of Extra, and arrays filled with MVs
! if PhilJ is set, 2 more metadata are used (code for homog, 1st usable year),requires HeadOnly etc
! assume LatMissVal=-9999 ; LonMissVal=-99999 ; ElvMissVal=-999 ; DataMissVal=-9999
!	to change set LatMV,LonMV,ElvMV,DataMV
! assume Lat*100,Lon*100,Elv*1 ; to change set LatF,LonF,ElvF
! Legacy, when set, allows loading of old format for headers
! NmlData may be accompanied by NmlYr0, NmlYr1, NmlSrc, NmlInc, and most other options,
!   but NOT Data, YearAD, Hulme, Legacy, HeadOnly, Source
! DtbNormals (or .dtb) implies a database file with a normals line below the metadata line

subroutine LoadCTS (StnInfo,StnLocal,StnName,StnCty,Code,Lat,Lon,Elv,OldCode,Data,YearAD,&
        NmlData,DtbNormals,CallFile,Hulme,Legacy,HeadOnly,HeadForm,LongType,Silent,Extra,PhilJ, &
        YearADMin,YearADMax,Source,SrcCode,SrcSuffix,SrcDate, &
        LatMV,LonMV,ElvMV,DataMV,LatF,LonF,ElvF,NmlYr0,NmlYr1,NmlSrc,NmlInc)

real, pointer, dimension (:), optional		:: Lat,Lon,Elv	! additional vec form

integer, pointer, dimension (:,:,:), optional	:: Data		! data integers (not if HeadOnly)
integer, pointer, dimension (:,:), optional	:: NmlData	! data integers (DataMV okay)
integer, pointer, dimension (:,:), optional	:: DtbNormals	! data integers (DataMV okay)
integer, pointer, dimension (:,:), optional	:: Hulme	! annual column in Hulme only
integer, pointer, dimension (:,:)		:: StnInfo	! station key, stored as integers
integer, pointer, dimension (:), optional	:: Code		! additional vec form
integer, pointer, dimension (:), optional	:: OldCode	! additional vec form
integer, pointer, dimension (:), optional	:: YearAD	! not if HeadOnly or NmlData
integer, pointer, dimension (:), optional	:: NmlYr0, NmlYr1 ! normals period (StnN)
integer, pointer, dimension (:), optional	:: NmlSrc, NmlInc ! normals source code, % present
integer, pointer, dimension (:), optional	:: SrcCode,SrcDate ! source info

character (len=20), pointer, dimension (:), optional	:: Source
character (len=20), pointer, dimension (:)		:: StnName
character (len=13), pointer, dimension (:)		:: StnCty
character (len=09), pointer, dimension (:)		:: StnLocal
character (len=04), pointer, dimension (:), optional	:: SrcSuffix

integer, intent(in), optional 	:: LatMV,LonMV,ElvMV,DataMV	! raw missing values
integer, intent(in), optional 	:: LatF,LonF,ElvF		! Factors
integer, intent(in), optional	:: HeadOnly	! if set, load headers-only file
integer, intent(in), optional	:: LongType	! 2=reverse-sign,3=180-360-->-180-0
integer, intent(in), optional 	:: Legacy	! if set, load old header format	 
integer, intent(in), optional 	:: Silent	 
integer, intent(in), optional 	:: Extra	 
integer, intent(in), optional 	:: PhilJ	 
integer, intent(in), optional 	:: YearADMin,YearADMax	 ! ensure YearAD covers this range

character (len=80), intent(in), optional	:: CallFile	! filepath to load (optional)
character (len=80), intent(in), optional	:: HeadForm	! format of the headers

real, parameter :: MissVal = -999.0

integer, parameter :: DataMissVal = -9999

integer :: LatFactor,LonFactor,ElvFactor, Year0,Year1,SuffixBeg
integer :: ReadStatus,AllocStat
integer :: FileStnN, StnN, MonthN, YearN, LineN, InfoN
integer :: XFileStn, XStn, XMonth, XYear, XLine, XInfo, XYearAD
integer :: HWmo,HLat,HLong,HElev,HYear,HYear0,HYear1,HGcm,HSrc,HInc,HHomog,HFirst
integer :: QDatabase
integer :: LatMissVal,LonMissVal,ElvMissVal,RawMissVal

character (len=80) :: GivenFile, LoadFile, HeaderFormat,DataFormat,Trash
character (len=80) :: ReadStnName, ReadCtyName, ReadStnLocal, HSource
character (len= 9) :: HLocal
character (len= 4) :: Suffix
character (len= 1) :: Skip

!*******************************************************************************

if (present(Hulme).AND.present(HeadOnly)) print*, &
		"  > ##### ERROR: LoadCRUts: Call Clash: Hulme, HeadOnly #####"
if (present(NmlData)) then
  if (present(Data).OR.present(YearAD).OR.present(Hulme).OR.present(Legacy).OR. &
  		present(HeadOnly).OR.present(Source)) &
  		print*, "  > ##### ERROR: LoadCRUts: Call Clash: NmlData present #####"
else
  if (present(NmlYr0).OR.present(NmlYr1).OR.present(NmlSrc).OR.present(NmlInc)) &
  		print*, "  > ##### ERROR: LoadCRUts: Call Clash: NmlData missing #####"
end if

if (present(PhilJ).AND.(present(NmlData).OR.present(Source).OR.present(SrcCode).OR.present(Hulme))) &
  		print*, "  > ##### ERROR: LoadCRUts: Call Clash: PhilJ present #####"

QDatabase = 0
if (present(DtbNormals)) QDatabase = 1

MonthN = 12

LatMissVal=-9999 ; LonMissVal=-99999 ; ElvMissVal=-999		! usual/hulme hdr miss vals
if (present(Legacy)) then
  LatMissVal=-999 ; LonMissVal=-9999 ; ElvMissVal=-999		! legacy hdr miss vals
end if
if (present(LatMV)) LatMissVal = LatMV				! custom hdr miss vals
if (present(LonMV)) LonMissVal = LonMV
if (present(ElvMV)) ElvMissVal = ElvMV

RawMissVal = -9999
if (present(Hulme))  RawMissVal = -10
if (present(DataMV)) RawMissVal = DataMV

LatFactor=100 ; LonFactor=100 ; ElvFactor=1			! usual/hulme hdr factors
if (present(Legacy)) then
  LatFactor=10 ; LonFactor=10 ; ElvFactor=1			! legacy hdr factors
end if
if (present(LatF)) LatFactor = LatF				! custom hdr factors
if (present(LonF)) LonFactor = LonF
if (present(ElvF)) ElvFactor = ElvF

DataFormat="(i4,12i5)"						! standard data format
if (present(Hulme)) DataFormat="(i4,12i5,i6)"			! hulme data format

if (present(HeadForm)) then
  HeaderFormat = trim(HeadForm)
  Suffix = "    "
else if (present(NmlData)) then
  HeaderFormat = "(i7,i6,i7,i5,x,a20,x,a13,x,i4,x,i4,i7,a9,x,i2,x,i3)"
  Suffix = ".nrm"
else if (present(Hulme)) then
  HeaderFormat = "(i7,i5,i6,i5,a20,a13,i4,i4,i7,a9)"
  Suffix = "    "
else if (present(Legacy)) then
  HeaderFormat = "(i7,i5,i6,i5,x,a20,x,a13,x,i4,x,i4,i7,a9)"
  Suffix = ".cts"
else
  if (present(Source)) then
    HeaderFormat = "(i7,i6,i7,i5,x,a20,x,a13,x,i4,x,i4,i7,a9,x,a20)"
  else if (present(SrcCode)) then
    HeaderFormat = "(i7,i6,i7,i5,x,a20,x,a13,x,i4,x,i4,i7,a9,x,i4,x,a4,x,i10)"
  else
    HeaderFormat = "(i7,i6,i7,i5,x,a20,x,a13,x,i4,x,i4,i7,a9)"
  end if
  
  if (present(HeadOnly)) then
   Suffix = ".hdr"
  else
   if (present(CallFile)) then
    SuffixBeg = index(CallFile,".",.TRUE.)
    if      (CallFile(SuffixBeg:(SuffixBeg+3)).EQ.".src") then
      Suffix = ".src"
    else if (CallFile(SuffixBeg:(SuffixBeg+3)).EQ.".dtb") then
      Suffix = ".dtb" ; QDatabase = 1
    else if (CallFile(SuffixBeg:(SuffixBeg+3)).EQ.".dts") then
      Suffix = ".dts" ; QDatabase = 1
    else
      Suffix = ".cts"
    end if
   else
    Suffix = ".cts"
   end if
  end if
end if

GivenFile = ""								! file to load
if (present(CallFile)) GivenFile = CallFile
if (GivenFile.EQ."") then
  print "(2a)", "  > Enter the file to load with suffix: ", Suffix  
  do
	read (*,*,iostat=ReadStatus), GivenFile
	if (ReadStatus.LE.0.AND.GivenFile.NE."") exit
  end do
end if
LoadFile = LoadPath (GivenFile,Suffix)

call system ('wc -l ' // LoadFile // ' > trashme-loadcts.txt')		! get number of lines
open  (3,file='trashme-loadcts.txt',status="old",access="sequential",form="formatted",action="read")
read  (3,"(i8)"), LineN
close (3)
call system ('rm trashme-loadcts.txt')

if (present(HeadOnly)) then
 StnN = LineN
else if (present(NmlData)) then
 StnN = LineN / 2
else    
 open  (2,file=LoadFile,status="old",access="sequential",form="formatted",action="read")    
 XLine = 1 ; Year0 = 1000000 ; Year1 = -1000000	; StnN = 0		! get StationN,YearN
 do
  if      (present(Source)) then
    read (2,HeaderFormat),HWmo,HLat,HLong,HElev,ReadStnName,ReadCtyName,HYear0,HYear1,HGcm,HLocal,HSource
  else if (present(PhilJ)) then
    read (2,HeaderFormat),HWmo,HLat,HLong,HElev,ReadStnName,ReadCtyName,HYear0,HYear1,HHomog,HFirst
  else
    read (2,HeaderFormat),HWmo,HLat,HLong,HElev,ReadStnName,ReadCtyName,HYear0,HYear1,HGcm,HLocal
  end if
  
  if (QDatabase.EQ.1) then
  	read (2,"(a1)"), Skip			! skip normals line
  	XLine = XLine + 1
  end if
  if (HYear0.LT.Year0) Year0 = HYear0
  if (HYear1.GT.Year1) Year1 = HYear1
  do XYear = HYear0,HYear1
    read (2,"(a1)"), Skip			! skip time-series lines
  end do
  XLine = XLine + 1 + (HYear1 - HYear0 + 1)
  StnN = StnN + 1
  if (XLine.GT.LineN) exit
 end do

 if (present(YearADMin)) then
   if (YearADMin.LT.Year0) Year0 = YearADMin
 end if
 if (present(YearADMax)) then
   if (YearADMax.GT.Year1) Year1 = YearADMax
 end if
 
 YearN = Year1 - Year0 + 1
 close (2)
end if

FileStnN = StnN
if (present(Extra)) StnN = StnN + Extra

if (present(NmlData)) then
  allocate (NmlData (MonthN,StnN),  stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadCRUts: Allocation failure: NmlData #####"
  NmlData = DataMissVal

  if (present(NmlSrc)) then
    allocate (NmlSrc  (StnN),  stat=AllocStat)
    if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadCRUts: Allocation failure: NmlSrc #####"
    NmlSrc = DataMissVal
  end if
  if (present(NmlInc)) then
    allocate (NmlInc  (StnN),  stat=AllocStat)
    if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadCRUts: Allocation failure: NmlInc #####"
    NmlInc = DataMissVal
  end if
else if (.not.present(HeadOnly)) then
  allocate (Data   (YearN,MonthN,StnN), &
	    YearAD (YearN),             stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadCRUts: Allocation failure: Data #####"
  Data = DataMissVal
  
  do XYear = 1, YearN
    YearAD(XYear) = Year0 + XYear - 1
  end do
  
  if (present(DtbNormals)) then
    allocate (DtbNormals(MonthN,StnN), stat=AllocStat)
    if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadCRUts: Allocation failure: DtbNormals #####"
    DtbNormals = DataMissVal
  end if
  
  if (present(Hulme)) then
    allocate (Hulme (YearN,StnN), stat=AllocStat)
    if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadCRUts: Allocation failure: Hulme #####"
    Hulme = DataMissVal
  end if
end if

allocate (StnInfo (StnN,8),            &
	  StnLocal(StnN),              &
	  StnName (StnN),              &
	  StnCty  (StnN),              stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadCRUts: Allocation failure: Stn #####"
StnInfo = MissVal ; StnName = "UNKNOWN" ; StnCty = "UNKNOWN" ; StnLocal = "   nocode"

if (present(Source)) then
  allocate (Source(StnN),stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadCRUts: Allocation failure: Source #####"
  Source = "SOURCE INFO"
else if (present(SrcCode)) then
  allocate (SrcCode(StnN), &
    	    SrcSuffix(StnN), &
    	    SrcDate(StnN), stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadCRUts: Allocation failure: Src* #####"
  SrcCode = MissVal ; SrcSuffix = "NONE" ; SrcDate = MissVal
end if

open  (2,file=LoadFile,status="old",access="sequential",form="formatted",action="read")    
do XStn = 1, FileStnN
  if (present(NmlData)) then
    read (2,HeaderFormat),(StnInfo(XStn,XInfo),XInfo=1,4),ReadStnName,ReadCtyName, &
  			  (StnInfo(XStn,XInfo),XInfo=5,7),ReadStnLocal,HSrc,HInc
    if (present(NmlSrc)) NmlSrc(XStn)=HSrc
    if (present(NmlInc)) NmlInc(XStn)=HInc
  else if (present(Source)) then
    read (2,HeaderFormat),(StnInfo(XStn,XInfo),XInfo=1,4),ReadStnName,ReadCtyName, &
  			  (StnInfo(XStn,XInfo),XInfo=5,7),ReadStnLocal,Source(XStn)
  else if (present(SrcCode)) then
    read (2,HeaderFormat),(StnInfo(XStn,XInfo),XInfo=1,4),ReadStnName,ReadCtyName, &
  			  (StnInfo(XStn,XInfo),XInfo=5,7),ReadStnLocal, &
  			  SrcCode(XStn),SrcSuffix(XStn),SrcDate(XStn)
  else if (present(PhilJ)) then
    read (2,HeaderFormat),(StnInfo(XStn,XInfo),XInfo=1,4),ReadStnName,ReadCtyName, &
  			  (StnInfo(XStn,XInfo),XInfo=5,6),HHomog,HFirst
  else
    read (2,HeaderFormat),(StnInfo(XStn,XInfo),XInfo=1,4),ReadStnName,ReadCtyName, &
  			  (StnInfo(XStn,XInfo),XInfo=5,7),ReadStnLocal
!    write (99,HeaderFormat),(StnInfo(XStn,XInfo),XInfo=1,4),ReadStnName,ReadCtyName, &
!  			  (StnInfo(XStn,XInfo),XInfo=5,7),ReadStnLocal	! @@@@@@@@@@@@@@@@@@@@@@@@
  end if
  
  StnLocal(XStn) = trim(ReadStnLocal)
  StnName(XStn)  = trim(ReadStnName)
  StnCty (XStn)  = trim(ReadCtyName)
  
  if (.not.present(HeadOnly)) then
    if (present(NmlData)) then
        read (2,DataFormat), HYear,(NmlData(XMonth,XStn),XMonth=1,MonthN)
    else if (QDatabase.EQ.1) then
      if (present(DtbNormals)) then
        read (2,"(i4,12i5)"), HYear,(DtbNormals(XMonth,XStn),XMonth=1,MonthN)
      else
  	read (2,"(a80)"), Trash			! skip normals line
      end if
      do XYear = (StnInfo(XStn,5)-Year0+1), (StnInfo(XStn,6)-Year0+1)
        read (2,DataFormat), HYear,(Data(XYear,XMonth,XStn),XMonth=1,MonthN)
      end do
    else if (present(Hulme)) then
      do XYear = (StnInfo(XStn,5)-Year0+1), (StnInfo(XStn,6)-Year0+1)
        read (2,DataFormat), HYear,(Data(XYear,XMonth,XStn),XMonth=1,MonthN),Hulme(XYear,XStn)
      end do
    else if (present(PhilJ)) then
      XYear = (StnInfo(XStn,5)-Year0+1)-1 ; XYearAD = StnInfo(XStn,5)-1
      do				
        XYear=XYear+1 ; XYearAD=XYearAD+1
        if (XYearAD.LT.HFirst) then
          read (2,"(a1)"), Skip			! ignore unsafe early period
        else					! read safe later period
          read (2,DataFormat), HYear,(Data(XYear,XMonth,XStn),XMonth=1,MonthN)
        end if
        if (XYear.EQ.(StnInfo(XStn,6)-Year0+1)) exit
      end do
      StnInfo(XStn,5) = HFirst
    else
      do XYear = (StnInfo(XStn,5)-Year0+1), (StnInfo(XStn,6)-Year0+1)
        read (2,DataFormat), HYear,(Data(XYear,XMonth,XStn),XMonth=1,MonthN)
      end do
    end if
  end if
end do
close (2)

if (present(Data))    where (Data.EQ.RawMissVal)       Data = DataMissVal
if (present(Hulme))   where (Hulme.EQ.RawMissVal)      Hulme = DataMissVal
if (present(NmlData)) where (NmlData.EQ.RawMissVal)    NmlData = DataMissVal
if (present(DtbNormals))   where (DtbNormals.EQ.RawMissVal) DtbNormals = DataMissVal

if (present(LongType)) then		! convert non-standard longitudes
  if      (LongType.EQ. 2) then
    do XStn = 1, FileStnN
      if (StnInfo(XStn,3).NE.LonMissVal) StnInfo(XStn,3) = StnInfo(XStn,3) * (0-1)
    end do
  else if (LongType.EQ. 3) then
    do XStn = 1, FileStnN
      if (StnInfo(XStn,3).GT.(180*LonFactor).AND.StnInfo(XStn,3).NE.LonMissVal) &
      				StnInfo(XStn,3) = -1 * ((360*LonFactor) - StnInfo(XStn,3))
    end do
  end if
end if
  
if (present(Code)) then
  allocate (Code (StnN), stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadCRUts: Allocation failure: Code #####"
  Code = MissVal
  do XStn = 1, FileStnN
    Code (XStn) = StnInfo (XStn,1)
  end do
end if

if (present(OldCode)) then
  allocate (OldCode (StnN), stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadCRUts: Allocation failure: OldCode #####"
  OldCode = MissVal
  do XStn = 1, FileStnN
    OldCode (XStn) = StnInfo (XStn,7)
  end do
end if

if (present(Lat)) then
  allocate (Lat (StnN), stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadCRUts: Allocation failure: Lat #####"
  Lat = MissVal
  do XStn = 1, FileStnN
    if (StnInfo(XStn,2).NE.LatMissVal) Lat (XStn) = real(StnInfo(XStn,2)) / real(LatFactor)
  end do
end if

if (present(Lon)) then
  allocate (Lon (StnN), stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadCRUts: Allocation failure: Lon #####"  
  Lon = MissVal
  do XStn = 1, FileStnN
    if (StnInfo(XStn,3).NE.LonMissVal) Lon (XStn) = real(StnInfo(XStn,3)) / real(LonFactor)
  end do
end if

if (present(Elv)) then
  allocate (Elv (StnN), stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadCRUts: Allocation failure: Elv #####"
  Elv = MissVal
  do XStn = 1, FileStnN
    if (StnInfo(XStn,4).NE.ElvMissVal) Elv (XStn) = real(StnInfo(XStn,4)) / real(ElvFactor)
  end do
end if

if (present(NmlYr0)) then
  allocate (NmlYr0 (StnN), stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadCRUts: Allocation failure: NmlYr0 #####"
  NmlYr0 = MissVal
  do XStn = 1, FileStnN
    NmlYr0 (XStn) = StnInfo(XStn,5)
  end do
end if

if (present(NmlYr1)) then
  allocate (NmlYr1 (StnN), stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadCRUts: Allocation failure: NmlYr1 #####"
  NmlYr1 = MissVal
  do XStn = 1, FileStnN
    NmlYr1 (XStn) = StnInfo(XStn,6)
  end do
end if

if (.not.present(Silent)) print*, "  > Stations in file total: ", FileStnN

end subroutine LoadCTS

!*******************************************************************************
! to save a stn, it must have: StnInfo(XStn,XInfo), where XInfo=1
! SaveCTS will only save the station if it has a valid column 1 value 
! 	if valid, this routine expects cols 5,6 to also be filled
! input missing values MUST be = -9999 ; to save miss val with different code, use DataMV
! StnLocal added 1.5.02 = a9 local stn identifier field that prev was in StnInfo(*,8)
! Source (optional) added 1.5.02 = a20 for master code files: requires HeadOnly
! NmlData must be accompanied by NmlSrc, NmlInc, and may take most other options,
!   but NOT Data, YearAD, Hulme, HeadOnly, Source
! to save in code order, fill Order with the stn indices to use
! DtbNormals (NOT .dtb) implies a database file with a normals line below the metadata line

subroutine SaveCTS (StnInfo,StnLocal,StnName,StnCty,Data,YearAD,NmlData,DtbNormals,CallFile, &
		    Hulme,HeadOnly,HeadForm,Silent,Source,Order, &
		    SrcCode,SrcSuffix,SrcDate,DataMV,NmlSrc,NmlInc)

integer, pointer, dimension (:,:,:), optional	:: Data		! data, stored as integers (not if HeadOnly)
integer, pointer, dimension (:,:), optional	:: NmlData	! data integers (DataMV okay)
integer, pointer, dimension (:,:), optional	:: DtbNormals	! data integers (DataMV okay)
integer, pointer, dimension (:,:), optional	:: Hulme	! annual column in Hulme
integer, pointer, dimension (:,:)		:: StnInfo	! station key, stored as integers
integer, pointer, dimension (:), optional	:: YearAD	! not if HeadOnly
integer, pointer, dimension (:), optional	:: NmlSrc, NmlInc ! normals source code, % present
integer, pointer, dimension (:), optional	:: SrcCode,SrcDate ! source info
integer, pointer, dimension (:), optional	:: Order 	! num order of stn codes

character (len=20), pointer, dimension (:), optional	:: Source
character (len=20), pointer, dimension (:)		:: StnName
character (len=13), pointer, dimension (:)		:: StnCty
character (len=09), pointer, dimension (:)		:: StnLocal
character (len=04), pointer, dimension (:), optional	:: SrcSuffix

integer, intent(in), optional 			:: Silent	 
integer, intent(in), optional 			:: DataMV	 
integer, intent(in), optional			:: HeadOnly	! if set, save headers-only file

character (len=80), intent(in), optional	:: HeadForm	! format of the headers
character (len=80), intent(in), optional	:: CallFile	! filepath to save (optional)

real, parameter :: MissVal = -999.0

integer, parameter :: DataMissVal = -9999

integer :: ReadStatus,AllocStat
integer :: StnN, MonthN, YearN, LineN, InfoN, SaveStnN, AllStnN
integer :: XStn, XMonth, XYear, XLine, XInfo, XSaveStn, XAllStn
integer :: HWmo,HLat,HLong,HElev,HYear,HYear0,HYear1,HGcm,HLocal
integer :: Year0,Year1,SuffixBeg

character (len=80) :: GivenFile, SaveFile, HeaderFormat
character (len=20) :: HName, HCty, DataFormat
character (len= 4) :: Suffix
character (len= 1) :: Skip

!*******************************************************************************

if (present(Hulme).AND.present(HeadOnly)) print*, &
		"  > ##### ERROR: SaveCRUts: Call Clash: Hulme, HeadOnly #####"

if (present(NmlData)) then
  if (present(Data).OR.present(YearAD).OR.present(Hulme).OR. &
  		present(HeadOnly).OR.present(Source)) &
  		print*, "  > ##### ERROR: SaveCRUts: Call Clash: NmlData present #####"
  if (present(NmlSrc).AND.present(NmlInc)) then
  else
  		print*, "  > ##### ERROR: SaveCRUts: Call Clash: NmlSrc or NmlInc missing #####"
  end if
else
  if (present(NmlSrc).OR.present(NmlInc)) &
  		print*, "  > ##### ERROR: SaveCRUts: Call Clash: NmlData missing #####"
end if

MonthN = 12 ; StnN = size (StnInfo,1)

DataFormat="(i4,12i5)"
if (present(Hulme)) DataFormat="(i4,12i5,i6)"

if (present(HeadForm)) then
  HeaderFormat = trim(HeadForm)
else if (present(NmlData)) then
  HeaderFormat = "(i7,i6,i7,i5,x,a20,x,a13,x,i4,x,i4,i7,a9,x,i2,x,i3)"
else if (present(Hulme)) then
  HeaderFormat = "(i7,i5,i6,i5,a20,a13,i4,i4,i7,a9)"
else
  if (present(Source)) then
    HeaderFormat = "(i7,i6,i7,i5,x,a20,x,a13,x,i4,x,i4,i7,a9,x,a20)"
  else if (present(SrcCode)) then				! note '0' + date = 10 char !!
    HeaderFormat = "(i7,i6,i7,i5,x,a20,x,a13,x,i4,x,i4,i7,a9,x,i4,x,a4,x,a1,i9)"
  else
    HeaderFormat = "(i7,i6,i7,i5,x,a20,x,a13,x,i4,x,i4,i7,a9)"
  end if
end if

if (present(HeadOnly)) then
  Suffix = ".hdr"
else if (present(NmlData)) then
  Suffix = ".nrm"
else
  if (present(CallFile)) then
    SuffixBeg = index(CallFile,".",.TRUE.)
    if      (CallFile(SuffixBeg:(SuffixBeg+3)).EQ.".src") then
      Suffix = ".src"
    else if (CallFile(SuffixBeg:(SuffixBeg+3)).EQ.".dtb") then
      Suffix = ".dtb" ; if (.not.present(DtbNormals)) print*, &
      			"  > ##### ERROR: SaveCRUts: DtbNormals not in save to .dtb #####"
    else if (CallFile(SuffixBeg:(SuffixBeg+3)).EQ.".dts") then
      Suffix = ".dts" ; if (.not.present(DtbNormals)) print*, &
      			"  > ##### ERROR: SaveCRUts: DtbNormals not in save to .dtb #####"
    else
      Suffix = ".cts"
    end if
  else
    Suffix = ".cts"
  end if
  
  YearN = size(Data,1)
  
  if (present(DataMV)) then
    if (present(Data)) where (Data.EQ.DataMissVal) Data = DataMV
    if (present(Hulme)) where (Hulme.EQ.DataMissVal) Hulme = DataMV
    if (present(NmlData)) where (NmlData.EQ.DataMissVal) NmlData = DataMV
    if (present(DtbNormals)) where (DtbNormals.EQ.DataMissVal) DtbNormals = DataMV
  end if
end if

GivenFile = ""								! file to save
if (present(CallFile)) GivenFile = CallFile
if (trim(GivenFile).EQ."") then
  print "(2a)", "  > Enter the file to save with suffix: ", Suffix  
  do
	read (*,*,iostat=ReadStatus), GivenFile
	if (ReadStatus.LE.0.AND.GivenFile.NE."") exit
  end do
end if
SaveFile = SavePath (GivenFile,Suffix)

SaveStnN = StnN

open  (2,file=SaveFile,status="replace",access="sequential",form="formatted",action="write")    
do XAllStn = 1, StnN
 if (present(Order)) then
   XStn = Order(XAllStn)
 else
   XStn = XAllStn
 end if
 
 if (XStn.GE.1.AND.XStn.LE.StnN) then
  if (StnInfo(XStn,1).NE.MissVal) then
   if (present(NmlData)) then
    write (2,HeaderFormat),(StnInfo(XStn,XInfo),XInfo=1,4),StnName(XStn),StnCty(XStn), &
  			 (StnInfo(XStn,XInfo),XInfo=5,7),StnLocal(XStn),NmlSrc(XStn),NmlInc(XStn)
   else if (present(Source)) then
    write (2,HeaderFormat),(StnInfo(XStn,XInfo),XInfo=1,4),StnName(XStn),StnCty(XStn), &
  			 (StnInfo(XStn,XInfo),XInfo=5,7),StnLocal(XStn),Source(XStn)
   else if (present(SrcCode)) then
    write (2,HeaderFormat),(StnInfo(XStn,XInfo),XInfo=1,4),StnName(XStn),StnCty(XStn), &
  			 (StnInfo(XStn,XInfo),XInfo=5,7),StnLocal(XStn), &
  			 SrcCode(XStn),SrcSuffix(XStn),'0',SrcDate(XStn)
   else
    write (2,HeaderFormat),(StnInfo(XStn,XInfo),XInfo=1,4),StnName(XStn),StnCty(XStn), &
  			 (StnInfo(XStn,XInfo),XInfo=5,7),StnLocal(XStn)
   end if
   
   if (present(DtbNormals)) write (2,"(a4,12i5)"), "6190",(DtbNormals(XMonth,XStn),XMonth=1,MonthN)

   if (.not.present(HeadOnly)) then
    if (present(NmlData)) then
        HYear = (100*mod(StnInfo(XStn,5),100)) + mod(StnInfo(XStn,6),100) 
        write (2,DataFormat), HYear,(NmlData(XMonth,XStn),XMonth=1,MonthN)
    else if (present(Hulme)) then
      do XYear = (StnInfo(XStn,5)-YearAD(1)+1), (StnInfo(XStn,6)-YearAD(1)+1)
        write (2,DataFormat), YearAD(XYear), (Data(XYear,XMonth,XStn),XMonth=1,MonthN), &
        			Hulme(XYear,XStn)
      end do
    else
      do XYear = (StnInfo(XStn,5)-YearAD(1)+1), (StnInfo(XStn,6)-YearAD(1)+1)
        write (2,DataFormat), YearAD(XYear), (Data(XYear,XMonth,XStn),XMonth=1,MonthN)
      end do
    end if
   end if
  else
   SaveStnN = SaveStnN - 1
  end if
 else
   SaveStnN = SaveStnN - 1
 end if
end do
close (2)

if (.not.present(Silent)) print*, "  > Stations in file total: ", SaveStnN

if (present(DataMV)) then
    if (present(Data)) where (Data.EQ.DataMV) Data = DataMissVal
    if (present(Hulme)) where (Hulme.EQ.DataMV) Hulme = DataMissVal
    if (present(NmlData)) where (NmlData.EQ.DataMV) NmlData = DataMissVal
    if (present(DtbNormals)) where (DtbNormals.EQ.DataMV) DtbNormals = DataMissVal
end if

end subroutine SaveCTS

!*******************************************************************************
! MAY BE NECESSARY TO ALLOC STNINFO PRIOR TO CALL
! make StnInfo for CRU ts file to be saved, using call info
! needs: WMOcode,Lat,Long,Elev (all vectors, StnN)
! needs: at least one of [Data,YearAD],[YearAD0,YearAD1]: [YearAD0,YearAD1] takes precedence
! needs: QNoPer (if no period obtainable: 0=save-all, 1=no-save)
! returns: StnInfo 
! SaveCTS will only save the station if it has a valid column 1 value 
! 	if valid, that routine expects cols 5,6 to also be filled
! data missing value MUST be -9999
! assume convert to Lat*100,Lon*100,Elv*1 ; to change set LatF,LonF,ElvF
! for .nrm files, supply NmlYr0, NmlYr1 for YearAD0,YearAD1
! for .dtb files, supply DtbNormals if supplying Data
! QBlank=1 if there are no stns to be saved

subroutine MakeStnInfo (StnInfo,WMOcode,OldCode,Lat,Long,Elev,QNoPer,QLongType, &
			YearAD0,YearAD1,YearAD,Data,DtbNormals,&
			Silent,QBlank,LatMV,LonMV,ElvMV,LatF,LonF,ElvF)

real, pointer, dimension (:)			:: Lat,Long,Elev

integer, pointer, dimension (:,:,:), optional	:: Data
integer, pointer, dimension (:,:), optional	:: DtbNormals
integer, pointer, dimension (:,:)		:: StnInfo
integer, pointer, dimension (:)			:: WMOcode,OldCode
integer, pointer, dimension (:), optional	:: YearAD0,YearAD1,YearAD

logical, allocatable, dimension (:)		:: Mask

character (len=20), pointer, dimension (:)	:: StnName
character (len=13), pointer, dimension (:)	:: CtyName

logical, intent(out), optional  :: QBlank

integer, intent(in), optional 	:: LatMV,LonMV,ElvMV	! missing values
integer, intent(in) 		:: QNoPer
integer, intent(in), optional	:: QLongType
integer, intent(in), optional 	:: Silent	 
integer, intent(in), optional 	:: LatF,LonF,ElvF	! Factors

real, parameter :: MissVal = -999.0

integer, parameter :: DataMissVal = -9999

integer :: LatFactor,LonFactor,ElvFactor
integer :: LatMissVal,LonMissVal,ElvMissVal
integer :: ReadStatus, AllocStat
integer :: StnN,XStn, YearN,XYear, MonthN,XMonth, SaveStnN,XSaveStn,BlankStnN,NoCodeStnN
integer :: YearADMin,YearADMax,Year

StnN = size (WMOCode,1) ; MonthN = 12 ; BlankStnN = 0 ; NoCodeStnN = 0 ; SaveStnN = 0

LatMissVal=-9999 ; LonMissVal=-99999 ; ElvMissVal=-999		! usual/hulme hdr miss vals
if (present(LatMV)) LatMissVal = LatMV				! custom hdr miss vals
if (present(LonMV)) LonMissVal = LonMV
if (present(ElvMV)) ElvMissVal = ElvMV

LatFactor=100 ; LonFactor=100 ; ElvFactor=1			! usual/hulme hdr factors
if (present(LatF)) LatFactor = LatF				! custom hdr factors
if (present(LonF)) LonFactor = LonF
if (present(ElvF)) ElvFactor = ElvF

if (.NOT.associated(StnINfo)) then
  allocate (StnInfo(StnN,8), stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: MakeStnInfo: alloc StnInfo #####"
end if
allocate (Mask   (StnN),   stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: MakeStnInfo: alloc Mask #####"

StnInfo = -999
do XStn = 1, StnN
  StnInfo(XStn,2)=LatMissVal ; StnInfo(XStn,3)=LonMissVal ; StnInfo(XStn,4)=ElvMissVal
end do

do XStn = 1, StnN
 if (WMOCode(XStn).NE.MissVal) StnInfo(XStn,1)=WMOCode(XStn)
 if (OldCode(XStn).NE.MissVal) StnInfo(XStn,7)=OldCode(XStn)
 if (Lat (XStn).NE.MissVal)    StnInfo(XStn,2)=nint(Lat (XStn)*real(LatFactor))
 if (Long(XStn).NE.MissVal)    StnInfo(XStn,3)=nint(Long(XStn)*real(LonFactor))
 if (Elev(XStn).NE.MissVal)    StnInfo(XStn,4)=nint(Elev(XStn)*real(ElvFactor))
end do

if (present(QLongType)) then		! convert non-standard longitudes
  if      (QLongType.EQ. 2) then
    do XStn = 1, StnN
      if (StnInfo(XStn,3).NE.LonMissVal) StnInfo(XStn,3) = StnInfo(XStn,3) * (0-1)
    end do
  else if (QLongType.EQ. 3) then
    do XStn = 1, StnN
      if (StnInfo(XStn,3).LT.0.AND.StnInfo(XStn,3).NE.LonMissVal) &
      				StnInfo(XStn,3) = 360 - StnInfo(XStn,3)
    end do
  end if
end if
  
if (present(YearAD0).AND.present(YearAD1)) then
 Mask=.FALSE.
 where (YearAD0.NE.MissVal) Mask=.TRUE.
 YearADMin = minval(YearAD0,Mask)
 YearADMin = YearAD0(YearADMin)
 
 Mask=.FALSE.
 where (YearAD1.NE.MissVal) Mask=.TRUE.
 YearADMax = maxval(YearAD1,Mask)
 YearADMax = YearAD1(YearADMax)
 
 do XStn = 1, StnN
  if (StnInfo(XStn,1).NE.MissVal) then
   if (YearAD0(XStn).NE.MissVal.AND.YearAD1(XStn).NE.MissVal) then
  	StnInfo(XStn,5)=YearAD0(XStn)
  	StnInfo(XStn,6)=YearAD1(XStn)
  	SaveStnN = SaveStnN + 1
   else
  	if (QNoPer.EQ.0) then
  		StnInfo(XStn,5)=YearAD0(XStn)
  		StnInfo(XStn,6)=YearAD1(XStn)
  		BlankStnN = BlankStnN + 1
  		SaveStnN = SaveStnN + 1
  	else
  		StnInfo(XStn,1)=MissVal
  		StnInfo(XStn,5)=MissVal
  		StnInfo(XStn,6)=MissVal
  		BlankStnN = BlankStnN + 1
  	end if
   end if
  else
   NoCodeStnN = NoCodeStnN + 1
  end if
 end do
 
 if (.not.present(Silent)) print "(a,3i6)", "   > Totals: no-code,no-data,save: ", &
 				NoCodeStnN,BlankStnN,SaveStnN 
else if (present(Data).AND.present(YearAD)) then
 YearN = size(YearAD,1) 
 YearADMin = YearAD(1) ; YearADMax = YearAD(YearN)
 
 do XStn = 1, StnN
  if (StnInfo(XStn,1).NE.MissVal) then
   XYear = 0					! find station's first year with data
   do
     XYear = XYear + 1

     XMonth = 0
     do
       XMonth = XMonth + 1
       if (Data(XYear,XMonth,XStn).NE.DataMissVal) StnInfo(XStn,5)=YearAD(XYear)
       if (StnInfo(XStn,5).NE.MissVal) exit
       if (XMonth.EQ.MonthN) exit
     end do
     
     if (StnInfo(XStn,5).NE.MissVal) exit
     if (XYear.EQ.YearN) exit
   end do
   
   if (StnInfo(XStn,5).NE.MissVal) then		! got first yr, find last yr
    SaveStnN = SaveStnN + 1
    XYear = YearN + 1				
    do
     XYear = XYear - 1

     XMonth = 0
     do
       XMonth = XMonth + 1
       if (Data(XYear,XMonth,XStn).NE.DataMissVal) StnInfo(XStn,6)=YearAD(XYear)
       if (StnInfo(XStn,6).NE.MissVal) exit
       if (XMonth.EQ.MonthN) exit
     end do
     
     if (StnInfo(XStn,6).NE.MissVal) exit
     if (XYear.EQ.1) exit
    end do   
   else if (present(DtbNormals)) then		! no ts data, seek normals
     XMonth = 0
     do
       XMonth = XMonth + 1
       if (DtbNormals(XMonth,XStn).NE.DataMissVal) then
       		StnInfo(XStn,5)=YearAD(1) ; StnInfo(XStn,6)=YearAD(1)
       end if
       if (StnInfo(XStn,5).NE.MissVal) exit
       if (XMonth.EQ.MonthN) exit
     end do
   end if
   
   if (StnInfo(XStn,5).EQ.MissVal) then		! no data whatsoever
    if (QNoPer.EQ.0) then				! set to full range
  		StnInfo(XStn,5)=YearAD(1)
  		StnInfo(XStn,6)=YearAD(YearN)
  		BlankStnN = BlankStnN + 1
  		SaveStnN = SaveStnN + 1
    else						! set to missing
  		BlankStnN = BlankStnN + 1
  		StnInfo(XStn,1)=MissVal
  		StnInfo(XStn,5)=MissVal
  		StnInfo(XStn,6)=MissVal
    end if    
   end if
  else
   NoCodeStnN = NoCodeStnN + 1
  end if
 end do
 
 if (.not.present(Silent)) print "(a,3i6)", "   > Totals: no-code,no-data,save: ", &
 				NoCodeStnN,BlankStnN,SaveStnN 
 if (present(QBlank)) then
   QBlank=.TRUE. ; XStn=0
   do
     XStn=XStn+1
     if (StnInfo(XStn,1).NE.MissVal) QBlank=.FALSE.
     if (XStn.EQ.StnN) exit
     if (.NOT.QBlank) exit
   end do
 end if
else
 print*, "  > ***** PROBLEM: MakeStnInfo: Insufficient call: no save *****"
 StnInfo=MissVal
end if

end subroutine MakeStnInfo

!*******************************************************************************
! takes two independent station code vectors, 
!	1. combines them (.OR.) into CStn (size NCStn)
! 	2. provides vectors CfromA,CfromB (size NCStn) of indices from AStn,BStn

subroutine CombineStn (AStn,BStn,CStn,CfromA,CfromB)

integer, pointer, dimension (:) 	:: AStn,BStn,CStn,CfromA,CfromB
integer, allocatable, dimension (:) 	:: CalcCStn,CalcCfromA,CalcCfromB

real, parameter :: MissVal = -999.0

integer :: AllocStat
integer :: XAStn,XBStn,XCStn
integer :: NAStn,NBStn,NCStn

NAStn=size(AStn) ; NBStn=size(BStn)
allocate (CalcCStn   (NAStn+NBStn), &
	  CalcCfromA (NAStn+NBStn), &
	  CalcCfromB (NAStn+NBStn), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: CombineStn: Allocation failure: Calc #####"
CalcCStn=MissVal ; CalcCfromA=MissVal ; CalcCfromB=MissVal

XAStn=1 ; XBStn=1 ; XCStn=0
do  
  if (XAStn.LE.NAStn) then
    if (XBStn.LE.NBStn) then
      if      (AStn(XAStn).LT.BStn(XBStn)) then
        XCStn = XCStn + 1
	CalcCStn(XCStn)=AStn(XAStn) ; CalcCfromA(XCStn)=XAStn 
	XAStn = XAStn + 1
      else if (AStn(XAStn).EQ.BStn(XBStn)) then
        XCStn = XCStn + 1
	CalcCStn(XCStn)=AStn(XAStn) ; CalcCfromA(XCStn)=XAStn ; CalcCfromB(XCStn)=XBStn 
        XBStn = XBStn + 1
        XAStn = XAStn + 1
      else if (AStn(XAStn).GT.BStn(XBStn)) then
        XCStn = XCStn + 1
	CalcCStn(XCStn)=BStn(XBStn) ; CalcCfromB(XCStn)=XBStn 
        XBStn = XBStn + 1
      end if
    else
      XCStn = XCStn + 1
      CalcCStn(XCStn)=AStn(XAStn) ; CalcCfromA(XCStn)=XAStn 
      XAStn = XAStn + 1
    end if
  else
    if (XBStn.LE.NBStn) then
      XCStn = XCStn + 1
      CalcCStn(XCStn)=BStn(XBStn) ; CalcCfromB(XCStn)=XBStn 
      XBStn = XBStn + 1
    end if
  end if
  
  if (XAStn.GT.NAStn.AND.XBStn.GT.NBStn) exit
end do

NCStn=XCStn
allocate (CStn   (NCStn), &
	  CfromA (NCStn), &
	  CfromB (NCStn), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: CombineStn: Allocation failure: C* #####"
do XCStn = 1, NCStn
  CStn   (XCStn) = CalcCStn   (XCStn)
  CfromA (XCStn) = CalcCfromA (XCStn)
  CfromB (XCStn) = CalcCfromB (XCStn)
end do

deallocate (CalcCStn,CalcCfromA,CalcCfromB, stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: CombineStn: Deallocation failure: Calc* #####"

end subroutine CombineStn

!*******************************************************************************

end module CRUtsFiles

