module Blend
(
    input clock,
    input   [5:0] rule,
    input         disable_hq2x,
    input  [23:0] E,
    input  [23:0] A,
    input  [23:0] B,
    input  [23:0] D,
    input  [23:0] F,
    input  [23:0] H,
    output reg [23:0] Result
);
    reg [23:0] E_reg, A_reg, B_reg, D_reg, F_reg, H_reg;
    reg [23:0] E_reg_d, A_reg_d, B_reg_d, D_reg_d, F_reg_q, H_reg_d;
    reg [23:0] Input1, Input2, Input3;
    reg [23:0] res_out, res_out_q, res_out_q_q, res_out_q_q_q, res_out_q_q_q_q;
    reg [1:0] input_ctrl;
    reg [8:0] op, op_q;
    reg [5:0] rule_reg, rule_1, rule_2, rule_3, rule_d;
    localparam BLEND0 = 9'b1_xxx_x_xx_xx; // 0: A
    localparam BLEND1 = 9'b0_110_0_10_00; // 1: (A * 12 + B * 4) >> 4
    localparam BLEND2 = 9'b0_100_0_10_10; // 2: (A * 8 + B * 4 + C * 4) >> 4
    localparam BLEND3 = 9'b0_101_0_10_01; // 3: (A * 10 + B * 4 + C * 2) >> 4
    localparam BLEND4 = 9'b0_110_0_01_01; // 4: (A * 12 + B * 2 + C * 2) >> 4
    localparam BLEND5 = 9'b0_010_0_11_11; // 5: (A * 4 + (B + C) * 6) >> 4
    localparam BLEND6 = 9'b0_111_1_xx_xx; // 6: (A * 14 + B + C) >> 4
    localparam AB = 2'b00;
    localparam AD = 2'b01;
    localparam DB = 2'b10;
    localparam BD = 2'b11;
    wire is_diff;
    DiffCheck diff_checker(clock, rule_reg[1] ? B_reg : H_reg, rule_reg[0] ? D_reg : F_reg, is_diff);

    delayline #(
        .WIDTH(6*24),
        .CYCLES(5)
    ) delay1 (
        .clock(clock),
        .in({ E_reg, A_reg, B_reg, D_reg, F_reg, H_reg }),
        .out({ E_reg_d, A_reg_d, B_reg_d, D_reg_d, F_reg_q, H_reg_d })
    );
    delayline #(
        .WIDTH(6),
        .CYCLES(4)
    ) delay2 (
        .clock(clock),
        .in(rule_reg),
        .out(rule_d)
    );

    always_ff @(posedge clock) begin
        { rule_reg, E_reg, A_reg, B_reg, D_reg, F_reg, H_reg } <= { rule, E, A, B, D, F, H };

        case({!is_diff, rule_d[5:2]})
            1,17:  {op, input_ctrl} <= {BLEND1, AB};
            2,18:  {op, input_ctrl} <= {BLEND1, DB};
            3,19:  {op, input_ctrl} <= {BLEND1, BD};
            4,20:  {op, input_ctrl} <= {BLEND2, DB};
            5,21:  {op, input_ctrl} <= {BLEND2, AB};
            6,22:  {op, input_ctrl} <= {BLEND2, AD};

             8: {op, input_ctrl} <= {BLEND0, 2'bxx};
             9: {op, input_ctrl} <= {BLEND0, 2'bxx};
            10: {op, input_ctrl} <= {BLEND0, 2'bxx};
            11: {op, input_ctrl} <= {BLEND1, AB};
            12: {op, input_ctrl} <= {BLEND1, AB};
            13: {op, input_ctrl} <= {BLEND1, AB};
            14: {op, input_ctrl} <= {BLEND1, DB};
            15: {op, input_ctrl} <= {BLEND1, BD};

            24: {op, input_ctrl} <= {BLEND2, DB};
            25: {op, input_ctrl} <= {BLEND5, DB};
            26: {op, input_ctrl} <= {BLEND6, DB};
            27: {op, input_ctrl} <= {BLEND2, DB};
            28: {op, input_ctrl} <= {BLEND4, DB};
            29: {op, input_ctrl} <= {BLEND5, DB};
            30: {op, input_ctrl} <= {BLEND3, BD};
            31: {op, input_ctrl} <= {BLEND3, DB};
            default: {op, input_ctrl} <= {11{1'bx}};
        endcase

        // Setting op[8] effectively disables HQ2X because blend will always return E.
        if (disable_hq2x) op[8] <= 1;
    end

    // Generate inputs to the inner blender. Valid combinations.
    // 00: E A B
    // 01: E A D 
    // 10: E D B
    // 11: E B D

    InnerBlend inner_blend1(clock, op_q, Input1[7:0],   Input2[7:0],   Input3[7:0],   res_out[7:0]);
    InnerBlend inner_blend2(clock, op_q, Input1[15:8],  Input2[15:8],  Input3[15:8],  res_out[15:8]);
    InnerBlend inner_blend3(clock, op_q, Input1[23:16], Input2[23:16], Input3[23:16], res_out[23:16]);

    always_ff @(posedge clock) begin
        op_q <= op;
        Input1 <= E_reg_d;
        Input2 <= !input_ctrl[1] ? A_reg_d :
                  !input_ctrl[0] ? D_reg_d : B_reg_d;
        Input3 <= !input_ctrl[0] ? B_reg_d : D_reg_d;
        { Result, res_out_q_q_q_q, res_out_q_q_q, res_out_q_q, res_out_q } <= { res_out_q_q_q_q, res_out_q_q_q, res_out_q_q, res_out_q, res_out };
    end
endmodule