//====================================================================
// phong_shader.v - Phong着色器
// 第16课：环境光+漫反射+镜面反射
//====================================================================
module phong_shader #(
    parameter COORD_WIDTH = 16,
    parameter COLOR_WIDTH = 24,
    parameter FRAC_BITS  = 12
)(
    input  wire                          clk, rst_n,
    input  wire                          frag_valid,
    input  wire [COLOR_WIDTH-1:0]        frag_base_color,
    input  wire signed [COORD_WIDTH-1:0] frag_nx, frag_ny, frag_nz,
    input  wire signed [COORD_WIDTH-1:0] frag_vx, frag_vy, frag_vz,
    input  wire signed [COORD_WIDTH-1:0] light_x, light_y, light_z,
    input  wire [7:0]                    shininess,
    input  wire [COLOR_WIDTH-1:0]        light_color,
    input  wire [7:0]                    ambient_strength,
    output reg                           frag_ready,
    output reg                           result_valid,
    output reg  [COLOR_WIDTH-1:0]        result_color
);
/* verilator lint_off WIDTHEXPAND */
/* verilator lint_off WIDTHTRUNC */
/* verilator lint_off CASEOVERLAP */
/* verilator lint_off CMPCONST */
/* verilator lint_off UNSIGNED */
/* verilator lint_off WIDTHCONCAT */

    function signed [COORD_WIDTH-1:0] qmul;
        input signed [COORD_WIDTH-1:0] a, b;
        reg signed [2*COORD_WIDTH-1:0] prod;
        begin prod = a * b; qmul = prod[2*COORD_WIDTH-FRAC_BITS-1:COORD_WIDTH-FRAC_BITS]; end
    endfunction
    reg signed [2*COORD_WIDTH-1:0] dot_nl, dot_rv;
    reg signed [COORD_WIDTH-1:0] reflect_x, reflect_y, reflect_z;
    reg [7:0] diff_factor, spec_factor;
    reg [7:0] base_r, base_g, base_b;
    reg [15:0] amb_r, amb_g, amb_b, dif_r, dif_g, dif_b, spc_r, spc_g, spc_b;
    localparam S_IDLE=3'd0, S_DIFFUSE=3'd1, S_SPECULAR=3'd2, S_COMBINE=3'd3, S_OUT=3'd4;
    reg [2:0] state;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin state<=S_IDLE; frag_ready<=1; result_valid<=0; end
        else begin
            result_valid <= 0;
            case (state)
                S_IDLE: begin
                    frag_ready <= 1;
                    if (frag_valid) begin
                        frag_ready <= 0;
                        base_r <= frag_base_color[23:16]; base_g <= frag_base_color[15:8]; base_b <= frag_base_color[7:0];
                        dot_nl = frag_nx*light_x + frag_ny*light_y + frag_nz*light_z;
                        diff_factor = (dot_nl > 0) ? ((dot_nl[19:12]>8'd255)?8'd255:dot_nl[19:12]) : 8'd0;
                        state <= S_DIFFUSE;
                    end
                end
                S_DIFFUSE: begin
                    // R = 2(N·L)N - L
                    reflect_x <= qmul(2*qmul(dot_nl[27:12], frag_nx), 16'h1000) - light_x;
                    reflect_y <= qmul(2*qmul(dot_nl[27:12], frag_ny), 16'h1000) - light_y;
                    reflect_z <= qmul(2*qmul(dot_nl[27:12], frag_nz), 16'h1000) - light_z;
                    state <= S_SPECULAR;
                end
                S_SPECULAR: begin
                    dot_rv = reflect_x*frag_vx + reflect_y*frag_vy + reflect_z*frag_vz;
                    spec_factor = (dot_rv > 0) ? 8'd255 : 8'd0; // 简化镜面反射
                    state <= S_COMBINE;
                end
                S_COMBINE: begin
                    amb_r = (base_r * ambient_strength) >> 8;
                    amb_g = (base_g * ambient_strength) >> 8;
                    amb_b = (base_b * ambient_strength) >> 8;
                    dif_r = (base_r * diff_factor) >> 8;
                    dif_g = (base_g * diff_factor) >> 8;
                    dif_b = (base_b * diff_factor) >> 8;
                    spc_r = (light_color[23:16] * spec_factor) >> 8;
                    spc_g = (light_color[15:8] * spec_factor) >> 8;
                    spc_b = (light_color[7:0] * spec_factor) >> 8;
                    state <= S_OUT;
                end
                S_OUT: begin
                    result_color <= {
                        (amb_r+dif_r+spc_r>255)?8'd255:amb_r[7:0]+dif_r[7:0]+spc_r[7:0],
                        (amb_g+dif_g+spc_g>255)?8'd255:amb_g[7:0]+dif_g[7:0]+spc_g[7:0],
                        (amb_b+dif_b+spc_b>255)?8'd255:amb_b[7:0]+dif_b[7:0]+spc_b[7:0]
                    };
                    result_valid <= 1; state <= S_IDLE;
                end
                default: state <= S_IDLE;
            endcase
        end
    end
endmodule