mutable struct SesOperator{A, S} <: AbstractSesCell
    alpha_proxy::A
    state0::S
    step::Int8
    warmup::Int8
    use_current_label::Bool 
    proxy_lb::Float32
    proxy_ub::Float32
    active::Union{Bool, Nothing}
end



function SesOperator(state::NTuple{2, T}, dim::Integer = 1;
                     discount::II = 0.88f0, lb = 0.05f0, ub = 0.95f0,
                     ewma::Bool = false) where {I, II<:Union{I, AbstractVecOrMat{I}}, T<:Union{I, AbstractVecOrMat{I}}}
    szd = size(discount);
    numeric_discount = isempty(szd);
    alphaV = if numeric_discount
        fill(discount, (dim, 1))
    elseif szd[1] == dim 
        discount
    else
        throw(DimensionMismatch("argument `discount` has dimension $szd, expected ($dim, 1)."));
    end;
    alpha_proxy = bounded_sigmoid_inverse.(alphaV, lb = lb, ub = ub);
    warmup_steps = if ewma inte_one else inte_two end
    cell = SesOperator(alpha_proxy, state,
                       inte_one, warmup_steps, ewma, lb, ub,
                       nothing);
    return cell;
end



alpha(op::SesOperator{A, NTuple{2, T}}) where {A, T} =
    Ses.bounded_sigmoid.(op.alpha_proxy, lb = op.proxy_lb, ub = op.proxy_ub);



function Base.show(io::IO, c::SesOperator)
    print(io, "Simple Exp. Smooth. Operator(");
    sza = size(c.alpha_proxy);
    szs = size(c.state0);
    print(io, "α: ", (if sza[1] * sza[2] == 1 c.alpha_proxy[1] else sza end),
              ", state0: ", (if szs[1] * szs[2] == 1 c.state0[1] else szs end, " x2"));
    # print(io, "α: ", (if sza[1] * sza[2] == 1 c.alpha_proxy[1] else sza end),
    #           ", state0: ", c.state0);
    print(io, ")");
end



function (op::SesOperator{A, <:NTuple{2, AbstractVecOrMat{T}}})((t, tmone), x::Union{I, AbstractVecOrMat{I}}) where {A, T, I}
    vx = reshape(x, :, 1);
    _label = if op.use_current_label vx else t end;
    _state = tmone;
    alpha = Ses.alpha(op);

    if op.step <= op.warmup;
        op.step += inte_one;
        return (vx, _label), _label;
    end

    _smooth = smooth(op, _label, _state, alpha);
    return (vx, _smooth), _smooth;
end
function (op::SesOperator{A, <:NTuple{2, T}})((t, tmone), x::I) where {A, T, I}
    _label = if op.use_current_label x else t end;
    _state = tmone;
    alpha = Ses.alpha(op);

    if op.step <= op.warmup;
        op.step += inte_one;
        return (x, _label), _label;
    end

    _smooth = smooth(op, _label, _state, alpha);
    return (x, _smooth[1]), _smooth[1];
end



@functor SesOperator



restart!(op::SesOperator; kwargs...) = (op.step = inte_one);



function test(::SesOperator, data)
    
end



testmode!(op::SesOperator, mode=true) =
  (op.active = (isnothing(mode) || mode == :auto) ? nothing : !mode; op)



trainable(m::SesOperator) = (m.alpha_proxy,)



warmup_done(op::SesOperator) = op.step == (op.warmup + 1);