/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 *   This file is part of
 *       _______   ______________  ______     _____
 *      / ____/ | / /  _/ ____/  |/  /   |   |__  /
 *     / __/ /  |/ // // / __/ /|_/ / /| |    /_ <
 *    / /___/ /|  // // /_/ / /  / / ___ |  ___/ /
 *   /_____/_/ |_/___/\____/_/  /_/_/  |_| /____/.
 *
 *   Copyright © 2003-2010 Brain Control, all rights reserved.
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "../../eshared.hpp"
#include "lsys2.hpp"

#ifdef eDEBUG
#include <windows.h>
#include <stdio.h>
#endif


#if defined(HAVE_OP_MISC_LSYS2) || defined(eEDITOR)
OP_DEFINE(eLSys2Op, eLSys2Op_ID, eIGenericOp, "LSys2", Misc, Misc_CID, eColor(170, 170, 170), ' ', 0, 0, "")
    OP_INIT()
    {
        this->m_genData = &m_context;
		eOP_PARAM_ADD_STRING("Symbol", "A");
        eOP_PARAM_ADD_LINK("Model", "Model");
        eOP_PARAM_ADD_FLOAT("Mean Grow Time", 0.0f, eF32_MAX, 1.0f);
        eOP_PARAM_ADD_FLOAT("Grow Time Variation", 0.0f, 1.0f, 0.0f);
        eOP_PARAM_ADD_FLOAT("Oscillation", 0.0f, 1.0f, 0.0f);
        eOP_PARAM_ADD_FLOAT("Oscillation Period", eF32_MIN, eF32_MAX, 1.0f);
		eOP_PARAM_ADD_FXYZ("Oscillation Axis", -eF32_MAX, eF32_MAX, 1.0f, 0.0f, 0.0f);
		eOP_PARAM_ADD_BOOL("Hinge", eFALSE);
    }

    OP_EXEC(eGraphicsApiDx9 *gfx, const eChar *symbol, eIModelOp* modelOp, eF32 growMean, eF32 growVar, eF32 oscillation, eF32 oscillationPeriod, const eVector3 &oscillationAxis, eBool hinge)
    {
        m_context = eLSys2_Context();

        m_symbol.setProperties((modelOp == eNULL) ? eNULL : &modelOp->getResult().sceneData,
                               oscillation,
                               oscillationAxis,
                               oscillationPeriod,
                               hinge);

        eU32 symbolNr = (symbol == "") ? 0 : symbol[0];
        m_context.setSymbol(symbolNr, &m_symbol);
        m_symbol.setGrowProperties(growMean, growVar);
    }

    eLSys2_Context          m_context;
    eLsys2_Symbol_Cylinder  m_symbol;
OP_END(eLSys2Op);
#endif


#if defined(HAVE_OP_MISC_LSYS2RULE) || defined(eEDITOR)
OP_DEFINE(eLSys2RuleOp, eLSys2RuleOp_ID, eIGenericOp, "LSys2Rule", Misc, Misc_CID, eColor(170, 170, 170), ' ', 1, 1, "Misc")
    OP_INIT()
    {
		eOP_PARAM_ADD_STRING("Axiom", "A");
		eOP_PARAM_ADD_STRING("Condition", "");
        eOP_PARAM_ADD_TEXT("Production", "AA");
    }

    OP_EXEC(eGraphicsApiDx9 *gfx, const eChar *axiom, const eChar *condition, const eChar *production)
    {
        // copy context pointer
        this->m_genData = ((eIGenericOp *)getInputOperator(0))->getResult().genericDataPtr;
        eLSys2_Context& context = *((eLSys2_Context*)m_genData);
        
        eU32 symbolNr = (axiom == "") ? 0 : axiom[0];
        eLsys2_Symbol* symbol = context.getSymbol(symbolNr);
        
        symbol->removeRule(&m_rule);

        m_rule.set(axiom, condition, production);

        symbol->addRule(&m_rule);
    }

    eLsys2_Rule m_rule;
OP_END(eLSys2RuleOp);
#endif


#if defined(HAVE_OP_MISC_LSYS2MERGEDEF) || defined(eEDITOR)
OP_DEFINE(eLSys2MergeDefOp, eLSys2MergeDefOp_ID, eIGenericOp, "LSys2MergeDef", Misc, Misc_CID, eColor(170, 170, 170), ' ', 1, -1, "Misc")
    OP_INIT()
    {
        this->m_genData = &m_context;
    }

    OP_EXEC(eGraphicsApiDx9 *gfx) {
        m_context = eLSys2_Context();
        for(eU32 i = 0; i < this->getInputCount(); i++) {
            eLSys2_Context& context = *((eLSys2_Context*)((eIGenericOp *)getInputOperator(i))->getResult().genericDataPtr);
            this->m_context.merge(context);
        }
    }

    eLSys2_Context          m_context;
OP_END(eLSys2MergeDefOp);
#endif

#if defined(HAVE_OP_MISC_LSYS2INSTANCE) || defined(eEDITOR)
OP_DEFINE(eLSys2InstanceOp, eLSys2InstanceOp_ID, eIGenericOp, "LSys2Instance", Misc, Misc_CID, eColor(170, 170, 170), ' ', 1, 1, "Misc")
    OP_INIT()
    {
        this->m_genData = &m_drawState;
		eOP_PARAM_ADD_STRING("Axiom", "A");
        eOP_PARAM_ADD_FLOAT("GrowTime", 0.0f, eF32_MAX, 1.0f);
        eOP_PARAM_ADD_FLOAT("Decay", 0.0f, eF32_MAX, 1.0f);
        eOP_PARAM_ADD_FLOAT("Rot X", -eF32_MAX, eF32_MAX, 0.1f);
        eOP_PARAM_ADD_FLOAT("Rot Y", -eF32_MAX, eF32_MAX, 0.1f);
        eOP_PARAM_ADD_FLOAT("Rot Z", -eF32_MAX, eF32_MAX, 0.1f);
        eOP_PARAM_ADD_FLOAT("Scale", 0.0f, eF32_MAX, 1.0f);
        eOP_PARAM_ADD_FLOAT("Gravity", -eF32_MAX, eF32_MAX, 0.0f);
		eOP_PARAM_ADD_FXYZ("Rotation", -eF32_MAX, eF32_MAX, 0.0f, 0.0f, 0.0f);
        eOP_PARAM_ADD_LINK("Physics", "Misc");
    }

    OP_EXEC(eGraphicsApiDx9 *gfx, const eChar *axiom, eF32 growTime, eF32 decay, eF32 rotX, eF32 rotY, eF32 rotZ, eF32 scale, eF32 gravity, const eVector3 &origRotation, eIGenericOp* physicsOp)
    {
        ePhysics* physics = (physicsOp == eNULL) ? eNULL : ((ePhysics*)physicsOp->getResult().genericDataPtr);
        if(getInputOperator(0)->getChanged()) {
            m_drawState = DrawState_X();
            // copy context 
            m_context = eLSys2_Context();
            m_context.merge(*((eLSys2_Context*)((eIGenericOp *)getInputOperator(0))->getResult().genericDataPtr));

            eU32 p = 0;
            while(axiom[p] != 0) {
                eU32 symNr = axiom[p++];
                m_context.addInstance(&m_drawState, symNr, -1);
            }
        }
        m_context.param(CTX_ROTATION_UNIT_X) = rotX;
        m_context.param(CTX_ROTATION_UNIT_Y) = rotY;
        m_context.param(CTX_ROTATION_UNIT_Z) = rotZ;
        m_context.param(CTX_DECAY) = decay;
        m_context.param(CTX_GRAVITY) = gravity;

        eQuat rotQuat;
        rotQuat.fromRotation(origRotation * ePI);
        m_drawState.reset(scale, eVector3(0,0,0), rotQuat, physics);
        m_context.evaluateSingleInstance(growTime, &m_drawState);

        m_drawState.finish();

    }
    
    DrawState_X         m_drawState;
    eLSys2_Context      m_context;
OP_END(eLSys2InstanceOp);
#endif

#if defined(HAVE_OP_MISC_LSYS2PHYSICS) || defined(eEDITOR)
OP_DEFINE(eLSys2PhysicsOp, eLSys2PhysicsOp_ID, eIGenericOp, "LSys2Physics", Misc, Misc_CID, eColor(170, 170, 170), ' ', 1, 1, "Misc")
    OP_INIT()
    {
        this->m_genData = eNULL;
    }
    OP_EXEC(eGraphicsApiDx9 *gfx)
    {
        DrawState_X& ds = *((DrawState_X*)((eIGenericOp *)getInputOperator(0))->getResult().genericDataPtr);
        this->m_genData = ds.m_physics;
    }
OP_END(eLSys2PhysicsOp);
#endif


#if defined(HAVE_OP_MODEL_LSYS2RENDERING) || defined(eEDITOR)
OP_DEFINE_MODEL(eModelLSys2RenderingOp, eModelLSys2RenderingOp_ID, "LSys2Rendering", 'p', 1, 1, "Misc")
    OP_INIT()
    {
        eOP_PARAM_ADD_FLOAT("Time", 0.0f, eF32_MAX, 0.0f);
    }

    OP_EXEC(eGraphicsApiDx9 *gfx, eF32 time)
    {
        this->m_sceneData.setStatic(eFALSE);
        DrawState_X& ds = *((DrawState_X*)((eIGenericOp *)getInputOperator(0))->getResult().genericDataPtr);
        m_sceneData.merge(ds.m_mainSd);
/*
        ds.m_physics.setGravity(eVector3(0,-9.81f,0));
        ds.m_physics.update(time);
        for(eU32 i = 0; i < ds.m_physics.m_components.size(); i++) {
            Object* obj = ds.m_physics.m_components[i];
            eSceneData* model = (eSceneData*)obj->getObjectData();
            eVector3 opos = ePhysics::convert(obj->get_position());
            eMatrix4x4 rotation = ePhysics::convert(obj->get_orientation());
            eMatrix4x4 mtx;
            mtx.translate(-model->getBoundingBox().getCenter());
            mtx *= rotation;
            mtx.translate(opos);
            m_sceneData.merge(*model, mtx);
        }
*/

        for(eU32 i = 0; i < ds.m_enabledHinges.size(); i++) {
            Hinge& o = ds.m_hinges[ds.m_enabledHinges[i]];
            eMatrix4x4 mtx(eQuat(o.m_axis.normalized(), ePI * o.m_amount * (eSin(2.0f * ePI * (time + o.m_offset)/ o.m_period))));
            o.m_sd.transform(mtx);
        }
    }
    
OP_END(eModelLSys2RenderingOp);

#endif

