//====================================================================
// environment_mapper.v - 环境映射器
// 第18课：立方体贴图反射向量计算
//====================================================================
module environment_mapper #(
    parameter COORD_WIDTH = 16,
    parameter COLOR_WIDTH = 24,
    parameter FRAC_BITS  = 12
)(
    input  wire                          clk, rst_n,
    input  wire                          frag_valid,
    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,
    output reg                           frag_ready,
    output reg  [2:0]                    cube_face,   // 0=+X,1=-X,2=+Y,3=-Y,4=+Z,5=-Z
    output reg  [COORD_WIDTH-1:0]        cube_u, cube_v,
    output reg                           result_valid,
    output reg  [COLOR_WIDTH-1:0]        result_color,
    input  wire [COLOR_WIDTH-1:0]        cube_color,
    input  wire                          cube_color_valid
);
/* 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 [COORD_WIDTH-1:0] rx, ry, rz;
    reg signed [COORD_WIDTH-1:0] abs_rx, abs_ry, abs_rz;
    localparam S_IDLE=3'd0, S_REFLECT=3'd1, S_FACE=3'd2, S_FETCH=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;
                        state <= S_REFLECT;
                    end
                end
                S_REFLECT: begin
                    // R = 2(N·V)N - V
                    rx <= qmul(2 * qmul(frag_nx*frag_vx + frag_ny*frag_vy + frag_nz*frag_vz, frag_nx), 16'h1000) - frag_vx;
                    ry <= qmul(2 * (frag_nx*frag_vx + frag_ny*frag_vy + frag_nz*frag_vz), frag_ny) - frag_vy;
                    rz <= qmul(2 * (frag_nx*frag_vx + frag_ny*frag_vy + frag_nz*frag_vz), frag_nz) - frag_vz;
                    state <= S_FACE;
                end
                S_FACE: begin
                    abs_rx = (rx < 0) ? -rx : rx;
                    abs_ry = (ry < 0) ? -ry : ry;
                    abs_rz = (rz < 0) ? -rz : rz;
                    if (abs_rx >= abs_ry && abs_rx >= abs_rz) begin
                        cube_face <= rx[COORD_WIDTH-1] ? 3'd1 : 3'd0; // -X : +X
                        cube_u <= (rx[COORD_WIDTH-1]) ? -rz : rz;
                        cube_v <= ry;
                    end else if (abs_ry >= abs_rx && abs_ry >= abs_rz) begin
                        cube_face <= ry[COORD_WIDTH-1] ? 3'd3 : 3'd2; // -Y : +Y
                        cube_u <= rx;
                        cube_v <= (ry[COORD_WIDTH-1]) ? -rz : rz;
                    end else begin
                        cube_face <= rz[COORD_WIDTH-1] ? 3'd5 : 3'd4; // -Z : +Z
                        cube_u <= (rz[COORD_WIDTH-1]) ? rx : -rx;
                        cube_v <= ry;
                    end
                    state <= S_FETCH;
                end
                S_FETCH: begin
                    if (cube_color_valid) begin
                        result_color <= cube_color;
                        state <= S_OUT;
                    end
                end
                S_OUT: begin
                    result_valid <= 1; state <= S_IDLE;
                end
                default: state <= S_IDLE;
            endcase
        end
    end
endmodule