! cleanmeta.f90
! f90 program written by Tim Mitchell on 14.05.02 as accession.f90
! rewritten as CleanMeta.f90 (March 2003), 
! then rewritten again as cleanmeta.f90 (June 2003)
! program to process new files of station monthly obs
! pgf90 -Mstandard -Minfo -fast -Mscalarsse -Mvect=sse -Mflushz 
!	-o ./../cruts/cleanmeta time.f90 filenames.f90 grimfiles.f90 
!	crutsfiles.f90 gridops.f90 sortmod.f90 ctyfiles.f90 countries.f90 
!	./../cruts/cleanmeta.f90 2> /tyn1/tim/scratch/stderr.txt

program CleanMeta

use Time
use FileNames
use GrimFiles
use CRUtsFiles
use GridOps
use SortMod
use CtyFiles
use Countries

implicit none

logical, pointer, dimension (:)		:: Check

real, pointer, dimension (:,:)		:: RelA,RelB,RelR
real, pointer, dimension (:)		:: LatO,LonO,ElvO, LatM,LonM,ElvM
real, pointer, dimension (:)		:: LatA,LonA,ElvA
real, pointer, dimension (:)		:: MasterLat,MasterLon,MasterEn
real, pointer, dimension (:)		:: OriginalLat,OriginalLon,OriginalEn
real, pointer, dimension (:)		:: CtyLon,CtyLat,CtyTot
real, pointer, dimension (:)		:: MatchCodeA,MatchDistanceA,MatchNameA,MatchScoreA
real, pointer, dimension (:)		:: CandidScoreA,CandidScoreM
real, pointer, dimension (:)		:: Decay

integer, pointer, dimension (:,:,:)	:: DataO,DataSrcO
integer, pointer, dimension (:,:)	:: DataNmlO
integer, pointer, dimension (:,:)	:: Info,TrashInfo
integer, pointer, dimension (:,:)	:: CandidExe,CandidWye,Combo
integer, pointer, dimension (:)		:: StnOldO,StnNewM,StnOldA,StnNewA
integer, pointer, dimension (:)         :: StnSrcCodeO,StnSrcCodeA,YearADO
integer, pointer, dimension (:)		:: StnCodeBegO,StnCodeEndO,StnContinentO
integer, pointer, dimension (:)		:: StnSrcDateA,MissInts,Source
integer, pointer, dimension (:) 	:: MasterCode0,MasterCode1,MasterContinent
integer, pointer, dimension (:) 	:: Order,DecayOrder,CandidIndexM,CandidIndexA

character (len=80), pointer, dimension (:) :: Batch
character (len=80), pointer, dimension (:) :: MasterBatch
character (len=20), pointer, dimension (:) :: StnNameO,StnNameM,StnNameA,TrashName
character (len=13), pointer, dimension (:) :: StnCtyO,StnCtyM,StnCtyA,TrashCty
character (len=13), pointer, dimension (:) :: MasterCty,MasterRawName,MasterFinalName
character (len=09), pointer, dimension (:) :: StnLocalO,StnLocalM,StnLocalA,TrashLocal
character (len=04), pointer, dimension (:) :: StnSrcSuffixA

real, dimension (12)	:: MonthA,MonthB,MonthR,SumR
integer, dimension (12)	:: Min,Max

real, parameter :: DataMissVal 	= -9999.0
real, parameter :: MissVal 	= -999.0
real, parameter :: MultiSource 	= -888.0
real, parameter :: SumRThresh 	= 0.25

character (len=80), parameter :: LatLonFile = './../../../scratch/test-latlon.txt'

logical :: Invalid

real :: OpVal,OpTot,OpEn,OpTest,OpDiff,StdErr,Lower,Upper
real :: Factor,Distance,StnODigit6,RealVal,CorrelMean,WeightMean,DecayMemory,Estimate
real :: TotNull,TotNeg,TotSmall,MeanR, CurrentKm,DecimalKM

integer :: ReadStatus, AllocStat
integer :: QOrigCode,QOrigSrc,QDigits,QIncludeOrig,QConvertedCty,QCandid,QDecay
integer :: QDodgy,QAlter,QClarify
integer :: NOYear,NDYear,NOStn,NMStn,NAStn
integer :: XOYear,XDYear,XOStn,XMStn,XAStn
integer :: NChar,NBatch,NMonth,NMasterCty,NPrior,NCandid,NDecay,NRel,NSource,NSuspect
integer :: XChar,XBatch,XMonth,XMasterCty,XPrior,XCandid,XDecay,XRel,XSource,XSuspect
integer :: SubBeg,SubLen,Ascii,Code,OrigSrcCode,AnswerNoYes,Reverse,Find,Seek
integer :: OStart,OEnd,DStart,DEnd,Butt,ButtYX,DecayDistance,StampInt,Current,RunLen
integer :: CandidAStn,CandidDStn,CandidMStn,MinusSrc,ExtraSrc,SuspectBeg,SuspectEnd
integer :: AssignAStn,AssignDStn,AssignMStn, Log1,Log2,Log3,Log4

character (len=80) :: GivenFile,Variable
character (len=80) :: OrigFileL,OrigSrcFileL,MasterFileL,AccessFileL
character (len=80) :: OrigFileS,OrigSrcFileS,MasterFileS,AccessFileS
character (len=80) :: DatabaseSrcFileL
character (len=80) :: DatabaseSrcFileS
character (len=20) :: Dirty,Name,Stamp20
character (len=10) :: CurrentTime,FileTime,Stamp10,LogText
character (len=08) :: CurrentDate,FileDate
character (len=04) :: VariableSuffix

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

call Intro			! initial tasks and options
call SelectOriginal
call SelectVariable

call LoadingOriginal		! load sufficent for the metadata clean + check
call LoadingMaster
call LoadingAccessions

call CleanNames			! metadata clean + check
call CleanOtherMeta
QClarify=1 ; call ClarifyDegrees
QClarify=2 ; call ClarifyDegrees
call CheckLocation				! main loop
if (VariableSuffix.NE.".pre".AND.VariableSuffix.NE.".wet") call CheckOtherMiss
call CheckMinData
call PrepareSave
call SavingOriginal

call Conclude

!*******************************************************************************
! CharByChar		goes through text chracter by character
! CheckLocation		checks lat/lon of each stn
! CleanNames		makes stn,cty names caps and no-hyphens
! CleanOtherMeta	cleans meta data	
! Conclude 		conclude program
! DumpDodgyLocations	dump any stns with dodgy locations to file
! FindDatabase		identify database file
! GetCtyDist		get average distance from mid-point by country
! GetFromMaster		total up all lat/lon in master code file by country
! GetFromOriginal	total up all lat/lon in original .cts file by country
! Intro			initialises program
! LoadingMaster		load master .hdr file
! LoadingOriginal	load original .cts file
! LogMarker		put progress marker in log file
! ManualMeta		allows manual changes to metadata
! PrepareSave		prepares for saving
! SavingOriginal	saves original .cts file, with metadata tidied up
! SelectOriginal	provides user with load options and specifications
! SelectProc		processing options
! SelectVariable	select climate variable, by suffix

contains

!*******************************************************************************
! goes through text chracter by character

subroutine CharByChar

NChar = len_trim(adjustl(Dirty))
if (NChar.EQ.0) then
    Dirty = "UNKNOWN" ; NChar = 7
end if
  
do XChar = 1, NChar			
    Ascii = iachar(Dirty(XChar:XChar))
    if      (Ascii.GE.97.AND.Ascii.LE.122) then 
    	Dirty(XChar:XChar) = achar(Ascii-32)		! make uppercase
    else if (Ascii.EQ.45) then
    	Dirty(XChar:XChar) = " "			! remove hyphenations
    else if (Ascii.EQ.42) then
    	Dirty(XChar:XChar) = " "			! remove asterisks
    else if (Ascii.EQ.35) then
    	Dirty(XChar:XChar) = "%"			! remove hashes
    else if (Ascii.GE. 1.AND.Ascii.LE.31) then 
    	Dirty(XChar:XChar) = " "			! remove funny characters
    else if (Ascii.LT. 1.OR.Ascii.GT.127) then
    	Dirty(XChar:XChar) = " "			! remove funny characters
    end if

    if (XChar.EQ.NChar) then			! remove trailing  
      if      (Ascii.EQ.38) then
    	Dirty(XChar:XChar) = " "			! ampersand
      else if (Ascii.EQ.40) then
    	Dirty(XChar:XChar) = " "			! round bracket
      else if (Ascii.EQ.91) then
    	Dirty(XChar:XChar) = " "			! square bracket
      else if (Ascii.EQ.44) then
    	Dirty(XChar:XChar) = " "			! comma
      end if
    end if
end do

NChar = len_trim(adjustl(Dirty))
if (NChar.EQ.0) then
    Dirty = "UNKNOWN" ; NChar = 7
end if

end subroutine CharByChar

!*******************************************************************************
! checks lat/lon of each stn

subroutine CheckLocation

print*, "  > Checking the lat/lon of each stn in .cts file ..."
QIncludeOrig=0 ; QDodgy=0
do
  if (associated(StnCodeBegO)) then
    deallocate (StnCodeBegO,StnCodeEndO,StnContinentO, stat=AllocStat)
    if (AllocStat.NE.0) print*, "  > ##### ERROR: CheckLocation: Dealloc #####"
  end if
  
  call ClarifyCty (StnCtyO,StnNameO,LatO,LonO,StnOldO,StnCtyM,StnNameM, &
  			LatM,LonM,StnCodeBegO,StnCodeEndO,StnContinentO)
  call GetFromMaster
  call GetFromOriginal
  call GetCtyDist
  call DumpDodgyLocations

  if (QDodgy.GT.0) then
    QIncludeOrig=1
    print "(2a)", "   > Lat/lon check: eyeball ", trim(LatLonFile)
    call ManualMeta
  end if

  deallocate (MasterCty,MasterLat,MasterLon,MasterEn, &
  	      OriginalLat,OriginalLon,OriginalEn,CtyLon,CtyLat,CtyTot,stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: CheckLocation: Dealloc #####"
  
  if (QDodgy.EQ.0) exit
end do

end subroutine CheckLocation

!*******************************************************************************
! checks whether a stn has at least one datum per calendar month 
! 	if not, the stn is omitted, as useless, because it can never be merged

subroutine CheckMinData

do XOStn = 1, NOStn
  Invalid=.FALSE. ; XMonth=0
  do
    XMonth=XMonth+1 ; XOYear=0
    do
      XOYear=XOYear+1
      if (DataO(XOYear,XMonth,XOStn).NE.DataMissVal) exit
      if (XOYear.EQ.NOYear) exit
    end do
    if (DataO(XOYear,XMonth,XOStn).EQ.DataMissVal) Invalid=.TRUE.
    if (XMonth.EQ.NMonth.OR.Invalid) exit
  end do
  
  if (Invalid) StnOldO(XOStn)=MissVal
end do

end subroutine CheckMinData

!*******************************************************************************
! checks whether a stn has unregistered missing values posing as valid data

subroutine CheckOtherMiss

do XOStn = 1, NOStn
 do
  Invalid=.FALSE. ; XOYear=0 ; Current=DataMissVal ; RunLen=0
  do
    XOYear=XOYear+1 ; XMonth=0
    do
      XMonth=XMonth+1
      if (DataO(XOYear,XMonth,XOStn).NE.DataMissVal) then
        if (DataO(XOYear,XMonth,XOStn).EQ.Current) then
          RunLen=RunLen+1 ; if (RunLen.EQ.4) Invalid=.TRUE.
        else
          RunLen=1
        end if
        Current=DataO(XOYear,XMonth,XOStn)
      end if
      if (XMonth.EQ.NMonth.OR.Invalid) exit
    end do
    if (XOYear.EQ.NOYear.OR.Invalid) exit
  end do
  
  if (Invalid) then
    do XOYear=1,NOYear
      do XMonth=1,NMonth
        if (DataO(XOYear,XMonth,XOStn).EQ.Current) &
        		DataO(XOYear,XMonth,XOStn)=DataMissVal
      end do
    end do
  end if
  
  if (.NOT.Invalid) exit
 end do 
end do

end subroutine CheckOtherMiss

!*******************************************************************************
! checks whether degrees have a fraction or no. minutes after decimal point 

subroutine ClarifyDegrees

Lower=0 ; Upper=0
do XOStn = 1, NOStn
  if (LatO(XOStn).NE.MissVal.AND.LatO(XOStn).NE.0.0 .AND. &
      LonO(XOStn).NE.MissVal.AND.LonO(XOStn).NE.0.0) then
    if (abs(mod(LatO(XOStn),1.0)).LT.0.6.AND.abs(mod(LonO(XOStn),1.0)).LT.0.6) then
      Lower=Lower+1
    else
      Upper=Upper+1
    end if
  end if
end do

if (Upper.EQ.0) then
  print*, "  > The lat/lon have minutes. Converting to decimal..."
  
  allocate (Check(NOStn),stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: ClarifyDegrees: alloc #####"
  Check=.TRUE.
  
  call ConvertDecimal
  
  deallocate (Check,stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: ClarifyDegrees: dealloc #####"
else if ((Lower+Upper).GT.0) then
  OpTest=Lower/(Lower+Upper)
  OpDiff=abs(OpTest-0.36)
  StdErr=sqrt((OpTest*(1.0-OpTest))/(Lower+Upper))
  
  if (OpDiff.GT.(3.0*StdErr)) then
    print*, "  > @@@@@ PROBLEM: Some stns have deg.min not deg.deg @@@@@"
    print "(a,3f8.2)", "   > @@@@@ test stat (stdev) =", OpDiff/StdErr,Lower,Upper

    if (QClarify.EQ.1) call SeekDegMinSrc
    if (QClarify.EQ.2) call SeekDegMinMaster
    if (QClarify.EQ.2) call SeekDegMinSection
  end if
end if

end subroutine ClarifyDegrees

!*******************************************************************************
! makes stn,cty names caps and no-hyphens

subroutine CleanNames

do XOStn = 1, NOStn
  Dirty = trim(adjustl(StnNameO(XOStn)))
  call CharByChar
  StnNameO(XOStn) = Dirty(1:20)
  
  Dirty = trim(adjustl(StnCtyO(XOStn))) ; Dirty(14:20) = "       "
  call CharByChar
  StnCtyO(XOStn) = Dirty(1:13)
end do

end subroutine CleanNames

!*******************************************************************************
! cleans meta data

subroutine CleanOtherMeta

do XOStn = 1, NOStn
  if (LatO(XOStn).LT. -90.OR.LatO(XOStn).GT.  90 .OR. &
      LonO(XOStn).LT.-180.OR.LonO(XOStn).GT. 180) then
      	LatO(XOStn) = MissVal ; LonO(XOStn) = MissVal
  end if
  if (ElvO(XOStn).LT.-400.OR.ElvO(XOStn).GT.6000) ElvO(XOStn) = MissVal
end do

end subroutine CleanOtherMeta

!*******************************************************************************
! conclude program

subroutine Conclude

print*
close (99)

end subroutine Conclude

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

subroutine ConvertDecimal

do XOStn = 1, NOStn
 if (Check(XOStn)) then
  if (	LatO(XOStn).NE.MissVal.AND.abs(mod(LatO(XOStn),1.0)).GE.0.6 .OR. &
  	LonO(XOStn).NE.MissVal.AND.abs(mod(LonO(XOStn),1.0)).GE.0.6) then
  		! nothing required here - already deg.deg
  else
    if (LatO(XOStn).NE.MissVal) LatO(XOStn) = &
    		LatO(XOStn)+(0.66666*mod(LatO(XOStn),1.0))
    if (LonO(XOStn).NE.MissVal) LonO(XOStn) = &
    		LonO(XOStn)+(0.66666*mod(LonO(XOStn),1.0))
  end if
 end if
end do

end subroutine ConvertDecimal

!*******************************************************************************
! dump any stns with dodgy locations to file

subroutine DumpDodgyLocations

open (1,file=LatLonFile,status="replace",action="write")
write (1,"(a7,a7,a8,a8,x,a20,x,a13,2a8)"), "7-digit","    lat","    long","     elv", &
        			"station name        ","country      ","distance","   rad*3 "
QDodgy=0
do XOStn = 1, NOStn			! check the lat/lon of each station
 if (LatO(XOStn).NE.MissVal.AND.LonO(XOStn).NE.MissVal) then
  XMasterCty=0
  do					! find cty on master list
    XMasterCty=XMasterCty+1

    if (MasterCty(XMasterCty).EQ.StnCtyO(XOStn).AND.CtyTot(XMasterCty).GT.2.AND. &
    		CtyLat(XMasterCty).NE.MissVal.AND.CtyLon(XMasterCty).NE.MissVal) then
      
      Distance = GetDistance (CtyLat(XMasterCty),CtyLon(XMasterCty),LatO(XOStn),LonO(XOStn))
      
      if (Distance.GT.(CtyTot(XMasterCty)*3.0)) then
        StnODigit6 = real(StnOldO(XOStn))/10.0 ; QConvertedCty=1
        
        if (nint(StnODigit6).EQ.StnODigit6) then
          XMStn=0
          do
            XMStn=XMStn+1
            if (StnODigit6.EQ.StnNewM(XMStn).AND.StnNameO(XOStn).EQ.StnNameM(XMStn)) then
              StnCtyO(XOStn)=StnCtyM(XMstn) ; QConvertedCty=1
            end if
            if (StnODigit6.EQ.StnNewM(XMStn).OR.StnNewM(XMStn).EQ.MissVal) exit
          end do
        end if
        
        if (QConvertedCty.EQ.0) then
          write (1,"(i8,f7.2,f8.2,f8.1,x,a20,x,a13,2f8.1)"), &
          		StnOldO(XOStn),LatO(XOStn),LonO(XOStn),ElvO(XOStn), &
        		StnNameO(XOStn),StnCtyO(XOStn),Distance,(CtyTot(XMasterCty)*3.0)
          QDodgy=QDodgy+1
        end if
      end if
    end if
    
    if (MasterCty(XMasterCty).EQ.StnCtyO(XOStn).OR.XMasterCty.EQ.NMasterCty) exit
  end do
 end if
end do

close (1)

end subroutine DumpDodgyLocations

!*******************************************************************************
! get average distance from mid-point by country

subroutine GetCtyDist

allocate (CtyLat   (NMasterCty), &
	  CtyLon   (NMasterCty), &
	  CtyTot   (NMasterCty), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: GetCountryDistComb: Allocation failure #####"
CtyLat = 0.0 ; CtyLon = 0.0 ; CtyTot = 0.0

do XMasterCty=1,NMasterCty	! get lat/lon mid-point by country
  if      (QIncludeOrig.EQ.0.AND.MasterEn(XMasterCty).GT.0) then
    CtyLat(XMasterCty) = MasterLat(XMasterCty) / MasterEn(XMasterCty)
    CtyLon(XMasterCty) = MasterLon(XMasterCty) / MasterEn(XMasterCty)
  else if (QIncludeOrig.EQ.1.AND.(MasterEn(XMasterCty)+OriginalEn(XMasterCty)).GT.0) then
    CtyLat(XMasterCty) = (MasterLat(XMasterCty)+OriginalLat(XMasterCty)) / &
    				(MasterEn(XMasterCty)+OriginalEn(XMasterCty))
    CtyLon(XMasterCty) = (MasterLon(XMasterCty)+OriginalLon(XMasterCty)) / &
    				(MasterEn(XMasterCty)+OriginalEn(XMasterCty))
  end if
end do

do XMStn = 1, NMStn		! total up all distances from mid-point by country (master)
  XMasterCty=0
  do
    XMasterCty=XMasterCty+1
    if (MasterCty(XMasterCty).EQ.StnCtyM(XMStn)) then
      if (LatM(XMStn).NE.MissVal.AND.LonM(XMStn).NE.MissVal) CtyTot(XMasterCty) = &
      		CtyTot(XMasterCty) + &
      		GetDistance (CtyLat(XMasterCty),CtyLon(XMasterCty),LatM(XMStn),LonM(XMStn))
    end if 
    if (MasterCty(XMasterCty).EQ.StnCtyM(XMStn).OR.XMasterCty.EQ.NMasterCty) exit
  end do
end do

if (QIncludeOrig.EQ.1) then
 do XOStn = 1, NOStn		! total up all distances from mid-point by country (original)
  XMasterCty=0
  do
    XMasterCty=XMasterCty+1
    if (MasterCty(XMasterCty).EQ.StnCtyO(XOStn)) then
      if (LatO(XOStn).NE.MissVal.AND.LonO(XOStn).NE.MissVal) CtyTot(XMasterCty) = &
      		CtyTot(XMasterCty) + &
      		GetDistance (CtyLat(XMasterCty),CtyLon(XMasterCty),LatO(XOStn),LonO(XOStn))
    end if 
    if (MasterCty(XMasterCty).EQ.StnCtyO(XOStn).OR.XMasterCty.EQ.NMasterCty) exit
  end do
 end do
end if

do XMasterCty=1,NMasterCty	! get average distance from mid-point by country
  if      (QIncludeOrig.EQ.0.AND.MasterEn(XMasterCty).GT.0) then
    CtyTot(XMasterCty) = CtyTot(XMasterCty) / MasterEn(XMasterCty)
  else if (QIncludeOrig.EQ.1.AND.(MasterEn(XMasterCty)+OriginalEn(XMasterCty)).GT.0) then
    CtyTot(XMasterCty) = CtyTot(XMasterCty) / (MasterEn(XMasterCty)+OriginalEn(XMasterCty))
  end if
end do

end subroutine GetCtyDist

!*******************************************************************************
! total up all lat/lon in master code file by country

subroutine GetFromMaster

NMasterCty = MasterSize()
allocate (MasterLat   (NMasterCty), &
	  MasterLon   (NMasterCty), &
	  MasterEn    (NMasterCty), &
	  MasterRawName(NMasterCty),MasterCty(NMasterCty),MasterCode0(NMasterCty), &
	  MasterCode1(NMasterCty),MasterContinent(NMasterCty),stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: GetFromMaster: Alloc #####"
MasterLat = 0.0 ; MasterLon = 0.0 ; MasterEn = 0.0

call LoadMasterCty (MasterRawName,MasterCty,MasterCode0,MasterCode1,MasterContinent)

deallocate (MasterRawName,MasterCode0,MasterCode1,MasterContinent, stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: GetFromMaster: Dealloc #####"

do XMStn = 1, NMStn
  XMasterCty=0
  do
    XMasterCty=XMasterCty+1
    if (MasterCty(XMasterCty).EQ.StnCtyM(XMStn)) then
      if (LatM(XMStn).NE.MissVal.AND.LonM(XMStn).NE.MissVal) then
        MasterLat(XMasterCty) = MasterLat(XMasterCty) + LatM(XMStn)
        MasterLon(XMasterCty) = MasterLon(XMasterCty) + LonM(XMStn)
        MasterEn (XMasterCty) = MasterEn (XMasterCty) + 1.0
      end if
    end if 
    if (MasterCty(XMasterCty).EQ.StnCtyM(XMStn).OR.XMasterCty.EQ.NMasterCty) exit
  end do
end do

end subroutine GetFromMaster

!*******************************************************************************
! total up all lat/lon in Original code file by country

subroutine GetFromOriginal

allocate (OriginalLat   (NMasterCty), &
	  OriginalLon   (NMasterCty), &
	  OriginalEn    (NMasterCty), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: GetFromOriginal: Alloc #####"
OriginalLat = 0.0 ; OriginalLon = 0.0 ; OriginalEn = 0.0

do XOStn = 1, NOStn	
  XMasterCty=0
  do
    XMasterCty=XMasterCty+1
    if (MasterCty(XMasterCty).EQ.StnCtyO(XOStn)) then
      if (LatO(XOStn).NE.MissVal.AND.LonO(XOStn).NE.MissVal) then
        OriginalLat(XMasterCty) = OriginalLat(XMasterCty) + LatO(XOStn)
        OriginalLon(XMasterCty) = OriginalLon(XMasterCty) + LonO(XOStn)
        OriginalEn (XMasterCty) = OriginalEn (XMasterCty) + 1.0
      end if
    end if 
    if (MasterCty(XMasterCty).EQ.StnCtyO(XOStn).OR.XMasterCty.EQ.NMasterCty) exit
  end do
end do

end subroutine GetFromOriginal

!*******************************************************************************
! initialises program

subroutine Intro

open (99,file="./../../../scratch/log-cleanmeta.dat", &
		status="replace",action="write")

print*
print*, "  > ***** CleanMeta: processes new stn files *****"
print*

NMonth = 12

call date_and_time (CurrentDate, CurrentTime)
Stamp10  = CurrentDate(3:8) // CurrentTime(1:4) 
Stamp20  = adjustr(Stamp10)
StampInt = GetIntFromText(Stamp20)

end subroutine Intro

!*******************************************************************************
! loading accessions file

subroutine LoadingAccessions

AccessFileL="./../../../data/cruts/accession/??????????.hdr"
call GetBatch (AccessFileL,Batch,Silent=1)
NBatch = size(Batch,1)
AccessFileL = Batch(NBatch) ; SubBeg=index(AccessFileL,"/",.TRUE.)
FileDate=AccessFileL((SubBeg+1):(SubBeg+6)) 
FileTime=AccessFileL((SubBeg+7):(SubBeg+10))

call LoadCTS (TrashInfo,StnLocalA,StnNameA,StnCtyA,Code=StnOldA,OldCode=StnNewA, &
		Silent=1,Lat=LatA,Lon=LonA,Elv=ElvA,HeadOnly=1,CallFile=AccessFileL)
NAStn = size(StnOldA,1)

print "(a,2(a2,a1),a2,a4,a2,a1,a2,a,i7,a)", "   > Access file on ", &
		FileDate(5:6),".",FileDate(3:4),".",FileDate(1:2)," at ", &
		FileTime(1:2),":",FileTime(3:4)," has ",NAStn," stns"
deallocate (TrashInfo,Batch,stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadingAccessions: Dealloc #####"

end subroutine LoadingAccessions

!*******************************************************************************
! load master file

subroutine LoadingMaster

MasterFileL="./../../../data/cruts/master/??????????.hdr"
call GetBatch (MasterFileL,Batch,Silent=1)
NBatch = size(Batch,1)
MasterFileL = Batch(NBatch) ; SubBeg=index(MasterFileL,"/",.TRUE.)
FileDate=MasterFileL((SubBeg+1):(SubBeg+6)) 
FileTime=MasterFileL((SubBeg+7):(SubBeg+10))

call LoadCTS (TrashInfo,StnLocalM,StnNameM,StnCtyM,Code=StnNewM,Lat=LatM,Lon=LonM,Elv=ElvM, &
			HeadOnly=1,CallFile=MasterFileL,Extra=NOStn,Silent=1)
NMStn = size(StnNewM,1)

print "(a,2(a2,a1),a2,a4,a2,a1,a2,a,i7,a)", "   > Master file at ", &
		FileDate(5:6),".",FileDate(3:4),".",FileDate(1:2)," at ", &
		FileTime(1:2),":",FileTime(3:4)," has ",(NMStn-NOStn)," stns."

deallocate (TrashInfo,Batch,stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadingMaster: Dealloc #####"

end subroutine LoadingMaster

!*******************************************************************************
! load original data file

subroutine LoadingOriginal

if (QOrigCode.EQ.0) then		! code in main code column
  call LoadCTS (TrashInfo,StnLocalO,StnNameO,StnCtyO,Code=StnOldO, &
    			Lat=LatO,Lon=LonO,Elv=ElvO,Data=DataO, &
			YearAD=YearADO,CallFile=OrigFileL,Silent=1)
  if (QDigits.EQ.6) where (StnOldO.GT.0) StnOldO = StnOldO * 10
else					! code in 'grid-box' column
  call LoadCTS (TrashInfo,StnLocalO,StnNameO,StnCtyO,OldCode=StnOldO, &
    			Lat=LatO,Lon=LonO,Elv=ElvO,Data=DataO, &
			YearAD=YearADO,CallFile=OrigFileL,Silent=1)
end if

deallocate (TrashInfo,stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadingOriginal: Dealloc 1 #####"

NOStn = size(StnOldO,1) ; NOYear = size(DataO,1)
print "(a,i7,2(a,i4))", "   > Original file has ", NOStn, " stns for ", &
			YearADO(1), "-", YearADO(NOYear)

if (QOrigSrc.EQ.1) then
  call LoadCTS (TrashInfo,TrashLocal,TrashName,TrashCty,Data=DataSrcO, &
			CallFile=OrigSrcFileL,Silent=1)
  deallocate (TrashInfo,TrashLocal,TrashName,TrashCty,stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadingOriginal: Dealloc 2 #####"
  
  MinusSrc = count(DataO.NE.DataMissVal.AND.DataSrcO.EQ.DataMissVal)
  ExtraSrc = count(DataO.EQ.DataMissVal.AND.DataSrcO.NE.DataMissVal)
  
  if (MinusSrc.GT.0) print "(a,i10)", "   > @@@@@ PROBLEM: data without src code:   ", MinusSrc
  if (ExtraSrc.GT.0) print "(a,i10)", "   > @@@@@ PROBLEM: src codes without datum: ", ExtraSrc
  
  if (MinusSrc.GT.0.OR.ExtraSrc.GT.0) then
!    print*, "  > Seeking problem..."		! @@@@@@@@@@@@@@@@@@@
    
    do XOStn=1,NOStn
      do XOYear=1,NOYear
        do XMonth=1,NMonth
	  if      (DataO   (XOYear,XMonth,XOStn).NE.DataMissVal.AND. &
	           DataSrcO(XOYear,XMonth,XOStn).EQ.DataMissVal) then
!	    print "(a,3i7)", "SRC ",StnOldO(XOStn),YearADO(XOYear),XMonth	! @@@@@@@@@@@@
	    write (99,"(a,3i7)"), "SRC ",StnOldO(XOStn),YearADO(XOYear),XMonth
	  else if (DataO   (XOYear,XMonth,XOStn).EQ.DataMissVal.AND. &
	           DataSrcO(XOYear,XMonth,XOStn).NE.DataMissVal) then
!	    print "(a,3i7)", "DAT ",StnOldO(XOStn),YearADO(XOYear),XMonth	! @@@@@@@@@@@@
	    write (99,"(a,3i7)"), "DAT ",StnOldO(XOStn),YearADO(XOYear),XMonth
	  end if
	end do
      end do
    end do
  end if
else
  allocate (DataSrcO(NOYear,NMonth,NOStn),stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: LoadingOriginal: Alloc 3 #####"
  DataSrcO=DataMissVal
  do XOYear = 1, NOYear
    do XMonth = 1, NMonth
      do XOStn = 1, NOStn
        if (DataO(XOYear,XMonth,XOStn).NE.DataMissVal) &
        		DataSrcO(XOYear,XMonth,XOStn) = OrigSrcCode
      end do
    end do
  end do
end if

end subroutine LoadingOriginal

!*******************************************************************************
! allows manual changes to metadata

subroutine ManualMeta

print*, "  > Manually change station meta information."
do
  print*, "  > Enter a station code (0=exit): "
  do
    read (*,*,iostat=ReadStatus), Code
    if (ReadStatus.LE.0) exit
  end do
  if (Code.NE.0) then
    XOStn = 0
    do
        XOStn = XOStn + 1
        if (XOStn.EQ.NOStn.AND.StnOldO(XOStn).NE.Code) Code = -1
        if (StnOldO(XOStn).EQ.Code) exit
    end do
      
    if (Code.NE.-1) then
      print "(a,i8,2f8.2,f8.1,x,a20,x,a13)", "   > ", StnOldO(XOStn),LatO(XOStn), &
      		LonO(XOStn),ElvO(XOStn),StnNameO(XOStn),StnCtyO(XOStn)
      print*, "  > Alter: code(=0), lat(=1), lon(=2), elv(=3) stn-name(=4), cty-name(=5): "
      do
        read (*,*,iostat=ReadStatus), QAlter
        if (ReadStatus.LE.0.AND.QAlter.GE.0.AND.QAlter.LE.5) exit
      end do
      if      (QAlter.EQ.0) then
        print*, "  > Enter the new station code: "
        do
          read (*,*,iostat=ReadStatus), StnOldO(XOStn)
          if (ReadStatus.LE.0) exit
        end do
      else if (QAlter.GE.1.AND.QAlter.LE.3) then
        print*, "  > Enter the new real value: "
        do
          read (*,*,iostat=ReadStatus), RealVal
          if (ReadStatus.LE.0) exit
        end do
        if (QAlter.EQ.1.AND.((RealVal.GE. -90.AND.RealVal.LE. 90).OR.RealVal.EQ.MissVal)) &
        	 	 LatO(XOStn)=RealVal
        if (QAlter.EQ.2.AND.((RealVal.GE.-180.AND.RealVal.LE.180).OR.RealVal.EQ.MissVal)) &
        		 LonO(XOStn)=RealVal
        if (QAlter.EQ.3) ElvO(XOStn)=RealVal
      else if (QAlter.EQ.4) then
        print*, "  > Enter the new station name: "
        do
          read (*,*,iostat=ReadStatus), StnNameO(XOStn)
          if (ReadStatus.LE.0) exit
        end do
      else if (QAlter.EQ.5) then
        if (associated(MasterCty)) then
          print*, "  > Select the new country (>0: see cty/master.txt): "
          do
            read (*,*,iostat=ReadStatus), XMasterCty
            if (ReadStatus.LE.0.AND.XMasterCty.GE.1.AND.XMasterCty.LE.NMasterCty) exit
          end do
          StnCtyO(XOStn) = MasterCty(XMasterCty)
        else
          print*, "  > Enter the new country name: "
          do
            read (*,*,iostat=ReadStatus), StnCtyO(XOStn)
            if (ReadStatus.LE.0) exit
          end do
        end if
      end if
      print "(a,i8,2f8.2,f8.1,x,a20,x,a13)", "   > ", StnOldO(XOStn),LatO(XOStn),&
      			LonO(XOStn),ElvO(XOStn),StnNameO(XOStn),StnCtyO(XOStn)
    end if
  end if
  if (Code.EQ.0) exit
end do

end subroutine ManualMeta

!*******************************************************************************
! prepares for saving

subroutine PrepareSave

print*, "  > The cleaned file goes in ./../../../data/cruts/clean/ ..."
SubBeg = index(OrigFileL,"/",.TRUE.)
SubLen = len_trim(OrigFileL)
GivenFile = "./../../../data/cruts/clean" // OrigFileL(SubBeg:SubLen)
OrigFileS = SavePath (GivenFile,".cts")

SubBeg = index(OrigFileS,".cts")
OrigSrcFileS = OrigFileS(1:SubBeg) // "src"

end subroutine PrepareSave

!*******************************************************************************
! saves original .cts file, with metadata tidied up

subroutine SavingOriginal

call QuickSort (Ints=StnOldO,Order=Order)

call MakeStnInfo (Info,StnOldO,StnOldO,LatO,LonO,ElvO,1,YearAD=YearADO,Data=DataO,Silent=1)
call SaveCTS (Info,StnLocalO,StnNameO,StnCtyO,Data=DataO,YearAD=YearADO, &
		CallFile=OrigFileS,Silent=1,Order=Order)		
call SaveCTS (Info,StnLocalO,StnNameO,StnCtyO,Data=DataSrcO,YearAD=YearADO, &
		CallFile=OrigSrcFileS,Silent=1,Order=Order)		

deallocate (Order,Info,stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: SavingOriginal: Dealloc 1 #####"

end subroutine SavingOriginal

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

subroutine SeekDegMinSection

Lower=0 ; SuspectBeg=0 ; SuspectEnd=0
do XOStn = 1, NOStn
  if (LatO(XOStn).NE.MissVal.AND.LatO(XOStn).NE.0.0 .AND. &
      LonO(XOStn).NE.MissVal.AND.LonO(XOStn).NE.0.0) then
    if (abs(mod(LatO(XOStn),1.0)).LT.0.6.AND.abs(mod(LonO(XOStn),1.0)).LT.0.6) then
      if (Lower.LT. 5) Lower=Lower+1
    else 
      if (Lower.GT. 0) Lower=Lower-1
    end if
    
    if      (SuspectBeg.EQ.0) then
      if (Lower.GE.5) SuspectBeg=XOStn-4
    else if (Lower.LE.0) then
      SuspectEnd=XOStn-4
    else if (XOstn.EQ.NOStn) then
      SuspectEnd=NOStn
    end if
    
    if (SuspectEnd.GT.0) then
      print "(2(a,i7))", "   > @@@@@ CHECK stns between ", StnOldO(SuspectBeg), &
      			 " and ", StnOldO(SuspectEnd)
      SuspectBeg=0 ; SuspectEnd=0 ; Lower=0
    end if
  end if
end do

end subroutine SeekDegMinSection

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

subroutine SeekDegMinMaster

allocate (Check(NOStn),stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: SeekDegMinMaster: alloc #####"
Check=.FALSE. ; Log1=0 ; Log2=0 ; Log3=0 ; Log4=0

do XOStn = 1, NOStn
  if (LatO(XOStn).NE.MissVal.AND.LatO(XOStn).NE.0.0 .AND. &
      LonO(XOStn).NE.MissVal.AND.LonO(XOStn).NE.0.0) then
   if (abs(mod(LatO(XOStn),1.0)).LT.0.6.AND.abs(mod(LonO(XOStn),1.0)).LT.0.6) then

    XAStn=0 ; Log1=Log1+1
    do
      XAStn=XAStn+1
      if (StnOldA(XAStn).EQ.StnOldO(XOStn)) then
        Distance = GetDistance (LatA(XAStn),LonA(XAStn),LatO(XOStn),LonO(XOStn))
	Log2=Log2+1
	
	if (Distance.NE.MissVal.AND.Distance.LT.40) then

	  XMStn=0 ; Log3=Log3+1
	  do
	    XMStn=XMstn+1
	    if (StnNewM(XMStn).EQ.StnNewA(XAStn).AND. &
	    	LatM(XMStn).NE.MissVal.AND.LonM(XMStn).NE.MissVal) then
	      Log4=Log4+1
	      CurrentKM = GetDistance (LatM(XMStn),LonM(XMStn),LatO(XOStn),LonO(XOStn))
	      DecimalKM = GetDistance (LatM(XMStn),LonM(XMStn), &
	      			(LatO(XOStn)+(0.66666*mod(LatO(XOStn),1.0))), &
				(LonO(XOStn)+(0.66666*mod(LonO(XOStn),1.0))) )
						! @@@@@@@@@@@@@@@@@@@@@@@
	      write (99,"(2i8,6f8.2)"), StnOldO(XOStn),StnNewM(XMStn),CurrentKM,DecimalKM, &
	      		LatO(XOStn),LonO(XOStn),LatM(XMStn),LonM(XMStn)
	      
	      if (CurrentKM.GT.DecimalKM) Check(XOStn)=.TRUE.
	    end if
	    if (XMstn.EQ.NMStn) exit
	  end do

	end if
      end if
      if (StnOldA(XAStn).GT.StnOldO(XOStn)) exit
      if (XAStn.EQ.NAStn) exit
    end do
    
   end if
  end if
end do

NSuspect = count(Check)
! print "(4i10)", Log1,Log2,Log3,Log4		! @@@@@@@@@@@@@@@@@@@@@@@@
print "(a,i10)", "   > @@@@@ Decimalise from master file: ", NSuspect
call ConvertDecimal

deallocate (Check,stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: SeekDegMinMaster: dealloc #####"

end subroutine SeekDegMinMaster

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

subroutine SeekDegMinSrc

allocate (Source(NOStn),Check(NOStn),stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: SeekDegMinSrc: alloc #####"
Source=MissVal ; Check=.TRUE.

do XOStn=1,NOStn
  do XOYear=1,NOYear
    if (Source(XOStn).NE.MultiSource) then
      do XMonth=1,NMonth
        if (DataSrcO(XOYear,XMonth,XOStn).NE.DataMissVal) then
	  if (Source(XOStn).EQ.MissVal) then
	    Source(XOStn) = DataSrcO(XOYear,XMonth,XOStn)
	  else if (DataSrcO(XOYear,XMonth,XOStn).NE.Source(XOStn)) then
	    Source(XOStn) = MultiSource
	  end if
	end if
      end do
    end if
  end do
end do

XSource=0
do
  XSource=XSource+1
  
  if (Source(XSource).NE.MissVal.AND.Check(XSource)) then
    Lower=0 ; Upper=0
    
    do XOStn = XSource, NOStn
      if (Source(XOStn).EQ.Source(XSource)) then
        Check(XOStn)=.FALSE.
	if (LatO(XOStn).NE.MissVal.AND.LatO(XOStn).NE.0.0 .AND. &
            LonO(XOStn).NE.MissVal.AND.LonO(XOStn).NE.0.0) then
          if (abs(mod(LatO(XOStn),1.0)).LT.0.6.AND.abs(mod(LonO(XOStn),1.0)).LT.0.6) then
	    Lower=Lower+1
	  else
	    Upper=Upper+1
	  end if
	end if
      end if
    end do
    
    OpTest=Lower/(Lower+Upper)
    OpDiff=abs(OpTest-0.36)
    StdErr=sqrt((OpTest*(1.0-OpTest))/(Lower+Upper))
  
    print "(a,i8,a,3f8.2)", "   > @@@@@ src code ", Source(XSource), " =", &
    			OpDiff/StdErr,Lower,Upper
  end if
  
  if (XSource.EQ.NOStn) exit
end do

do
  print*, "  > Select a src code to convert to decimal (-999=end): "
  read (*,*,iostat=ReadStatus), OpTest
  
  if (ReadStatus.LE.0) then
   if (OpTest.NE.MissVal) then
    Check=.FALSE.
    do XOStn=1,NOStn
      if (Source(XOStn).EQ.OpTest) Check(XOstn)=.TRUE.
    end do
    call ConvertDecimal
   end if
  end if
  
  if (OpTest.EQ.MissVal) exit
end do


deallocate (Source,Check,stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: SeekDegMinSrc: dealloc #####"

end subroutine SeekDegMinSrc

!*******************************************************************************
! provides user with load options and specifications

subroutine SelectOriginal

do
  print*, "  > Select the .cts file to load: "
  read (*,*,iostat=ReadStatus), GivenFile
  if (ReadStatus.LE.0.AND.GivenFile.NE."") exit
end do
OrigFileL = LoadPath (GivenFile,".cts")

do
  print*, "  > Does the file have a parallel .src file (0=no,1=yes) ?"
  read (*,*,iostat=ReadStatus), QOrigSrc
  if (ReadStatus.LE.0.AND.QOrigSrc.GE.0.AND.QOrigSrc.LE.1) exit
end do

if (QOrigSrc.EQ.1) then
  do
   print*, "  > Identify the parallel .src file: "
   read (*,*,iostat=ReadStatus), GivenFile
   if (ReadStatus.LE.0.AND.GivenFile.NE."") exit
  end do
  OrigSrcFileL = LoadPath (GivenFile,".src")
else
  do
   print*, "  > Identify the source code for the file: "
   read (*,*,iostat=ReadStatus), OrigSrcCode
   if (ReadStatus.LE.0) exit
  end do
end if

do
  print*, "  > Is the original 7-digit code in the 'grid box' col? (0=no,1=yes)"
  read (*,*,iostat=ReadStatus), QOrigCode
  if (ReadStatus.LE.0.AND.QOrigCode.GE.0.AND.QOrigCode.LE.1) exit
end do

if (QOrigCode.EQ.0) then
 do
  print*, "  > Does the file have 7 (=7) or 6 (=6) digit station codes (i7) ?"
  read (*,*,iostat=ReadStatus), QDigits
  if (ReadStatus.LE.0.AND.QDigits.GE.6.AND.QDigits.LE.7) exit
 end do
end if

end subroutine SelectOriginal

!*******************************************************************************
! select the climate variable, by suffix

subroutine SelectVariable

SubBeg = index(OrigFileL,".cts")
VariableSuffix = OrigFileL((SubBeg-4):(SubBeg-1))

do
   if (VariableSuffix.EQ."    ") then
    do
      print*, "  > Identify the variable suffix (form:'.???'): "
      read (*,*,iostat=ReadStatus), VariableSuffix
      if (ReadStatus.LE.0) exit
    end do
   end if
  
   call CheckVariSuffix (VariableSuffix,Variable,Factor)
   if (Variable.EQ."") VariableSuffix = "    "
   if (Variable.NE."") print "(2a)", "   > The variable is: ", trim(Variable)
   if (Variable.NE."") exit
end do

if      (VariableSuffix.EQ.".pre") then
  DecayDistance = 450  ; Min=0 ; Max=99900
else if (VariableSuffix.EQ.".tmp") then
  DecayDistance = 1200 ; Min=-2730 ; Max=9000
else if (VariableSuffix.EQ.".dtr") then
  DecayDistance = 750  ; Min=0 ; Max=10000
else if (VariableSuffix.EQ.".cld") then
  DecayDistance = 750  ; Min=0 ; Max=1000
else if (VariableSuffix.EQ.".spc") then		! assumed, on the basis of cld
  DecayDistance = 750  ; Min=0 ; Max=1000
else if (VariableSuffix.EQ.".vap") then
  DecayDistance = 1200 ; Min=0 ; Max=10000
else if (VariableSuffix.EQ.".rhm") then		! assumed, on the basis of vap and tmp
  DecayDistance = 1200 ; Min=0 ; Max=1000
else if (VariableSuffix.EQ.".wet") then
  DecayDistance = 450  ; Min=0 
  Max=(/3100,2900,3100,3000,3100,3000,3100,3100,3000,3100,3000,3100/)
else
  print*, "  > @@@@@ ERROR: SelectVariable: no var @@@@@"
end if

end subroutine SelectVariable

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

end program CleanMeta
