/*--------------------------------------------------------------------------
  Project:  Manual 2D Library - m2d
  File:     m2d_window.cpp

  Copyright (C)2007-2009 Nintendo  All Rights Reserved.

  These coded instructions, statements, and computer programs contain
  proprietary information of Nintendo of America Inc. and/or Nintendo
  Company Ltd., and are protected by Federal copyright law. They may
  not be disclosed to third parties or copied or duplicated in any form,
  in whole or in part, without the prior written consent of Nintendo.

  $Revision: 1.44 $
 *-------------------------------------------------------------------------*/

#include <nitro.h>
#include <nnsys/misc.h>
#include <ntmv/m2d/debug.h>
#include <ntmv/m2d/common.h>
#include <ntmv/m2d/drawInfo.h>
#include <ntmv/m2d/page.h>
#include <ntmv/m2d/objectConstructor.h>
#include <ntmv/m2d/window.h>
#include <ntmv/m2d/resourceAccessor.h>


namespace ntmv
{
namespace m2d
{

namespace
{

void
GetLTFrameSize(
    Point*                      pPt,
    Size*                       pSize,
    const Point                 basePt,
    const Size                  winSize,
    const detail::FrameSize&    frameSize
)
{
    *pPt = basePt;

    pSize->width = static_cast<s16>(winSize.width - frameSize.r);
    pSize->height = frameSize.t;
}

void
GetLTTexCoord(
    Point&                          texCd,
    const Size&                     /* polSize */,
    const Size&                     texSize,
    const detail::TextureFlipInfo&  flipInfo
)
{
    detail::GetTexCoord(texCd, texSize, flipInfo);
}

void
GetRTFrameSize(
    Point*                      pPt,
    Size*                       pSize,
    const Point&                basePt,
    const Size&                 winSize,
    const detail::FrameSize&    frameSize
)
{
    *pPt = Point(static_cast<s16>(basePt.x + winSize.width - frameSize.r), basePt.y);

    pSize->width  = frameSize.r;
    pSize->height = static_cast<s16>(winSize.height - frameSize.b);
}

void
GetRTTexCoord(
    Point&                          texCd,
    const Size&                     /* polSize */,
    const Size&                     texSize,
    const detail::TextureFlipInfo&  flipInfo
)
{
    const int ix = flipInfo.swap ? 1: 0;
    const int iy = (ix + 1) % 2;

    texCd[ix] = static_cast<s16>(flipInfo.coords[ix] * (texSize[ix] - 1));
    texCd[iy] = static_cast<s16>(flipInfo.coords[iy] * (texSize[iy] - 1));
}

void
GetLBFrameSize(
    Point*                      pPt,
    Size*                       pSize,
    const Point&                basePt,
    const Size&                 winSize,
    const detail::FrameSize&    frameSize
)
{
    *pPt = Point(basePt.x, static_cast<s16>(basePt.y + frameSize.t));

    pSize->width  = frameSize.l;
    pSize->height = static_cast<s16>(winSize.height - frameSize.t);
}

void
GetLBTexCoord(
    Point&                          texCd,
    const Size&                     polSize,
    const Size&                     texSize,
    const detail::TextureFlipInfo&  flipInfo
)
{
    const int ix = flipInfo.swap ? 1: 0;
    const int iy = (ix + 1) % 2;

    texCd[ix] = static_cast<s16>(flipInfo.coords[ix] * (texSize[ix] - 1));
    texCd[iy] = static_cast<s16>((flipInfo.coords[iy] + flipInfo.dir[iy]) * (texSize[iy] - 1) - (polSize.height - 1) * flipInfo.dir[iy]);
}

void
GetRBFrameSize(
    Point*                      pPt,
    Size*                       pSize,
    const Point&                basePt,
    const Size&                 winSize,
    const detail::FrameSize&    frameSize
)
{
    *pPt = Point(static_cast<s16>(basePt.x + frameSize.l), static_cast<s16>(basePt.y + winSize.height - frameSize.b));

    pSize->width  = static_cast<s16>(winSize.width - frameSize.l);
    pSize->height = frameSize.b;
}

void
GetRBTexCoord(
    Point&                          texCd,
    const Size&                     polSize,
    const Size&                     texSize,
    const detail::TextureFlipInfo&  flipInfo
)
{
    const int ix = flipInfo.swap ? 1: 0;
    const int iy = (ix + 1) % 2;

    texCd[ix] = static_cast<s16>((flipInfo.coords[ix] + flipInfo.dir[ix]) * (texSize[ix] - 1) - (polSize.width - 1) * flipInfo.dir[ix]);
    texCd[iy] = static_cast<s16>(flipInfo.coords[iy] * (texSize[iy] - 1));
}

}   // namespace

/*---------------------------------------------------------------------------*
  Name:         Window::Frame::Finalize

  Description:  Performs shutdown.

  Arguments:    objectConstructor: A reference to an ObjectConstructor object.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void
Window::Frame::Finalize(const ObjectConstructor& /* objectConstructor */)
{
    NTMV_LOG("Window::Frame::Finalize()\n");
}

NTMV_RNT_RUNTIME_TYPEINFO_DEFINITION(Window, Window::Base);    // Defines an instance of runtime type information

/*---------------------------------------------------------------------------*
  Name:         Window::Window

  Description:  Constructor.
                Generates a Window object from a resource.

  Arguments:    objectConstructor: A reference to an ObjectConstructor object.
                pBlock: Pointer to the resource.
                pResAcsr: A pointer to the ResourceAccessor object.

  Returns:      None.
 *---------------------------------------------------------------------------*/
Window::Window(
    const ObjectConstructor&    objectConstructor,
    const res::Window*          pBlock,
    const ResBlockSet&          resBlockSet
)
:   Base(pBlock, resBlockSet)
{
    // content
    // Color
    mContent.color = pBlock->content.color;

    // Frame
    mFrameNum = 0;
    mFrames = 0;
    if (pBlock->frameNum > 0)
    {
        InitFrame(objectConstructor, pBlock->frameNum);

        for (int i = 0; i < mFrameNum; ++i)
        {
            const res::WindowFrame& resFrame = pBlock->frames[i];

            // TexMap
            NNS_ASSERT(resFrame.texMap.texIdx < resBlockSet.pTextureList->texNum);
            mFrames[i].texMap.Set(&resBlockSet.pTextureList->texList[resFrame.texMap.texIdx]);

            mFrames[i].textureFlip = resFrame.texMap.textureFlip;
        }
    }
}

void
Window::InitFrame(
    const ObjectConstructor&    objectConstructor,
    u8                          frameNum
)
{
    mFrameNum = 0;
    mFrames = objectConstructor.NewArray<Frame>(frameNum);
    NNS_NULL_ASSERT(mFrames);

    mFrameNum = frameNum;
}

/*---------------------------------------------------------------------------*
  Name:         Window::Finalize

  Description:  Performs shutdown.

  Arguments:    objectConstructor: A reference to an ObjectConstructor object.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void
Window::Finalize(const ObjectConstructor& objectConstructor)
{
    #if defined(NTMV_M2D_NAMEEMBEDDED)
        NTMV_LOG("Window::Finalize() - [%s]\n", mName);
    #endif

    objectConstructor.DeleteArray(mFrames, mFrameNum);

    Base::Finalize(objectConstructor);
}

void
Window::DrawSelf(const DrawInfo& drawInfo)
{
    const detail::FrameSize frameSize = GetFrameSize(mFrameNum, mFrames);

    // content rendering
    DrawContent(drawInfo, mPosition, frameSize);

    // frame rendering
    switch (mFrameNum)
    {
    case 1:
        DrawFrame(drawInfo, mPosition, mFrames[WINDOWFRAME_LT], frameSize);
        break;

    case 4:
        DrawFrame4(drawInfo, mPosition, mFrames, frameSize);
        break;
    }
}

void
Window::DrawContent(
    const DrawInfo&             drawInfo,
    const Point                 basePt,
    const detail::FrameSize&    frameSize
)
{
    if (mContent.color & GX_RGBA_A_MASK)
    {
        detail::FillColor(
            drawInfo,
            Point(static_cast<s16>(basePt.x + frameSize.l), static_cast<s16>(basePt.y + frameSize.t)),
            Size(static_cast<s16>(mSize.width - frameSize.l - frameSize.r), static_cast<s16>(mSize.height - frameSize.t - frameSize.b)),
            mContent.color);
    }
}

void
Window::DrawFrame(
    const DrawInfo& drawInfo,
    const Point                 basePt,
    const Frame&                frame,
    const detail::FrameSize&    frameSize
)
{
    const u8 texCoordNum = 1;

    const Size texSize = frame.texMap.GetSize();
    const TexMap& texMap = frame.texMap;
    Point texCd;
    Point polPt;
    Size polSize;

    {
        GetLTFrameSize(&polPt, &polSize, basePt, mSize, frameSize);
        const detail::TextureFlipInfo& flipInfo = detail::GetTexutreFlipInfo(TEXTUREFLIP_NONE);
        GetLTTexCoord(texCd, polSize, texSize, flipInfo);
        detail::DrawImage(drawInfo, polPt, polSize, texMap, texCd, flipInfo);
    }

    {
        GetRTFrameSize(&polPt, &polSize, basePt, mSize, frameSize);
        const detail::TextureFlipInfo& flipInfo = detail::GetTexutreFlipInfo(TEXTUREFLIP_FLIPH);
        GetRTTexCoord(texCd, polSize, texSize, flipInfo);
        detail::DrawImage(drawInfo, polPt, polSize, texMap, texCd, flipInfo);
    }

    {
        GetRBFrameSize(&polPt, &polSize, basePt, mSize, frameSize);
        const detail::TextureFlipInfo& flipInfo = detail::GetTexutreFlipInfo(TEXTUREFLIP_ROTATE180);
        GetRBTexCoord(texCd, polSize, texSize, flipInfo);
        detail::DrawImage(drawInfo, polPt, polSize, texMap, texCd, flipInfo);
    }

    {
        GetLBFrameSize(&polPt, &polSize, basePt, mSize, frameSize);
        const detail::TextureFlipInfo& flipInfo = detail::GetTexutreFlipInfo(TEXTUREFLIP_FLIPV);
        GetLBTexCoord(texCd, polSize, texSize, flipInfo);
        detail::DrawImage(drawInfo, polPt, polSize, texMap, texCd, flipInfo);
    }
}

void
Window::DrawFrame4(
    const DrawInfo&             drawInfo,
    const Point                 basePt,
    const Frame*                frames,
    const detail::FrameSize&    frameSize
)
{
    const u8 texCoordNum = 1;

    Point texCd;
    Point polPt;
    Size polSize;

    {
        GetLTFrameSize(&polPt, &polSize, basePt, mSize, frameSize);
        const detail::TextureFlipInfo& flipInfo = detail::GetTexutreFlipInfo(frames[WINDOWFRAME_LT].textureFlip);
        GetLTTexCoord(texCd, polSize, frames[WINDOWFRAME_LT].texMap.GetSize(), flipInfo);
        detail::DrawImage(drawInfo, polPt, polSize, frames[WINDOWFRAME_LT].texMap, texCd, flipInfo);
    }

    {
        GetRTFrameSize(&polPt, &polSize, basePt, mSize, frameSize);
        const detail::TextureFlipInfo& flipInfo = detail::GetTexutreFlipInfo(frames[WINDOWFRAME_RT].textureFlip);
        GetRTTexCoord(texCd, polSize, frames[WINDOWFRAME_RT].texMap.GetSize(), flipInfo);
        detail::DrawImage(drawInfo, polPt, polSize, frames[WINDOWFRAME_RT].texMap, texCd, flipInfo);
    }

    {
        GetRBFrameSize(&polPt, &polSize, basePt, mSize, frameSize);
        const detail::TextureFlipInfo& flipInfo = detail::GetTexutreFlipInfo(frames[WINDOWFRAME_RB].textureFlip);
        GetRBTexCoord(texCd, polSize, frames[WINDOWFRAME_RB].texMap.GetSize(), flipInfo);
        detail::DrawImage(drawInfo, polPt, polSize, frames[WINDOWFRAME_RB].texMap, texCd, flipInfo);
    }

    {
        GetLBFrameSize(&polPt, &polSize, basePt, mSize, frameSize);
        const detail::TextureFlipInfo& flipInfo = detail::GetTexutreFlipInfo(frames[WINDOWFRAME_LB].textureFlip);
        GetLBTexCoord(texCd, polSize, frames[WINDOWFRAME_LB].texMap.GetSize(), flipInfo);
        detail::DrawImage(drawInfo, polPt, polSize, frames[WINDOWFRAME_LB].texMap, texCd, flipInfo);
    }
}

const detail::FrameSize
Window::GetFrameSize(
    u8                      frameNum,
    const Window::Frame*    frames
)
{
    detail::FrameSize ret = { 0, 0, 0, 0 };

    switch (frameNum)
    {
    case 1:
        {
            Size texSize = frames[WINDOWFRAME_LT].texMap.GetSize();
            ret.l = texSize.width;
            ret.t = texSize.height;
            ret.r = texSize.width;
            ret.b = texSize.height;
        }
        break;
    case 4:
        {
            ret.t = frames[WINDOWFRAME_LT].texMap.GetSize().height;
            ret.r = frames[WINDOWFRAME_RT].texMap.GetSize().width;
            ret.b = frames[WINDOWFRAME_RB].texMap.GetSize().height;
            ret.l = frames[WINDOWFRAME_LB].texMap.GetSize().width;
        }
        break;
    }

    return ret;
}

}   // namespace m2d
}   // namespace ntmv
