module d3d;

import dlangui;
import dlangui.graphics.scene.scene3d;
import dlangui.graphics.scene.camera;
import dlangui.graphics.scene.mesh;
import dlangui.graphics.scene.material;
import dlangui.graphics.scene.effect;
import dlangui.graphics.scene.model;
import dlangui.graphics.scene.node;
import dlangui.graphics.scene.light;
import dlangui.graphics.scene.objimport;
import dlangui.graphics.scene.fbximport;
import dlangui.graphics.glsupport;
import dlangui.graphics.gldrawbuf;
import derelict.opengl3.gl3;
import derelict.opengl3.gl;

mixin APP_ENTRY_POINT;

/// entry point for dlangui based application
extern (C) int UIAppMain(string[] args) {
    // embed resources listed in views/resources.list into executable
    embeddedResourceList.addResources(embedResourcesFromList!("resources.list")());

    // create window
    Window window = Platform.instance.createWindow("DlangUI example - 3D Application", null, WindowFlag.Resizable, 600, 500);
    static if (ENABLE_OPENGL) {
        window.mainWidget = new UiWidget();
    } else {
        window.mainWidget = new TextWidget("error", "Please build with OpenGL enabled"d);
    }

    //MeshPart part = new MeshPart();

    // show window
    window.show();

    // run message loop
    return Platform.instance.enterMessageLoop();
}

static if (ENABLE_OPENGL):

class UiWidget : VerticalLayout {
    this() {
        super("OpenGLView");
        layoutWidth = FILL_PARENT;
        layoutHeight = FILL_PARENT;
        alignment = Align.Center;
        try {
            parseML(q{
                {
                  margins: 10
                  padding: 10
                  backgroundImageId: "tx_fabric.tiled"
                  layoutWidth: fill
                  layoutHeight: fill

                  VerticalLayout {
                    id: glView
                    margins: 10
                    padding: 10
                    layoutWidth: fill
                    layoutHeight: fill
                    TextWidget { text: "There should be OpenGL animation on background"; textColor: "red"; fontSize: 150%; fontWeight: 800; fontFace: "Arial" }
                    TextWidget { text: "Do you see it? If no, there is some bug in Mesh rendering code..."; fontSize: 120% }
                    // arrange controls as form - table with two columns
                    TableLayout {
                        colCount: 6
                        TextWidget { text: "Translation X" }
                        TextWidget { id: lblTranslationX; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
                        ScrollBar { id: sbTranslationX; orientation: horizontal; minValue: -100; maxValue: 100; position: 0; minWidth: 200; alpha: 0.6 }
                        TextWidget { text: "Rotation X" }
                        TextWidget { id: lblRotationX; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
                        ScrollBar { id: sbRotationX; orientation: horizontal; minValue: -180; maxValue: 180; position: 0; minWidth: 200; alpha: 0.6 }
                        TextWidget { text: "Translation Y" }
                        TextWidget { id: lblTranslationY; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
                        ScrollBar { id: sbTranslationY; orientation: horizontal; minValue: -100; maxValue: 100; position: 15; minWidth: 200; alpha: 0.6 }
                        TextWidget { text: "Rotation Y" }
                        TextWidget { id: lblRotationY; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
                        ScrollBar { id: sbRotationY; orientation: horizontal; minValue: -180; maxValue: 180; position: 0; minWidth: 150; alpha: 0.6 }
                        TextWidget { text: "Translation Z" }
                        TextWidget { id: lblTranslationZ; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
                        ScrollBar { id: sbTranslationZ; orientation: horizontal; minValue: -100; maxValue: 100; position: 45; minWidth: 150; alpha: 0.6 }
                        TextWidget { text: "Rotation Z" }
                        TextWidget { id: lblRotationZ; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
                        ScrollBar { id: sbRotationZ; orientation: horizontal; minValue: -180; maxValue: 180; position: 0; minWidth: 150; alpha: 0.6 }
                        TextWidget { text: "Near" }
                        TextWidget { id: lblNear; text: "0.1"; minWidth: 80; backgroundColor: 0x80FFFFFF }
                        ScrollBar { id: sbNear; orientation: horizontal; minValue: 1; maxValue: 100; position: 1; minWidth: 150; alpha: 0.6 }
                        TextWidget { text: "Far" }
                        TextWidget { id: lblFar; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
                        ScrollBar { id: sbFar; orientation: horizontal; minValue: 20; maxValue: 1000; position: 1000; minWidth: 150; alpha: 0.6 }
                    }
                    VSpacer { layoutWeight: 30 }
                    HorizontalLayout {
                        TextWidget { text: "Some buttons:" }
                        Button { id: btnOk; text: "Ok"; fontSize: 27px }
                        Button { id: btnCancel; text: "Cancel"; fontSize: 27px }
                    }
                  }
                }
            }, "", this);
        } catch (Exception e) {
            Log.e("Failed to parse dml", e);
        }
        // assign OpenGL drawable to child widget background
        childById("glView").backgroundDrawable = DrawableRef(new OpenGLDrawable(&doDraw));
        controlsToVars();
        assignHandlers();

        _scene = new Scene3d();

        _cam = new Camera();
        _cam.translate(vec3(0, 14, -7));

        _scene.activeCamera = _cam;

        dirLightNode = new Node3d();
        //dirLightNode.lookAt(vec3(-5, -5, -5), vec3(0, 0, 0), vec3(0, 1, 0));
        dirLightNode.rotateY(-15);
        //dirLightNode.rotateX(20);
        dirLightNode.translateX(2);
        dirLightNode.translateY(3);
        dirLightNode.translateZ(0);
        dirLightNode.light = Light.createPoint(vec3(2, 2, 2), 15); //Light.createDirectional(vec3(1, 0.5, 0.5));
        //dirLightNode.light = Light.createDirectional(vec3(1, 0.5, 0.8));
        dirLightNode.light.enabled = true;
        _scene.addChild(dirLightNode);

        int x0 = 0;
        int y0 = 0;
        int z0 = 0;

        Mesh _mesh = Mesh.createCubeMesh(vec3(x0+ 0, y0 + 0, z0 + 0), 0.3f);
        for (int i = 0; i < 10; i++) {
            _mesh.addCubeMesh(vec3(x0+ 0, y0+0, z0+ i * 2 + 1.0f), 0.2f, vec4(i / 12, 1, 1, 1));
            _mesh.addCubeMesh(vec3(x0+ i * 2 + 1.0f, y0+0, z0+ 0), 0.2f, vec4(1, i / 12, 1, 1));
            _mesh.addCubeMesh(vec3(x0+ -i * 2 - 1.0f, y0+0, z0+ 0), 0.2f, vec4(1, i / 12, 1, 1));
            _mesh.addCubeMesh(vec3(x0+ 0, y0+i * 2 + 1.0f, z0+ 0), 0.2f, vec4(1, 1, i / 12 + 0.1, 1));
            _mesh.addCubeMesh(vec3(x0+ 0, y0+-i * 2 - 1.0f, z0+ 0), 0.2f, vec4(1, 1, i / 12 + 0.1, 1));
            _mesh.addCubeMesh(vec3(x0+ i * 2 + 1.0f, y0+i * 2 + 1.0f, z0+ i * 2 + 1.0f), 0.2f, vec4(i / 12, i / 12, i / 12, 1));
            _mesh.addCubeMesh(vec3(x0+ -i * 2 + 1.0f, y0+i * 2 + 1.0f, z0+ i * 2 + 1.0f), 0.2f, vec4(i / 12, i / 12, 1 - i / 12, 1));
            _mesh.addCubeMesh(vec3(x0+  i * 2 + 1.0f, y0+-i * 2 + 1.0f, z0+ i * 2 + 1.0f), 0.2f, vec4(i / 12, 1 - i / 12, i / 12, 1));
            _mesh.addCubeMesh(vec3(x0+ -i * 2 - 1.0f, y0+-i * 2 - 1.0f, z0+ -i * 2 - 1.0f), 0.2f, vec4(1 - i / 12, i / 12, i / 12, 1));
        }
        Material cubeMaterial = new Material(EffectId("textured.vert", "textured.frag", null), "crate");
        Model cubeDrawable = new Model(cubeMaterial, _mesh);
        Node3d cubeNode = new Node3d("cubes", cubeDrawable);
        _scene.addChild(cubeNode);

        debug(fbximport) {
            // test FBX import
            FbxModelImport importer;
            string src = loadTextResource("suzanne.fbx");
            importer.filename = "suzanne.fbx";
            importer.parse(src);
        }

        ObjModelImport importer;
        string src = loadTextResource("suzanne.obj");
        importer.parse(src);
        Log.d("suzanne mesh:", importer.mesh.dumpVertexes(20));
        Material suzanneMaterial = new Material(EffectId("colored.vert", "colored.frag", null), null); //"SPECULAR"
        //suzanneMaterial.ambientColor = vec3(0.5, 0.5, 0.5);
        suzanneMaterial.diffuseColor = vec4(0.7, 0.7, 0.5, 1.0);
        //suzanneMaterial.specular = true;
        Model suzanneDrawable = new Model(suzanneMaterial, importer.mesh);
        suzanneNode = new Node3d("suzanne", suzanneDrawable);
        suzanneNode.translate(vec3(2, 2, -5));
        _scene.addChild(suzanneNode);


        brickNode = new Node3d("brick");
        brickNode.translate(vec3(-2, 2, -3));
        Mesh brickMesh = Mesh.createCubeMesh(vec3(0, 0, 0), 0.8, vec4(0.8, 0.8, 0.8, 1));
        Material brickMaterial = new Material(EffectId("textured.vert", "textured.frag", null), "brick", "brickn"); // with bump mapping
        //brickMaterial.specular = true;
        brickNode.drawable = new Model(brickMaterial, brickMesh);
        _scene.addChild(brickNode);

    }

    Node3d dirLightNode;
    Node3d suzanneNode;
    Node3d brickNode;

    float rotationX;
    float rotationY;
    float rotationZ;
    float translationX;
    float translationY;
    float translationZ;
    float near;
    float far;

    /// handle scroll event
    bool onScrollEvent(AbstractSlider source, ScrollEvent event) {
        controlsToVars();
        return true;
    }

    void assignHandlers() {
        childById!ScrollBar("sbNear").scrollEvent = &onScrollEvent;
        childById!ScrollBar("sbFar").scrollEvent = &onScrollEvent;
        childById!ScrollBar("sbRotationX").scrollEvent = &onScrollEvent;
        childById!ScrollBar("sbRotationY").scrollEvent = &onScrollEvent;
        childById!ScrollBar("sbRotationZ").scrollEvent = &onScrollEvent;
        childById!ScrollBar("sbTranslationX").scrollEvent = &onScrollEvent;
        childById!ScrollBar("sbTranslationY").scrollEvent = &onScrollEvent;
        childById!ScrollBar("sbTranslationZ").scrollEvent = &onScrollEvent;
    }

    void controlsToVars() {
        near = childById!ScrollBar("sbNear").position / 10.0f;
        far = childById!ScrollBar("sbFar").position / 10.0f;
        translationX = childById!ScrollBar("sbTranslationX").position / 10.0f;
        translationY = childById!ScrollBar("sbTranslationY").position / 10.0f;
        translationZ = childById!ScrollBar("sbTranslationZ").position / 10.0f;
        rotationX = childById!ScrollBar("sbRotationX").position;
        rotationY = childById!ScrollBar("sbRotationY").position;
        rotationZ = childById!ScrollBar("sbRotationZ").position;
        childById("lblNear").text = to!dstring(near);
        childById("lblFar").text = to!dstring(far);
        childById("lblTranslationX").text = to!dstring(translationX);
        childById("lblTranslationY").text = to!dstring(translationY);
        childById("lblTranslationZ").text = to!dstring(translationZ);
        childById("lblRotationX").text = to!dstring(rotationX);
        childById("lblRotationY").text = to!dstring(rotationY);
        childById("lblRotationZ").text = to!dstring(rotationZ);
    }

    /// returns true is widget is being animated - need to call animate() and redraw
    @property override bool animating() { return true; }
    /// animates window; interval is time left from previous draw, in hnsecs (1/10000000 of second)
    override void animate(long interval) {
        //Log.d("animating");
        _cam.rotateX(0.01);
        _cam.rotateY(0.02);
        angle += interval * 0.000002f;
        invalidate();
        suzanneNode.rotateY(interval * 0.000002f);
        brickNode.rotateY(interval * 0.00000123f);
        brickNode.rotateZ(interval * 0.0000004123f);
        brickNode.rotateX(interval * 0.0000007543f);
    }
    float angle = 0;

    Scene3dRef _scene;
    Camera _cam;

    /// this is OpenGLDrawableDelegate implementation
    private void doDraw(Rect windowRect, Rect rc) {
        _cam.setPerspective(rc.width, rc.height, 45.0f, near, far);
        _cam.setIdentity();
        _cam.translateX(translationX);
        _cam.translateY(translationY);
        _cam.translateZ(translationZ);
        _cam.rotateX(rotationX);
        _cam.rotateY(rotationY);
        _cam.rotateZ(rotationZ);

        checkgl!glEnable(GL_CULL_FACE);
        //checkgl!glDisable(GL_CULL_FACE);
        checkgl!glEnable(GL_DEPTH_TEST);
        checkgl!glCullFace(GL_BACK);

        _scene.drawScene(false);

        checkgl!glDisable(GL_DEPTH_TEST);
        checkgl!glDisable(GL_CULL_FACE);
    }

    ~this() {
    }
}
