function [D_rv, D_flux, DvDt, solutionPenalties, residualPenalties] = constructDiffOps(scheme, g, order, opSet, waveSpeed, BCs, fluxSplitting)
    default_arg('fluxSplitting',[]);

    %% DiffOps for solution vector
    [D, solutionPenalties] = constructTotalFluxDiffOp(scheme, g, order, opSet, waveSpeed, BCs, fluxSplitting);
    D2 = constructSymmetricD2Operator(g, order, opSet);
    D_rv = @(v,viscosity)(D + D2(viscosity))*v;

    %% DiffOps for residual viscosity
    [D_flux, residualPenalties] = constructTotalFluxDiffOp(scheme, g, max(order-2,2), opSet, waveSpeed, BCs, fluxSplitting);
    % DiffOp for flux in residual viscosity. Due to sign conventions of the implemnted schemes, we need to
    % change the sign.
    D_flux = -D_flux;
    D_flux = @(v) D_flux*v;
    % DiffOp for time derivative in residual viscosity
    DvDt = @(v)D*v;

    %% DiffOps for residual viscosity
    % xl = g.getBoundary('l');
    % xr = g.getBoundary('r');
    % xlim = {xl, xr};
    % m_f = 2*(g.m()-1)+1;
    % g_f = grid.equidistant(m_f, xlim);

    % [DvDt, residualPenalties] = constructTotalFluxDiffOp(scheme, g_f, 9, opSet, waveSpeed, BCs, fluxSplitting);
    % % DiffOp for flux in residual viscosity. Due to sign conventions of the implemnted schemes, we need to
    % % change the sign.
    % D_flux = -D;
    % D_flux = @(v) D_flux*v;
    % % DiffOp for time derivative in residual viscosity
    % DvDt = @(v)applyInterpDiffOp(g.points(), v, g_f.points(), DvDt);
end

% function u = applyInterpDiffOp(x_c, u_c, x_f,  D_f)
%     u_f = D_f*interp1(x_c,u_c,x_f,'linear');
%     u = u_f(ismember(x_f,x_c));
% end

function [D, penalties] = constructTotalFluxDiffOp(scheme, grid, order, opSet, waveSpeed, BCs, fluxSplitting)
    if isequal(opSet, @sbp.D1Upwind)
        diffOp = scheme(grid, order, opSet, waveSpeed, fluxSplitting);
    else
        diffOp = scheme(grid, order, opSet, waveSpeed);
    end
    [D, penalties]  = addClosuresToDiffOp(diffOp, BCs);
end

function [D, penalties] = addClosuresToDiffOp(diffOp, BCs)
    D = diffOp.D;
    penalties = cell(size(BCs));
    for i = 1:size(BCs,1)
        for j = 1:size(BCs,2)
            [closure, penalties{i,j}] = diffOp.boundary_condition(BCs{i,j}.boundary, BCs{i,j}.type);
            D = D + closure;
        end
    end
end

function D2 = constructSymmetricD2Operator(grid, order, opSet)
    % TODO: 
    % Currently only implemented for upwind operators.
    % Remove this part once the time-dependent D2 operator is implemented for other opSets
    % or if it is decided that it should only be supported for upwind operators.
    assert(isequal(opSet,@sbp.D1Upwind))

    m = grid.size();
    ops = cell(grid.D(),1);
    I = cell(grid.D(),1);
    for i = 1:grid.D()
       lim = {grid.x{i}(1), grid.x{i}(end)};
       ops{i} = opSet(m(i), lim, order);
       I{i} = speye(m(i));
    end

    % TBD: How is this generalized to a loop over dimensions or similar?
    switch grid.D()
        case 1
            e_r = ops{1}.e_r;
            e_l = ops{1}.e_l;
            Dm = ops{1}.Dm;
            Dp = ops{1}.Dp;
            Hi = ops{1}.HI;
            B = e_r*e_r' - e_l*e_l';
            D2 = @(viscosity) Dm*spdiag(viscosity)*Dp-Hi*(B*spdiag(viscosity)*Dp);
        case 2
            e_e = kron(ops{1}.e_r,I{2});
            e_w = kron(ops{1}.e_l,I{2});
            Dm_x = kron(ops{1}.Dm,I{2});
            Dp_x  = kron(ops{1}.Dp,I{2});
            H_x  = kron(ops{1}.HI,I{2});
            B_x = e_e*e_e' - e_w*e_w';
            D2_x = @(viscosity) Dm_x*spdiag(viscosity)*Dp_x-H_x*(B_x*spdiag(viscosity)*Dp_x);

            e_n = kron(I{1},ops{2}.e_r);
            e_s = kron(I{1},ops{2}.e_l);
            Dm_y = kron(I{1},ops{2}.Dm);
            Dp_y  = kron(I{1},ops{2}.Dp);
            H_y = kron(I{1},ops{2}.HI);
            B_y = e_n*e_n' - e_s*e_s';
            D2_y = @(viscosity) Dm_y*spdiag(viscosity)*Dp_y-H_y*(B_y*spdiag(viscosity)*Dp_y);
            D2 = @(viscosity)D2_x(viscosity) + D2_y(viscosity);
        otherwise
            error('3D not yet implemented')
    end
end