#include "stdafx.h"

#include "GameObject3D.h"
#include "DxWrapper.h"
#include "PnxUtility.h"
#include "AssimpRenderer.h"
#include "aiMatrix4x4.h"
#include "aiVector3D.h"


//ctors
GameObject3D::GameObject3D()
{
    Initialize();
}

GameObject3D::GameObject3D(LPCTSTR file, LPCTSTR path, float calibrationAngleX, float calibrationAngleY, float calibrationAngleZ)
{
    Initialize();
    this->m_Model.calibrationAngleX = calibrationAngleX;
    this->m_Model.calibrationAngleY = calibrationAngleY;
    this->m_Model.calibrationAngleZ = calibrationAngleZ;
    DxWrapper::LoadModel(&this->m_Model, file, path);
    InitializeBoundingBoxByModel();
}


GameObject3D::GameObject3D(const Model &model)
{
    Initialize();
    this->m_Model = model;              // auf das member Model kopieren
}


GameObject3D::~GameObject3D()
{
	if (m_AssetHelper)
		delete m_AssetHelper;
}



void GameObject3D::Initialize()
{
    m_Velocity = CVector(0,0,0);

    m_RotX = 0;
    m_RotY = 0;
    m_RotZ = 90.0f;
    m_Active = false;
    m_ScaleFactorX = 1.0f;
    m_ScaleFactorY = 1.0f;
    m_ScaleFactorZ = 1.0f;    
	m_AssetHelper = NULL;
}

/** Initialisiert eine OBB-Hitbox anhand der minimalen und maximalen Vertex-Position. 
*/
void GameObject3D::InitializeBoundingBoxByModel()
{
    IvVector3 minBounds(this->m_Model.minBound.x * m_ScaleFactorX, this->m_Model.minBound.y * m_ScaleFactorY, this->m_Model.minBound.z * m_ScaleFactorZ);
    IvVector3 maxBounds(this->m_Model.maxBound.x * m_ScaleFactorX, this->m_Model.maxBound.y * m_ScaleFactorY, this->m_Model.maxBound.z * m_ScaleFactorZ);
    IvVector3 center(maxBounds + minBounds);    // den mittelpunkt berechnen
    
    this->m_BoundingBox.SetExtents(maxBounds - center);

    std::cout << "center=" << center << "  obb getExtents=" << m_BoundingBox.GetExtents() << std::endl;
}


void GameObject3D::Draw()
{
	if (m_AssetHelper)
	{
		aiVector3D vPos(m_Position.x, m_Position.y, m_Position.z);
		aiVector3D vRot(m_RotX, m_RotY, m_RotZ);
		aiVector3D vScale(m_ScaleFactorX, m_ScaleFactorY, m_ScaleFactorZ);
		AssimpRendererD3d::AssimpRenderer::Instance()->RenderAsset(*m_AssetHelper, vPos, vRot, vScale);
	}
	
	else
		DxWrapper::Instance()->RenderObject(this);
}

void GameObject3D::UpdatePosition( float deltaMs )
{    
    m_LastPosition = m_Position;    // save old position for calculation the ray traveled

    m_Position.x += (float)(deltaMs * m_Velocity.x);
    m_Position.y += (float)(deltaMs * m_Velocity.y);
    m_Position.z += (float)(deltaMs * m_Velocity.z);

    // save the ray just traveled for collision check
    IvVector3 ivPosCurrent(m_Position.x , m_Position.y, m_Position.z);
    IvVector3 ivPosLast(m_LastPosition.x , m_LastPosition.y, m_LastPosition.z);
    m_RayTraveledInLastCycle.Set(ivPosLast, ivPosCurrent);

    UpdateBoundingBox();
}


void GameObject3D::UpdateBoundingBox()
{
    IvVector3 ivecTranslate(m_Position.x, m_Position.y, m_Position.z);
    m_BoundingBox.SetCenter(ivecTranslate);
    IvMatrix33 matrixRotX;
    IvMatrix33 matrixRotY;
    IvMatrix33 matrixRotZ;
    matrixRotX.RotationX(static_cast<float>(PNX_ToRadian(m_RotX + m_Model.calibrationAngleX)));
    matrixRotY.RotationY(static_cast<float>(PNX_ToRadian(m_RotY + m_Model.calibrationAngleY)));
    matrixRotZ.RotationZ(static_cast<float>(PNX_ToRadian(m_RotZ + m_Model.calibrationAngleZ)));
    m_BoundingBox.SetRotation(matrixRotX * matrixRotY * matrixRotZ);
}

/** die Methode hier bastelt eine hitbox um den im letzten Frame zurückgelegten Flugpfad
*/
IvOBB GameObject3D::GetBoundingBoxOfFlightPath()
{
    IvOBB pathBox;
    IvVector3 lastCenter(m_LastPosition.x, m_LastPosition.y, m_LastPosition.z);
    IvVector3 currentCenter(m_Position.x, m_Position.y, m_Position.z);    
    IvMatrix33 matRotation = m_BoundingBox.GetRotation();
    IvVector3 boundingBoxExtents = m_BoundingBox.GetExtents();

    IvVector3 pathCenter = lastCenter + ((currentCenter - lastCenter) / 2);
    IvVector3 pathCenterToCurrentCenter(currentCenter - pathCenter);
    
    IvVector3 pathExtent(pathCenterToCurrentCenter + m_BoundingBox.GetExtents() * matRotation);   
    // jetzt hat der patchExtent-Vektor die richtige Länge, ist aber noch auf weltkoordinaten gedreht. Deshalb den Vektor noch zurückdrehen:            
    IvMatrix33 backRotX, backRotY, backRotZ;
    backRotX.RotationX(static_cast<float>(PNX_ToRadian(-m_RotX)));
    backRotY.RotationY(static_cast<float>(PNX_ToRadian(-m_RotY)));
    backRotZ.RotationZ(static_cast<float>(PNX_ToRadian(-m_RotZ)));
    pathExtent =  pathExtent * (backRotX * backRotY * backRotZ);

    pathBox.SetCenter(pathCenter);

    // beim Berechnen der Vektoren hab ich die Rotationsmatrix benutzt, in der die Kalibrierungswinkel schon drin waren, deshalb muss ich
    // jetzt wohl die Rotation des flugpfad-box jetzt auf die reinen rotationswinkel setzen (nicht ganz kapiert, warum)
    IvMatrix33 matrixRotX;
    IvMatrix33 matrixRotY;
    IvMatrix33 matrixRotZ;
    matrixRotX.RotationX(static_cast<float>(PNX_ToRadian(m_RotX)));
    matrixRotY.RotationY(static_cast<float>(PNX_ToRadian(m_RotY)));
    matrixRotZ.RotationZ(static_cast<float>(PNX_ToRadian(m_RotZ)));

    pathBox.SetRotation(matrixRotX * matrixRotY * matrixRotZ);
    pathBox.SetExtents(pathExtent);
    
    //std::cout << "currentCenter=" << currentCenter << "  lastCenter="  << lastCenter << "  pathCenter="  << pathCenter << "  pathExtent=" << pathExtent << "   pathExtent length=" << pathExtent.Length() << std::endl;

    //DxWrapper::Instance()->DrawBoundingBox(pathBox);
    return pathBox;    
}