! annbulk.f90
! performs basic operations on .ann files in bulk
! pgf90 -Mstandard -Minfo -fast -Mscalarsse -Mvect=sse -Mflushz 
! 	-o ./../obs/annbulk time.f90 filenames.f90 annfiles.f90 basicfun.f90 
!	./../obs/annbulk.f90

program AnnBulk

use Time
use FileNames
use AnnFiles
use BasicFun

implicit none

!*******************************************************************************
! definitions

real, pointer, dimension (:,:) 	   		:: InnData,MidData,OutData
real, pointer, dimension (:) 	   		:: InnVec,MidVec,OutVec

integer, pointer, dimension (:) 	   	:: YearAD,InnYearAD,MidYearAD,OutYearAD
integer, pointer, dimension (:) 	   	:: Prune

character (len=80), pointer, dimension (:) 	:: InnFile,MidFile,OutFile
character (len= 9), pointer, dimension (:) 	:: Labels,InnLabels,MidLabels,OutLabels

real, parameter	:: MissVal = -999.0

character (len=80), parameter :: Blank = ""

logical :: IfPrune

real :: MagMin,MagMax,OpConstant

integer :: ReadStatus,AllocStat
integer :: QOperation,QOpPerform,QOutMag,QReplicate,QDecPlace,QKeepMiss
integer :: NCol,NInnCol,NMidCol,NOutCol,NYear,NInnYear,NMidYear,NOutYear
integer :: XCol,XInnCol,XMidCol,XOutCol,XYear,XInnYear,XMidYear,XOutYear
integer :: NFile,NMidFile,NPrune
integer :: XFile,XMidFile,XPrune
integer :: InnSubLen,InnSubBeg,InnFileLen,LastSlash,THalf
integer :: InnBeg,InnEnd,MidBeg,MidEnd,BegYearAD,EndYearAD

character (len=80) :: InnSub,OutSub,GivenFile,NameOnly
character (len= 9) :: InnText,MidText,OutText

!*******************************************************************************
! main program

open  (99,file="/tyn1/tim/scratch/log-annbulk.dat",status="replace",action="write")

print*
print*, "  > ***** AnnBulk: performs basic ops on .ann files in bulk *****"
print*

call SelectOp

print*

close (99)

contains

!*******************************************************************************
! main selection

subroutine SelectOp

print*, "  > Select the operation to perform (0=list): "
do
    read (*,*,iostat=ReadStatus), QOperation
    if (QOperation.EQ.0) then
      print*, "  >  7. smooth with Gaussian filter"
      print*, "  >  8. operate .ann on constant"
      print*, "  >  9. operate .ann on .ann"
      print*, "  > 10. prune columns"
      print*, "  > 11. restrict period"
    end if
    if (ReadStatus.LE.0.AND.QOperation.NE.0) exit
end do

if (QOperation.EQ. 7) call SmoothGaussian
if (QOperation.EQ. 8) call OpAnnConstant
if (QOperation.EQ. 9) call OpMultiAnn
if (QOperation.EQ.10) call PruneCols
if (QOperation.EQ.11) call RestrictPeriod

end subroutine SelectOp

!*******************************************************************************
!  7. smooth with Gaussian filter

subroutine SmoothGaussian

print*, "  > Select the periodicity at which to smooth: "
do
	read (*,*,iostat=ReadStatus), THalf
	if (ReadStatus.LE.0 .AND. THalf.GE.1) exit
end do

print*, "  > Replace (=1) or retain (=2) missing values ?"
do
	read (*,*,iostat=ReadStatus), QKeepMiss
	if (ReadStatus.LE.0 .AND. QKeepMiss.GE.1 .AND. QKeepMiss.LE.2) exit
end do

call QueryOutMag
call GetInnFile
call GetOutFile

do XFile = 1, NFile
  call GetNameOnly
  call LoadANN (InnFile(XFile),InnYearAD,InnLabels,InnData)
  
  NCol=size(InnLabels,1) ; NYear=size(InnYearAD,1)
  allocate (OutData(NYear,NCol), &
  	    InnVec(NYear),MidVec(NYear),OutVec(NYear),stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: SmoothGaussian: Alloc #####"
  OutData=MissVal
  
  do XCol=1,NCol
    do XYear=1,NYear
      InnVec(XYear) = InnData(XYear,XCol)
    end do
    
    call GaussSmooth (NYear,THalf,QKeepMiss,InnVec,OutVec,MidVec)
    
    do XYear=1,NYear
      OutData(XYear,XCol) = OutVec(XYear)
    end do
  end do
  
  if (QOutMag.EQ.1) call CheckOutMag
    
  call SaveANN (OutFile(XFile),InnYearAD,InnLabels,OutData,DecPlaceN=QDecPlace)
    
  print "(2a,3(a,i4))", "   > ", trim(NameOnly), &
    		" for", NCol, " in ", InnYearAD(1), "-", InnYearAD(NYear)

  deallocate (OutData,InnVec,MidVec,OutVec,stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: SmoothGaussian: Dealloc #####"
end do

end subroutine SmoothGaussian

!*******************************************************************************
!  8. operate .ann on constant

subroutine OpAnnConstant

print*, "  > Select the op: A/k=1,A*k=2,A-k=3,A+k=4,sqroot(A)=5,A**k=6,abs(A)=7"
do
      read (*,*,iostat=ReadStatus), QOpPerform
      if (ReadStatus.LE.0.AND.QOpPerform.GE.1.AND.QOpPerform.LE.7) exit
end do

if (QOpPerform.NE.5.AND.QOpPerform.NE.7) then
  print*, "  > Select the constant:"
  do
      read (*,*,iostat=ReadStatus), OpConstant
      if (ReadStatus.LE.0) exit
  end do
end if

call QueryOutMag
call GetInnFile
call GetOutFile

do XFile = 1, NFile
  call GetNameOnly
  call LoadANN (InnFile(XFile),YearAD,Labels,InnData)
  
  NCol=size(Labels,1) ; NYear=size(YearAD,1)
  allocate (OutData(NYear,NCol),stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: OpAnnConstant: Alloc #####"
  OutData=MissVal
  
  do XCol=1,NCol
      do XYear=1,NYear
	OutData(XYear,XCol) = OpAwithB (InnData(XYear,XCol), &
					OpConstant,QOpPerform)
      end do
  end do
  
  if (QOutMag.EQ.1) call CheckOutMag
    
  call SaveANN (OutFile(XFile),YearAD,Labels,OutData,DecPlaceN=QDecPlace)
    
  print "(2a,3(a,i4))", "   > ", trim(NameOnly), &
    		" for", NCol, " in ", YearAD(1), "-", YearAD(NYear)

  deallocate (OutData,stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: OpAnnConstant: Dealloc #####"
end do

end subroutine OpAnnConstant

!*******************************************************************************
!  9. operate .ann on .ann

subroutine OpMultiAnn

print*, "  > Select the op: A/B=1,A*B=2,A-B=3,A+B=4,A**B=6"
do
      read (*,*,iostat=ReadStatus), QOpPerform
      if (ReadStatus.LE.0.AND.QOpPerform.GE.1.AND.QOpPerform.LE.7) exit
end do

call QueryOutMag
call GetParallelFiles
call GetOutFile

do XFile = 1, NFile
  call GetMidFileIndex
  call GetNameOnly
  call LoadANN (InnFile(XFile),   InnYearAD,InnLabels,InnData)
  call LoadANN (MidFile(XMidFile),MidYearAD,MidLabels,MidData)
  
  NInnCol=size(InnLabels,1) ; NMidCol=size(MidLabels,1) 
  NOutCol=NInnCol ; if (NMidCol.LT.NOutCol) NOutCol=NMidCol
  
  call CommonVecPer (InnYearAD,MidYearAD,InnBeg,InnEnd,MidBeg,MidEnd)
  
  if (InnBeg.NE.MissVal) then
    NOutYear=InnEnd-InnBeg+1
    
    allocate (OutYearAD(NOutYear),OutLabels(NOutCol),OutData(NOutYear,NOutCol), &
    	      stat=AllocStat)
    if (AllocStat.NE.0) print*, "  > ##### ERROR: OpMultiAnn: Alloc #####"
    OutYearAD=MissVal ; OutLabels="" ; OutData=MissVal
    
    do XOutYear=1,NOutYear
      XInnYear=InnBeg+XOutYear-1
      OutYearAD(XOutYear)=InnYearAD(XInnYear)
    end do
    
    do XOutCol=1,NOutCol
      InnText=trim(adjustl(InnLabels(XOutCol))) 
      MidText=trim(adjustl(MidLabels(XOutCol)))
      OutLabels(XOutCol) = " " // InnText(1:4) // MidText(1:4)
      OutLabels(XOutCol) = adjustr(OutLabels(XOutCol))
    end do
    
    do XOutCol=1,NOutCol
      do XOutYear=1,NOutYear
        XInnYear=InnBeg+XOutYear-1 ; XMidYear=MidBeg+XOutYear-1
	OutData(XOutYear,XOutCol) = OpAwithB (InnData(XInnYear,XOutCol), &
					MidData(XMidYear,XOutCol),QOpPerform)
      end do
    end do
    
    NYear=NOutYear ; NCol=NOutCol
    if (QOutMag.EQ.1) call CheckOutMag
    
    call SaveANN (OutFile(XFile),OutYearAD,OutLabels,OutData,DecPlaceN=QDecPlace)
    
    print "(2a,5(a,i4))", "   > ", trim(NameOnly), &
    		" for", NOutCol, " from", NInnCol, ",", NMidCol, &
		" in ", OutYearAD(1), "-", OutYearAD(NOutYear)

    deallocate (OutYearAD,OutLabels,OutData,stat=AllocStat)
    if (AllocStat.NE.0) print*, "  > ##### ERROR: OpMultiAnn: Dealloc #####"
  else
    print "(3a)", "   > ", trim(NameOnly), " has no common period"
  end if
end do

end subroutine OpMultiAnn

!*******************************************************************************
! 10. prune columns

subroutine PruneCols

print*, "  > Select the number of columns to prune: "
do
      read (*,*,iostat=ReadStatus), NPrune
      if (ReadStatus.LE.0.AND.NPrune.GE.1) exit
end do

allocate (Prune(NPrune),stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: PruneCols: Alloc #####"

do XPrune=1,NPrune
  print "(a,i2)", "   > Select a column to prune: ", XPrune
  do
      read (*,*,iostat=ReadStatus), Prune(XPrune)
      if (ReadStatus.LE.0.AND.Prune(XPrune).GE.1) exit
  end do
end do

call GetInnFile
call GetOutFile

do XFile = 1, NFile
  call GetNameOnly
  call LoadANN (InnFile(XFile),InnYearAD,InnLabels,InnData)
  
  NInnCol=size(InnLabels,1) ; NYear=size(InnYearAD,1) ; NOutCol=NInnCol
  do XInnCol=1,NInnCol
    XPrune=0
    do
      XPrune=XPrune+1
      if (Prune(XPrune).EQ.XInnCol) NOutCol=NOutCol-1
      if (XPrune.EQ.NPrune.OR.Prune(XPrune).EQ.XInnCol) exit
    end do
  end do
  
  allocate (OutData(NYear,NOutCol),OutLabels(NOutCol),stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: PruneCols: Alloc 2 #####"
  OutData=MissVal ; OutLabels=""
  
  XOutCol=0
  do XInnCol=1,NInnCol
    XPrune=0 ; IfPrune=.FALSE.
    do
      XPrune=XPrune+1
      if (Prune(XPrune).EQ.XInnCol) IfPrune=.TRUE.
      if (XPrune.EQ.NPrune.OR.Prune(XPrune).EQ.XInnCol) exit
    end do
    
    if (.NOT.IfPrune) then
      XOutCol=XOutCol+1
      OutLabels(XOutCol)=InnLabels(XInnCol)
      do XYear=1,NYear
        OutData(XYear,XOutCol)=InnData(XYear,XInnCol)
      end do
    end if
  end do
  
  call SaveANN (OutFile(XFile),InnYearAD,OutLabels,OutData,DecPlaceN=QDecPlace)
    
  print "(2a,4(a,i4))", "   > ", trim(NameOnly), &
    		" for", NOutCol, " of", NInnCol, " in ", InnYearAD(1), "-", InnYearAD(NYear)

  deallocate (OutData,OutLabels,stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: PruneCols: Dealloc 2 #####"
end do

deallocate (Prune,stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: PruneCols: Dealloc #####"

end subroutine PruneCols

!*******************************************************************************
! 11. restrict period

subroutine RestrictPeriod

print*, "  > Select the year AD beg,end (-999 for no limit): "
do
      read (*,*,iostat=ReadStatus), BegYearAD,EndYearAD
      if (ReadStatus.LE.0) exit
end do

call GetInnFile
call GetOutFile

do XFile = 1, NFile
  call GetNameOnly
  call LoadANN (InnFile(XFile),InnYearAD,Labels,InnData)
  
  NInnYear=size(InnYearAD,1) ; NCol=size(Labels,1)
  
  if      (BegYearAD.EQ.MissVal.AND.EndYearAD.NE.MissVal) then
    if (InnYearAD(1).LE.EndYearAD) 		BegYearAD=InnYearAD(1)
  else if (BegYearAD.NE.MissVal.AND.EndYearAD.EQ.MissVal) then
    if (InnYearAD(NInnYear).GE.BegYearAD) 	EndYearAD=InnYearAD(NInnYear)
  else if (BegYearAD.EQ.MissVal.AND.EndYearAD.EQ.MissVal) then
    BegYearAD=InnYearAD(1) ; EndYearAD=InnYearAD(NInnYear)
  end if
  
  if (BegYearAD.NE.MissVal.AND.EndYearAD.NE.MissVal) then
    NMidYear=EndYearAD-BegYearAD+1
    allocate (MidYearAD(NMidYear),stat=AllocStat)
    if (AllocStat.NE.0) print*, "  > ##### ERROR: RestrictPeriod: Alloc 1 #####"
    do XMidYear=1,NMidYear
      MidYearAD(XMidYear) = BegYearAD + XMidYear - 1
    end do
    call CommonVecPer (InnYearAD,MidYearAD,InnBeg,InnEnd,MidBeg,MidEnd)
    deallocate (MidYearAD,stat=AllocStat)
    if (AllocStat.NE.0) print*, "  > ##### ERROR: RestrictPeriod: Dealloc 1 #####"
  else
    InnBeg=MissVal
  end if
  
  if (InnBeg.NE.MissVal) then
    NOutYear=MidEnd-MidBeg+1
    allocate (OutData(NOutYear,NCol),OutYearAD(NOutYear), stat=AllocStat)
    if (AllocStat.NE.0) print*, "  > ##### ERROR: RestrictPeriod: Alloc 2 #####"
    
    do XOutYear=1,NOutYear
      OutYearAD(XOutYear)=BegYearAD+MidBeg+XOutYear-2
    end do
      
    do XOutYear=1,NOutYear
      XInnYear=InnBeg+XOutYear-1
      do XCol=1,NCol
        OutData(XOutYear,XCol)=InnData(XInnYear,XCol)
      end do
    end do
    
    call SaveANN (OutFile(XFile),OutYearAD,Labels,OutData,DecPlaceN=QDecPlace)
    
    print "(2a,4(a,i4))", "   > ", trim(NameOnly), &
    		" has ",  OutYearAD(1), "-", OutYearAD(NOutYear), &
		" from ", InnYearAD(1), "-", InnYearAD(NInnYear)

    deallocate (OutData,OutYearAD, stat=AllocStat)
    if (AllocStat.NE.0) print*, "  > ##### ERROR: RestrictPeriod: Dealloc 2 #####"
  else
    print "(3a)", "   > ", trim(NameOnly), " has no common period"
  end if
end do

end subroutine RestrictPeriod

!*******************************************************************************
! query whether to check output magnitudes

subroutine QueryOutMag

print*, "  > Restrict output magnitudes (0=no,1=yes) ?"
do
      read (*,*,iostat=ReadStatus), QOutMag
      if (ReadStatus.LE.0.AND.QOutMag.GE.0.AND.QOutMag.LE.1) exit
end do
if (QOutMag.EQ.1) then
  print*, "  > Magnitudes smaller than X are set to X. Enter X."
  do
      read (*,*,iostat=ReadStatus), MagMin
      if (ReadStatus.LE.0.AND.MagMin.GE.0) exit
  end do
  
  print*, "  > Magnitudes larger  than Y are set to Y. Enter Y."
  do
      read (*,*,iostat=ReadStatus), MagMax
      if (ReadStatus.LE.0.AND.MagMax.GE.0) exit
  end do  
end if

end subroutine QueryOutMag

!*******************************************************************************
! check output magnitudes

subroutine CheckOutMag

do XCol=1,NOutCol
  do XYear=1,NOutYear
    if (OutData(XYear,XCol).NE.MissVal) then
      if (abs(OutData(XYear,XCol)).LT.MagMin) &
      		OutData(XYear,XCol) = sign(MagMin,OutData(XYear,XCol))
      if (abs(OutData(XYear,XCol)).GT.MagMax) &
      		OutData(XYear,XCol) = sign(MagMax,OutData(XYear,XCol))
    end if
  end do
end do

end subroutine CheckOutMag

!*******************************************************************************
! get parallel files

subroutine GetParallelFiles

print*, "  > Select OLD files 'A'. There must be a common substring."
call GetBatch (Blank,InnFile)
NFile  = size (InnFile, 1)

print*, "  > Select OLD files 'B'. There must be a common substring."
do
  call GetBatch (Blank,MidFile)
  NMidFile = size(MidFile, 1)
  if (NFile.NE.NMidFile) then
    if (mod(real(NFile),real(NMidFile)).EQ.0) then
      print*, "  > Replicate the files to match A ? (0=no,1,2=yes)"
      print*, "  >   1=(a:a,n+a,...) 2=(a:n*a+1,n*a+2,...)"
      do
        read (*,*,iostat=ReadStatus), QReplicate
        if (ReadStatus.LE.0.AND.QReplicate.GE.0.AND.QReplicate.LE.2) exit
      end do
    end if

    if (QReplicate.EQ.0) then
      print*, "  > Incorrect number of files. Try again."
      deallocate (MidFile, stat=AllocStat)
      if (AllocStat.NE.0) print*, "  > ##### ERROR: GetParallelFiles: Dealloc #####"
    end if
  end if
  if (NFile.EQ.NMidFile.OR.QReplicate.GT.0) exit
end do

end subroutine GetParallelFiles

!*******************************************************************************
! get input file vector

subroutine GetInnFile

print*, "  > Select the OLD files. There must be a common substring."
call GetBatch (Blank,InnFile)
NFile  = size (InnFile, 1)

end subroutine GetInnFile

!*******************************************************************************
! get output file vector

subroutine GetOutFile

print*, "  > Enter the substring in the OLD A files to alter in the NEW files: "
do
	read (*,*,iostat=ReadStatus), InnSub
	if (ReadStatus.GT.0) then	
			print*, "  > Bad format. Try again."
	else if (InnSub.EQ."") then
			print*, "  > Blank not permitted. Try again."
	end if
	if (ReadStatus.LE.0.AND.InnSub.NE."") exit
end do
InnSub=trim(adjustl(InnSub))

print "(3a)", "   > Enter the substring in the NEW files to replace '", trim(InnSub), "':"
do
	read (*,*,iostat=ReadStatus), OutSub
	if (ReadStatus.GT.0) then	
			print*, "  > Bad format. Try again."
	else if (OutSub.EQ."") then
			print*, "  > Blank not permitted. Try again."
	end if
	if (ReadStatus.LE.0.AND.OutSub.NE."") exit
end do

InnSubLen = len(trim(InnSub))
  
allocate (OutFile (NFile), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: GloBulk: OutFile: Allocation failure #####"
OutFile = ""

do XFile = 1, NFile
      GivenFile = InnFile(XFile)
      InnSubBeg  = index(GivenFile,InnSub(1:InnSubLen))
      InnFileLen = len(adjustl(trim(GivenFile)))
      
      OutFile(XFile) = GivenFile(1:(InnSubBeg-1)) // trim(adjustl(OutSub)) // &
      		GivenFile((InnSubBeg+InnSubLen):InnFileLen)
end do

print*, "  > Enter the decimal places to save (1-3):"
do
      read (*,*,iostat=ReadStatus), QDecPlace
      if (ReadStatus.LE.0.AND.QDecPlace.GE.1.AND.QDecPlace.LE.3) exit
end do

end subroutine GetOutFile

!*******************************************************************************
! get 'mid' file index, depending on replication

subroutine GetMidFileIndex

if      (QReplicate.EQ.0) then		! work out 'mid' file index
    XMidFile = XFile
else if (QReplicate.EQ.1) then
    XMidFile = nint(mod(real(XFile),real(NMidFile)))
    if (XMidFile.EQ.0) XMidFile = NMidFile
else if (QReplicate.EQ.2) then
    if (XFile.EQ.1) XMidFile=0
    if (mod(real(XFile),(real(NFile)/real(NMidFile))).EQ.1) XMidFile=XMidFile+1
end if

GivenFile = OutFile(XFile)
LastSlash = index(GivenFile,"/",.TRUE.)
NameOnly  = trim(GivenFile((LastSlash+1):80))
  
end subroutine GetMidFileIndex

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

subroutine GetNameOnly

GivenFile = OutFile(XFile)
LastSlash = index(GivenFile,"/",.TRUE.)
NameOnly  = trim(GivenFile((LastSlash+1):80))
  
end subroutine GetNameOnly

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

end program AnnBulk
