// Copyright (C) 2026 Kiyotsugu Arai
// SPDX-License-Identifier: LGPL-3.0-or-later

// POV.hpp — POV-Ray シーン出力
//
// 移植元: lib++20 メディア/画像/POV.h
// 改良点:
//   - calx namespace, 英語 API
//   - std::ostream ベース (ファイル型不要)
//   - 幾何プリミティブを image::geometry:: から取得
//   - string ベース (raw ポインタ廃止)

#ifndef CALX_IMAGE_RENDER_POV_HPP
#define CALX_IMAGE_RENDER_POV_HPP

#include <image/geometry/primitives.hpp>
#include <image/types/RGB.hpp>
#include <ostream>
#include <string>

namespace calx::image {

    /// POV-Ray カメラ
    template<typename T>
    struct Camera {
        Point3D<T> location{0, 0, 100};
        Point3D<T> lookAt{0, 0, 0};
        T angle{20};
        bool orthographic{false};
    };

    /// POV-Ray 光源
    template<typename T>
    struct Light {
        Point3D<T> position;
        RGB<T> color{1, 1, 1};
    };

    /// POV-Ray シーンライター
    /// ostream に POV-Ray SDL を出力する
    class POVWriter {
    public:
        explicit POVWriter(std::ostream& os) : os_(os) {}

        /// グローバル設定を出力
        void writeHeader(double gamma = 1.0, int maxTraceLevel = 5) {
            os_ << "#include \"colors.inc\"\n"
                << "#include \"textures.inc\"\n\n"
                << "global_settings {\n"
                << "  assumed_gamma " << gamma << "\n"
                << "  max_trace_level " << maxTraceLevel << "\n"
                << "}\n\n";
        }

        /// カメラ
        template<typename T>
        void writeCamera(const Camera<T>& cam) {
            os_ << "camera {\n";
            if (cam.orthographic) os_ << "  orthographic\n";
            os_ << "  location " << pov(cam.location) << "\n"
                << "  look_at " << pov(cam.lookAt) << "\n"
                << "  angle " << cam.angle << "\n"
                << "  right x*image_width/image_height\n"
                << "}\n\n";
        }

        /// 光源
        template<typename T>
        void writeLight(const Light<T>& light) {
            os_ << "light_source { " << pov(light.position)
                << " rgb <" << light.color.r << "," << light.color.g
                << "," << light.color.b << "> }\n\n";
        }

        /// テクスチャ / ピグメント設定
        void setTexture(const std::string& tex) { texture_ = tex; }
        void setPigment(const std::string& pig) { pigment_ = pig; }
        void setPigment(double r, double g, double b) {
            pigment_ = "color rgb <" + std::to_string(r) + ","
                       + std::to_string(g) + "," + std::to_string(b) + ">";
        }

        /// 球
        template<typename T>
        void writeSphere(const Sphere<T>& s) {
            os_ << "sphere { " << pov(s.center) << ", " << s.radius;
            writeFinish();
            os_ << " }\n";
        }

        /// 円柱
        template<typename T>
        void writeCylinder(const Cylinder<T>& c) {
            os_ << "cylinder { " << pov(c.p0) << ", " << pov(c.p1) << ", " << c.radius;
            writeFinish();
            os_ << " }\n";
        }

        /// 円錐
        template<typename T>
        void writeCone(const Cone<T>& c) {
            os_ << "cone { " << pov(c.base) << ", " << c.radius
                << ", " << pov(c.apex) << ", 0";
            writeFinish();
            os_ << " }\n";
        }

        /// 三角形 (3D)
        template<typename T>
        void writeTriangle(const Triangle3D<T>& t) {
            os_ << "triangle { " << pov(t.p0) << ", " << pov(t.p1) << ", " << pov(t.p2);
            writeFinish();
            os_ << " }\n";
        }

        /// ボックス (AABB)
        template<typename T>
        void writeBox(const AABB<T>& box) {
            os_ << "box { " << pov(box.min) << ", " << pov(box.max);
            writeFinish();
            os_ << " }\n";
        }

    private:
        std::ostream& os_;
        std::string texture_;
        std::string pigment_;

        /// POV-Ray 座標系 (Y-up): calx (x,y,z) → POV (x,z,y)
        template<typename T>
        static std::string pov(const Point3D<T>& p) {
            return "<" + std::to_string(p.x) + ","
                       + std::to_string(p.z) + ","
                       + std::to_string(p.y) + ">";
        }

        void writeFinish() {
            if (!texture_.empty())
                os_ << " texture { " << texture_ << " }";
            if (!pigment_.empty())
                os_ << " pigment { " << pigment_ << " }";
        }
        // ============================================================
        // POV-29: 追加プリミティブの SDL 出力
        // ============================================================

        void writeTorus(T majorR, T minorR, const Point3D<T>& center = {}) {
            os_ << "torus { " << majorR << ", " << minorR;
            if (center.x != T(0) || center.y != T(0) || center.z != T(0))
                os_ << " translate " << vec(center);
            writeTexture(); os_ << " }\n";
        }

        void writeDisc(const Point3D<T>& center, const Point3D<T>& normal,
                        T outerR, T innerR = T(0)) {
            os_ << "disc { " << vec(center) << ", " << vec(normal)
                << ", " << outerR;
            if (innerR > T(0)) os_ << ", " << innerR;
            writeTexture(); os_ << " }\n";
        }

        void writeLathe(const std::vector<std::pair<T,T>>& points) {
            os_ << "lathe { linear_spline " << points.size();
            for (auto& [r, y] : points) os_ << ", <" << r << ", " << y << ">";
            writeTexture(); os_ << " }\n";
        }

        void writePrism(T y0, T y1, const std::vector<std::pair<T,T>>& points) {
            os_ << "prism { linear_spline " << y0 << ", " << y1 << ", " << points.size();
            for (auto& [x, z] : points) os_ << ", <" << x << ", " << z << ">";
            writeTexture(); os_ << " }\n";
        }

        void writeQuadric(T A, T B, T C, T D, T E, T F, T G, T H, T I, T J) {
            os_ << "quadric { <" << A << "," << B << "," << C << ">, <"
                << D << "," << E << "," << F << ">, <"
                << G << "," << H << "," << I << ">, " << J;
            writeTexture(); os_ << " }\n";
        }

        void writeSuperellipsoid(T e, T n) {
            os_ << "superellipsoid { <" << e << ", " << n << ">";
            writeTexture(); os_ << " }\n";
        }

        void writeHeightField(const std::string& filename) {
            os_ << "height_field { png \"" << filename << "\"";
            writeTexture(); os_ << " }\n";
        }

        void writeCSG(const std::string& op, const std::string& body) {
            os_ << op << " {\n" << body << "  ";
            writeTexture(); os_ << "\n}\n";
        }

        void writeMesh2(const std::vector<Point3D<T>>& vertices,
                         const std::vector<std::array<int,3>>& faces) {
            os_ << "mesh2 {\n  vertex_vectors { " << vertices.size();
            for (auto& v : vertices) os_ << ", " << vec(v);
            os_ << " }\n  face_indices { " << faces.size();
            for (auto& f : faces) os_ << ", <" << f[0] << "," << f[1] << "," << f[2] << ">";
            os_ << " }\n  ";
            writeTexture(); os_ << "\n}\n";
        }

        // ============================================================
        // POV-30: テクスチャ SDL 出力
        // ============================================================

        void writeNormal(const std::string& pattern, T amount = T(1)) {
            os_ << " normal { " << pattern << " " << amount << " }";
        }

        void writeFinish(T phong = T(0), T reflection = T(0),
                          T irid = T(0), bool metallic = false) {
            os_ << " finish {";
            if (phong > T(0)) os_ << " phong " << phong;
            if (reflection > T(0)) os_ << " reflection " << reflection;
            if (metallic) os_ << " metallic";
            if (irid > T(0)) os_ << " irid { " << irid << " }";
            os_ << " }";
        }

        void writeImageMap(const std::string& filename, const std::string& mapType = "png") {
            os_ << " pigment { image_map { " << mapType << " \"" << filename << "\" } }";
        }

        void writeCheckerPigment(const RGB<T>& c1, const RGB<T>& c2) {
            os_ << " pigment { checker rgb " << rgbStr(c1) << " rgb " << rgbStr(c2) << " }";
        }

        void writeMarblePigment(const RGB<T>& c1, const RGB<T>& c2, T turbulence = T(0.5)) {
            os_ << " pigment { marble turbulence " << turbulence
                << " color_map { [0.0 rgb " << rgbStr(c1)
                << "] [1.0 rgb " << rgbStr(c2) << "] } }";
        }

        // ============================================================
        // POV-31: アニメーション SDL 出力
        // ============================================================

        /// clock 変数を使った時間変化のコメント出力
        void writeClockComment() {
            os_ << "// Use 'clock' variable for animation (0.0 to 1.0)\n";
            os_ << "// Run: povray +KFI0 +KFF30 +KC scene.pov\n";
        }

        /// 回転アニメーション (clock ベース)
        void writeRotateAnimation(const std::string& axis = "y", T degrees = T(360)) {
            os_ << "rotate " << axis << "*clock*" << degrees << "\n";
        }

    private:
        std::string rgbStr(const RGB<T>& c) const {
            std::ostringstream s;
            s << "<" << c.r << "," << c.g << "," << c.b << ">";
            return s.str();
        }
    };

} // namespace calx::image

#endif // CALX_IMAGE_RENDER_POV_HPP
