/*
* Author: Chris Campbell - www.iforce2d.net
*
* This software is provided 'as-is', without any express or implied
* warranty.  In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/

#include <cstring>
#include <Box2D/Box2D.h>
#include "b2dJsonImage.h"
#include "bitmap.h"

// helper functions

void _setMat33Translation(b2Mat33& mat, b2Vec2 t)
{
    mat.SetZero();
    mat.ex.x = 1;
    mat.ey.y = 1;
    mat.ez.x = t.x;
    mat.ez.y = t.y;
    mat.ez.z = 1;
}

void _setMat33Rotation(b2Mat33& mat, float angle)
{
    mat.SetZero();
    float32 c = cosf(angle), s = sinf(angle);
    mat.ex.x = c; mat.ey.x = -s;
    mat.ex.y = s; mat.ey.y = c;
    mat.ez.z = 1;
}

void _setMat33Scale(b2Mat33& mat, float xfactor, float yfactor)
{
    mat.SetZero();
    mat.ex.x = xfactor;
    mat.ey.y = yfactor;
    mat.ez.z = 1;
}

b2Vec2 _b2Mul(const b2Mat33& A, const b2Vec2& v2)
{
    b2Vec3 v(v2.x, v2.y, 1);
    b2Vec3 r = v.x * A.ex + v.y * A.ey + v.z * A.ez;
    return b2Vec2(r.x, r.y);
}

b2Mat33 _b2Mul(const b2Mat33& B, const b2Mat33& A)
{
    return b2Mat33(b2Mul(A, B.ex), b2Mul(A, B.ey), b2Mul(A, B.ez));
}

///////////////////

b2dJsonImage::b2dJsonImage()
{
    m_body = NULL;
    m_center.SetZero();
    m_angle = 0;
    m_scale = 1;
    m_flip = false;
    m_filter = FT_LINEAR;
    m_opacity = 1;
    m_renderOrder = 0;

    m_numPoints = 0;
    m_points = NULL;
    m_uvCoords = NULL;
    m_numIndices = 0;
    m_indices = NULL;
}

b2dJsonImage::~b2dJsonImage()
{
    if (m_points) delete[] m_points;
    if (m_uvCoords) delete[] m_uvCoords;
    if (m_indices) delete[] m_indices;
}

b2dJsonImage::b2dJsonImage(const b2dJsonImage *other)
{
    m_body = other->m_body;
    m_name = other->m_name;
    m_file = other->m_file;
    m_center = other->m_center;
    m_angle = other->m_angle;
    m_scale = other->m_scale;
    m_flip = other->m_flip;
    m_filter = other->m_filter;
    m_opacity = other->m_opacity;
    m_renderOrder = other->m_renderOrder;

    memcpy(m_corners, other->m_corners, 4 * sizeof(b2Vec2));

    m_numPoints = other->m_numPoints;
    m_points = new float[2 * m_numPoints];
    m_uvCoords = new float[2 * m_numPoints];
    memcpy(m_points, other->m_points, 2 * m_numPoints * sizeof(float));
    memcpy(m_uvCoords, other->m_uvCoords, 2 * m_numPoints * sizeof(float));
    m_numIndices = other->m_numIndices;
    m_indices = new unsigned int[m_numIndices];
    memcpy(m_indices, other->m_indices, m_numIndices * sizeof(unsigned int));
}

void b2dJsonImage::updateCorners(float aspect)
{
    float hx = 0.5 * aspect;
    float hy = 0.5;

    m_corners[0].Set(-hx, -hy);
    m_corners[1].Set( hx, -hy);
    m_corners[2].Set( hx,  hy);
    m_corners[3].Set(-hx,  hy);

    b2Mat33 r, s;
    _setMat33Rotation(r, m_angle);
    _setMat33Scale(s, m_scale, m_scale);
    b2Mat33 m = _b2Mul(r,s);

    for (int i = 0; i < 4; i++) {
        m_corners[i] = _b2Mul(m, m_corners[i]);
        m_corners[i] += m_center;
    }
}

void b2dJsonImage::updateUVs(float aspect)
{
    //set up vertices

    float hx = 0.5 * aspect;
    float hy = 0.5;

    b2Vec2 verts[4];
    verts[0].Set(-hx, -hy);
    verts[1].Set( hx, -hy);
    verts[2].Set( hx,  hy);
    verts[3].Set(-hx,  hy);

    b2Mat33 r, s;
    _setMat33Rotation(r, m_angle);
    _setMat33Scale(s, m_scale, m_scale);
    b2Mat33 m = _b2Mul(r,s);

    for (int i = 0; i < 4; i++) {
        verts[i] = _b2Mul(m, verts[i]);
        verts[i] += m_center;
    }

    //set up uvs

    b2Vec2 uvs[4];
    uvs[0].Set(0, 0);
    uvs[1].Set(1, 0);
    uvs[2].Set(1, 1);
    uvs[3].Set(0, 1);

    //set up arrays for rendering

    m_numPoints = 4;
    m_numIndices = 6;

    if (m_points) delete[] m_points;
    if (m_uvCoords) delete[] m_uvCoords;
    if (m_indices) delete[] m_indices;
    m_points = new float[2 * m_numPoints];
    m_uvCoords = new float[2 * m_numPoints];
    m_indices = new unsigned int[m_numIndices];

    for (int i = 0; i < m_numPoints; i++) {
        m_points[2*i+0] = verts[i].x;
        m_points[2*i+1] = verts[i].y;
        m_uvCoords[2*i+0] = uvs[i].x;
        m_uvCoords[2*i+1] = uvs[i].y;
    }
    m_indices[0] = 0;
    m_indices[1] = 1;
    m_indices[2] = 2;
    m_indices[3] = 2;
    m_indices[4] = 3;
    m_indices[5] = 0;

}

b2AABB b2dJsonImage::getAABB()
{
    b2Mat33 r, t, m;
    if ( m_body ) {
        _setMat33Rotation(r, m_body->GetAngle());
        _setMat33Translation(t, m_body->GetPosition());
        m = _b2Mul(r,t);
    }
    else
        m = b2Mat33( b2Vec3(1,0,0), b2Vec3(0,1,0), b2Vec3(0,0,1) ); //identity matrix

    b2AABB aabb;
    aabb.lowerBound.Set(FLT_MAX, FLT_MAX);
    aabb.upperBound.Set(-FLT_MAX, -FLT_MAX);
    for (int i = 0; i < 4; i++) {
        aabb.lowerBound = b2Min(aabb.lowerBound, _b2Mul(m, m_corners[i]));
        aabb.upperBound = b2Max(aabb.upperBound, _b2Mul(m, m_corners[i]));
    }
    return aabb;
}




