# pragma once
# include "ParseArgs.h"
# include "basedata.h"
# include "mdatetime.h"
# include "mregex.h"
# include "traits.h"
# include "uvdata.h"
# include <array>
# include <utility>
using michlib : : MDateTime ;
# if defined GENACTIONLIST
# define ADD_ACTION(actclass, actname, suptest, ...) ADD ACTION CLASS: actclass
# else
# define ADD_ACTION(actclass, actname, suptest, ...) \
class Action # # actclass __VA_OPT__ ( : protected ) __VA_ARGS__ \
{ \
public : \
static constexpr const char * name = # actname ; \
template < class Source > static constexpr bool IsSupported = ( suptest ) ; \
template < class Source > static MString DoAction ( const CLArgs & args , Source & data ) ; \
} ;
# endif
enum class VelSource
{
AUTOSEL ,
STANDART ,
GEOSTROPHIC ,
GEOSTROPHICSSH
} ;
struct TimeData
{
static constexpr auto stepunits = std : : to_array < uint > ( { 3600 * 24 , 3600 , 60 , 1 } ) ;
static constexpr std : : array < const std : : string , stepunits . size ( ) > stepnames { " days " , " hours " , " minutes " , " seconds " } ;
MDateTime refdate ;
size_t unitind ;
std : : vector < michlib : : int4 > steps ;
template < class D > TimeData ( const D & data , const TIndex & tindexes ) : refdate ( ) , unitind ( stepunits . size ( ) ) , steps ( tindexes . size ( ) )
{
if ( steps . size ( ) = = 0 ) return ;
refdate = data . Time ( tindexes [ 0 ] ) ;
unitind = 0 ;
for ( size_t i = 0 ; i < steps . size ( ) ; i + + )
{
auto delta = data . Time ( tindexes [ i ] ) - refdate ;
while ( delta . Seconds ( ) % stepunits [ unitind ] ! = 0 ) unitind + + ;
}
for ( size_t i = 0 ; i < steps . size ( ) ; i + + )
{
auto delta = data . Time ( tindexes [ i ] ) - refdate ;
steps [ i ] = michlib : : int_cast < decltype ( steps ) : : value_type > ( delta . Seconds ( ) / stepunits [ unitind ] ) ;
}
}
MString UnitName ( ) const
{
MString out = stepnames [ unitind ] . c_str ( ) ;
return out + " since " + refdate . ToString ( ) ;
}
} ;
template < class D > size_t GetTIndex ( const D & data , const MDateTime & t )
{
size_t nt = data . NTimes ( ) ;
if ( t < = data . Time ( 0 ) ) return 0 ;
if ( t > = data . Time ( nt - 1 ) ) return nt - 1 ;
for ( size_t i = 0 ; i < nt - 1 ; i + + )
if ( t > = data . Time ( i ) & & t < = data . Time ( i + 1 ) ) return ( t - data . Time ( i ) < = data . Time ( i + 1 ) - t ) ? i : ( i + 1 ) ;
return 0 ;
}
template < class D > std : : pair < TIndex , MString > GetTIndexes ( const D & data , const CLArgs & args , michlib_internal : : ParameterListEx & pars )
{
TIndex tindexes ;
if ( args . contains ( " time " ) & & ( args . contains ( " timeb " ) | | args . contains ( " timee " ) ) )
return { tindexes , " Time must be set via time parameter or timeb and timee parameter but not via both " } ;
if ( ! ( args . contains ( " time " ) | | ( args . contains ( " timeb " ) & & args . contains ( " timee " ) ) ) ) return { tindexes , " Time must be set via time parameter or timeb and timee parameter " } ;
if ( args . contains ( " time " ) )
{
MString regex = args . at ( " time " ) ;
MDateTime time ;
if ( time . FromString ( regex ) ) // Time, not regex
tindexes . push_back ( GetTIndex ( data , time ) ) ;
else if ( regex = = " BEGIN " | | regex = = " BEG " | | regex = = " FIRST " ) // First time
tindexes . push_back ( 0 ) ;
else if ( regex = = " END " | | regex = = " LAST " ) // Last time
tindexes . push_back ( data . NTimes ( ) - 1 ) ;
else // Regular expression
{
michlib : : RegExpSimple reg ( regex . Buf ( ) ) ;
if ( reg . Compile ( ) ! = 0 ) return { tindexes , " Bad regular expression: " + regex } ;
for ( size_t i = 0 ; i < data . NTimes ( ) ; i + + )
{
MString date = data . Time ( i ) . ToString ( ) ;
if ( reg . Match ( date . Buf ( ) ) ) tindexes . push_back ( i ) ;
}
}
if ( tindexes . size ( ) = = 0 ) return { tindexes , " There are no times matching the regular expression: " + regex } ;
if ( tindexes . size ( ) = = 1 )
pars . SetParameter ( " time " , data . Time ( tindexes [ 0 ] ) . ToTString ( ) ) ;
else
pars . SetParameter ( " timeregex " , args . at ( " time " ) ) ;
}
else
{
MString tb = args . at ( " timeb " ) ;
MString te = args . at ( " timee " ) ;
MDateTime b ( tb ) , e ( te ) ;
auto nt = data . NTimes ( ) ;
if ( tb = = " BEGIN " | | tb = = " BEG " ) b = data . Time ( 0 ) ;
if ( te = = " LAST " | | te = = " END " ) e = data . Time ( nt - 1 ) ;
const MDateTime & beg = ( b < e ) ? b : e ;
const MDateTime & end = ( b > e ) ? b : e ;
if ( beg > data . Time ( nt - 1 ) ) return { tindexes , " Begin time " + b . ToTString ( ) + " is greater then end time in the dataset " + data . Time ( nt - 1 ) . ToTString ( ) } ;
if ( end < data . Time ( 0 ) ) return { tindexes , " End time " + e . ToTString ( ) + " is lesser then begin time in the dataset " + data . Time ( 0 ) . ToTString ( ) } ;
for ( size_t i = 0 ; i < nt ; i + + )
if ( data . Time ( i ) > = beg & & data . Time ( i ) < = end ) tindexes . push_back ( i ) ;
if ( tindexes . size ( ) = = 0 ) return { tindexes , " There are no times between " + b . ToString ( ) + " and " + e . ToString ( ) } ;
pars . SetParameter ( " timeb " , b . ToTString ( ) ) ;
pars . SetParameter ( " timee " , e . ToTString ( ) ) ;
}
std : : ranges : : sort ( tindexes , [ & data = std : : as_const ( data ) ] ( size_t a , size_t b ) { return data . Time ( a ) < data . Time ( b ) ; } ) ;
return { tindexes , " " } ;
}
template < class D > std : : vector < ReadType < D > > Read ( const D & data , const std : : vector < MString > & vnames , const BaseParameters * p , size_t ind )
{
using RT = ReadType < D > ;
std : : vector < RT > out ;
michlib : : message ( " Time: " + data . Time ( ind ) . ToTString ( ) ) ;
std : : map < MString , RT > cache ;
for ( const auto & vname : vnames )
{
bool res ;
if constexpr ( ReadPSupported < D > )
res = data . Read ( vname , cache , p , ind ) ;
else if constexpr ( ReadSupported < D > )
res = data . Read ( vname , cache , ind ) ;
if ( ! res ) return out ;
}
for ( size_t i = 0 ; i < vnames . size ( ) ; i + + ) out . emplace_back ( std : : move ( cache [ vnames [ i ] ] ) ) ;
return out ;
}
template < class D > std : : vector < ReadType < D > > Read ( const D & data , const std : : vector < MString > & vnames , const BaseParameters * p , const TIndex & tindex )
{
using RT = ReadType < D > ;
size_t ind ;
std : : vector < RT > out ;
if ( tindex . size ( ) = = 1 )
return Read ( data , vnames , p , tindex [ 0 ] ) ;
else
{
std : : vector < Averager < RT > > aver ( vnames . size ( ) ) ;
for ( size_t i = 0 ; i < tindex . size ( ) ; i + + )
{
ind = tindex [ i ] ;
michlib : : message ( " Time: " + data . Time ( ind ) . ToTString ( ) ) ;
std : : map < MString , RT > cache ;
for ( const auto & vname : vnames )
{
bool res ;
if constexpr ( ReadPSupported < D > )
res = data . Read ( vname , cache , p , ind ) ;
else if constexpr ( ReadSupported < D > )
res = data . Read ( vname , cache , ind ) ;
if ( ! res ) return out ;
}
for ( size_t i = 0 ; i < vnames . size ( ) ; i + + ) aver [ i ] . Add ( std : : move ( cache [ vnames [ i ] ] ) ) ;
}
for ( size_t i = 0 ; i < vnames . size ( ) ; i + + ) out . emplace_back ( std : : move ( aver [ i ] . Div ( ) ) ) ;
return out ;
}
return out ;
}
template < class D > UVData < ReadType < D > > ReadUV ( const D & data , const BaseParameters * p , size_t ind , VelSource usegeo )
{
using RT = ReadType < D > ;
using UV = UVData < RT > ;
michlib : : message ( " Time: " + data . Time ( ind ) . ToTString ( ) ) ;
std : : map < MString , RT > cache ;
bool res = false ;
MString uname , vname ;
switch ( usegeo )
{
case ( VelSource : : AUTOSEL ) :
{
if ( data . CheckVar ( " u " ) = = VarPresence : : INTERNAL & & data . CheckVar ( " v " ) = = VarPresence : : INTERNAL )
{
uname = " u " ;
vname = " v " ;
}
else
{
uname = " ugs " ;
vname = " vgs " ;
}
break ;
}
case ( VelSource : : STANDART ) :
{
uname = " u " ;
vname = " v " ;
break ;
}
case ( VelSource : : GEOSTROPHIC ) :
{
uname = " ugs " ;
vname = " vgs " ;
break ;
}
case ( VelSource : : GEOSTROPHICSSH ) :
{
uname = " ugeo " ;
vname = " vgeo " ;
break ;
}
}
if constexpr ( ReadPSupported < D > )
res = data . Read ( uname , cache , p , ind ) & & data . Read ( vname , cache , p , ind ) ;
else if constexpr ( ReadSupported < D > )
res = data . Read ( uname , cache , ind ) & & data . Read ( vname , cache , ind ) ;
if ( ! res ) return UV ( ) ;
return UV ( cache . at ( uname ) , cache . at ( vname ) ) ;
}
template < class D > UVData < ReadType < D > > ReadUV ( const D & data , const BaseParameters * p , const TIndex & tindex , VelSource usegeo )
{
using RT = ReadType < D > ;
using UV = UVData < RT > ;
MString uname , vname ;
switch ( usegeo )
{
case ( VelSource : : AUTOSEL ) :
{
if ( data . CheckVar ( " u " ) = = VarPresence : : INTERNAL & & data . CheckVar ( " v " ) = = VarPresence : : INTERNAL )
{
uname = " u " ;
vname = " v " ;
}
else
{
uname = " ugs " ;
vname = " vgs " ;
}
break ;
}
case ( VelSource : : STANDART ) :
{
uname = " u " ;
vname = " v " ;
break ;
}
case ( VelSource : : GEOSTROPHIC ) :
{
uname = " ugs " ;
vname = " vgs " ;
break ;
}
case ( VelSource : : GEOSTROPHICSSH ) :
{
uname = " ugeo " ;
vname = " vgeo " ;
break ;
}
}
if ( tindex . size ( ) = = 1 )
return ReadUV ( data , p , tindex [ 0 ] , usegeo ) ;
else
{
Averager < UV > out ;
bool ok = true ;
size_t ind ;
for ( size_t i = 0 ; i < tindex . size ( ) ; i + + )
{
if ( ! ok ) break ;
ind = tindex [ i ] ;
michlib : : message ( " Time: " + data . Time ( ind ) . ToTString ( ) ) ;
std : : map < MString , RT > cache ;
bool res = false ;
if constexpr ( ReadPSupported < D > )
res = data . Read ( uname , cache , p , ind ) & & data . Read ( vname , cache , p , ind ) ;
else if constexpr ( ReadSupported < D > )
res = data . Read ( uname , cache , ind ) & & data . Read ( vname , cache , ind ) ;
if ( ! res ) return UV ( ) ;
UV dat ( cache . at ( uname ) , cache . at ( vname ) ) ;
if ( dat )
out . Add ( std : : move ( dat ) ) ;
else
ok = false ;
}
if ( ok ) return out . Div ( ) ;
}
return UV ( ) ;
}
template < class D > std : : pair < struct Region , MString > GetRegion ( const CLArgs & args )
{
struct Region reg ;
if constexpr ( ! ParametersRequiredRegion < D > ) return { reg , " " } ;
if ( ! ( args . contains ( " lonb " ) & & args . contains ( " lone " ) & & args . contains ( " latb " ) & & args . contains ( " late " ) ) ) return { reg , " Region not specified (lonb, lone, latb, late) " } ;
reg . lonb = args . at ( " lonb " ) . ToReal ( ) ;
reg . lone = args . at ( " lone " ) . ToReal ( ) ;
real latb = args . at ( " latb " ) . ToReal ( ) ;
real late = args . at ( " late " ) . ToReal ( ) ;
reg . latb = std : : min ( latb , late ) ;
reg . late = std : : max ( latb , late ) ;
return { reg , " " } ;
}