# ^^^^^^^^^^^^^^^^^
# ====> Class <====
# ^^^^^^^^^^^^^^^^^
abstract type AbstractSesCell end



# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# ====> Class Properties <====
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
columns(::Type{<:AbstractSesCell}) = [:Qty];



# function default_model!(df::Union{DF, GroupedDataFrame{DF}}, cell::Type{C},
#                         init_cell_state::AbstractVecOrMat{NTuple{2, S}} = fill((0.0f0, 0.0f0), (size(df)[1], 1));
#                         kwargs...) where {C<:Ses.AbstractSesCell,
#                                           DF<:AbstractDataFrame,
#                                           S<:AbstractFloat}
#     nrows, ncols = size(df);
#     models = [cell(init_cell_state[idx]; kwargs...) for idx in 1:nrows];
#     insertcols!(df, ncols + 1, :Model => models);
#     return nothing;
# end
# function default_model!(df::Union{DF, GroupedDataFrame{DF}}, cell::Type{C},
#                         modes::String = "mul:mul:mul:mul",
#                         frequencies::String = "moy:woy:doy:dow",
#                         delimiter::String = ":";
#                         init_cell_state::AbstractVecOrMat{NTuple{2, ST}}
#                            = fill((0.0f0, 0.0f0), (size(df)[1], 1)),
#                         kwargs...) where {C<:AbstractSesCell,
#                                           DF<:AbstractDataFrame,
#                                           ST<:AbstractFloat}
#     lwr_modes = lowercase(modes);
#     lwr_frequencies = lowercase(frequencies);
#     symbs_mds = to_symbols(lwr_modes, Ses.available_modalities, delimiter);
#     symbs_frqs = to_symbols(lwr_frequencies, Ses.available_frequencies, delimiter);

#     return default_model!(df, cell,
#                           modes = symbs_mds, frequencies = symbs_frqs;
#                           init_cell_state = init_cell_state,
#                           kwargs...);
# end
# function default_model!(df::Union{DF, GroupedDataFrame{DF}}, cell::Type{C},
#                         modes::AbstractVector{Symbol} = [:mul, :mul, :mul, :mul],
#                         frequencies::AbstractVector{Symbol} = [:moy, :woy, :doy, :dow];
#                         init_cell_state::AbstractVecOrMat{NTuple{2, ST}}
#                            = fill((0.0f0, 0.0f0), (size(df)[1], 1)),
#                         kwargs...) where {C<:AbstractSesCell,
#                                           DF<:AbstractDataFrame,
#                                           ST<:AbstractFloat}
#     if length(modes) != length(frequencies)
#         throw(DimensionMismatch("`modes` and `frequencies` have to be of the same size; extra- or unsupported mode/frequency specified"))
#     end
#     _check_columns([:TExplanatory, :TMode], df,
#                     message = "[:TExplanatory, :TMode] are mandatory to init a seasonal model. Use add_time_explanatory!")

#     akwargs = setdiff(keys(kwargs), (:modes, :frequencies, :delimiter));

#     _, ncols = size(df);
#     models = [];
#     for (state, row) in zip(init_cell_state, eachrow(df))
#         c = cell(state; akwargs...);
#         scaler_types = collect_values(Ses.to_scaler, row.TExplanatory);
#         modes = collect_values(row.TMode, row.TExplanatory);
#         frwrd = [];
#         bckwrd = [];
#         for (scalers, mode) in zip(scaler_types, modes)
#             scaler = scalers[1](mode);
#             inv_scaler = scalers[2](scaler);
#             push!(frwrd, scaler);
#             push!(bckwrd, inv_scaler);
#         end
#         m = Chain(forward = Chain(frwrd...),
#                   skip = SkipConnection(
#                       Chain(pass = Ses.passtrough, cell = c),
#                       (mx, x) -> (mx, x[2])
#                   ),
#                   backward = Chain(bckwrd...),
#                   pass = Ses.passtrough);
#         push!(models, m);
#     end
#     insertcols!(df, ncols + 1, :Model => models);
#     return nothing;
# end



function Base.getproperty(obj::Recur{<:AbstractSesCell}, sym::Symbol)
    if sym in [:cell, :state]
        return getfield(obj, sym);
    else
        return getproperty(obj.cell, sym);
    end
end



function restart!(rec::Recur{C}; kwargs...) where {C<:AbstractSesCell}
    verbose = if :verbose in keys(kwargs) kwargs[:verbose] else false end;
    if verbose
        println("---[ Before reset!() ]----");
        println("State: $(rec.state)");
    end
    
    reset!(rec);
    
    if verbose
        println("---[ After reset!() ]----");
        println("State: $(rec.state)");
        println("---[ Before restart!() ]----");
        println("Step: $(rec.cell.step)");
    end
    
    Ses.restart!(rec.cell);
    
    if verbose
    println("---[ After restart!() ]----");
    println("Step: $(rec.cell.step)");
    end
    return nothing;
end



testmode!(rec::Recur{C}) where {C<:AbstractSesCell} = testmode!(rec.cell);



smooth(::AbstractSesCell, label::L, state::S, discount::AbstractVecOrMat{Float32}) where {L, S} =
    return discount .* label + (1 .- discount) .* state;