/******************************************************************

  Module: TiCircleDraw.cpp

  Author: Jon Wiesman

  Description:

    class to draw a pie slice of a circle based on a source texture and a beginning and ending
    set of radians

  Copyright 2006 Sony Online Entertainment.  All rights reserved.

*******************************************************************/

#include "TiCircleDraw.h"
#include "TiUI.h"

TiCircleDraw::TiCircleDraw()
{
  m_idSurface = 0;
  m_fBeginSlice = 0.f;
  m_fEndSlice = 0.f;

  for(int i = 0; i < c_nMaxTriangles; i++)
  {
    m_bValidSlice[i] = false;
    for(int iPt = 0; iPt < 3; iPt++)
    {
      m_arScreen[i][iPt].Set(0, 0);
      m_arSurface[i][iPt].Set(0, 0);
    }
  }
}

TiPoint TiCircleDraw::GetProjectedPoint(TiSize size, float32 fAngle)
{
  TiPoint pt;
  TiRect rectOrigin(TiPoint(0, 0), size);


  while(fAngle < 0.f)
    fAngle += (SY_PI * 2.f);
  while(fAngle > SY_PI * 2.f)
    fAngle -= (SY_PI * 2.f);

  TiPoint ptCenter = rectOrigin.CenterPoint();
  TiSize sizeHalf = rectOrigin.GetSize() / 2;

  if(fAngle == 0.f)
  {
    return TiPoint(rectOrigin.right, ptCenter.y);
  }
  if(fAngle == SY_PI)
  {
    return TiPoint(rectOrigin.left, ptCenter.y);
  }

  int32 iOctant = int(fAngle / (SY_PI / 4.f));

  if(iOctant % 2 == 0)
  {
    fAngle -= ((float32)(iOctant / 2) * (SY_PI / 2.f));
  }
  else
  {
    fAngle -= ((float32)(iOctant / 2) * SY_PI / 2.f);
    fAngle = (SY_PI / 2.f) - fAngle;
  }
  float fTan = tanf(fAngle);
  
  int32 nTanX = SY_ABS(int(fTan * (float)sizeHalf.cx));
  int32 nTanY = SY_ABS(int(fTan * (float)sizeHalf.cy));



  switch(iOctant)
  {
  case 0:
    pt.x = rectOrigin.right;
    pt.y = ptCenter.y - nTanY;
    break;

  case 1:
    pt.x = ptCenter.x + nTanX;
    pt.y = rectOrigin.top;
    break;

  case 2:
    pt.x = ptCenter.x - nTanX;
    pt.y = rectOrigin.top;
    break;

  case 3:
    pt.x = rectOrigin.left;
    pt.y = ptCenter.y - nTanY;
    break;

  case 4:
    pt.x = rectOrigin.left;
    pt.y = ptCenter.y + nTanY;
    break;

  case 5:
    pt.x = ptCenter.x - nTanX;
    pt.y = rectOrigin.bottom;
    break;

  case 6:
    pt.x = ptCenter.x + nTanX;
    pt.y = rectOrigin.bottom;
    break;

  case 7:
    pt.x = rectOrigin.right;
    pt.y = ptCenter.y + nTanY;
    break;
  }

  return pt;
}

TiPoint TiCircleDraw::GetProjectedPoint(float32 fAngle) const
{
  return GetProjectedPoint(m_rect.GetSize(), fAngle);
}

void TiCircleDraw::SetRect(TiRect rect)
{
  TiPoint ptCenter = rect.CenterPoint();

  m_rect = rect;
  SetPoints();
}

void TiCircleDraw::SetAngles(float32 fBegin, float32 fEnd)
{
  if(fBegin != m_fBeginSlice || fEnd != m_fEndSlice)
  {
    m_fBeginSlice = fBegin;
    while(m_fBeginSlice < 0.f)
      m_fBeginSlice += (SY_PI * 2.f);
    while(m_fBeginSlice > SY_PI * 2.f)
      m_fBeginSlice -= (SY_PI * 2.f);

    m_fEndSlice = fEnd;
    while(m_fEndSlice < m_fBeginSlice)
      m_fEndSlice += (SY_PI * 2.f);

    SetPoints();
  }
}

void TiCircleDraw::SetPoints()
{
  TiPoint ptCenter = m_rect.CenterPoint();

  float32 fBeginOctant = floorf(m_fBeginSlice / (SY_PI / 4.f));

  float32 fAngleMin = fBeginOctant * (SY_PI / 4.f);
  float32 fAngleMax = fAngleMin;
  bool bBeginPassed = false;
  bool bEndPassed = false;


  for(int iOctant = 0; iOctant < c_nMaxTriangles; iOctant++)
  {
    fAngleMax += (SY_PI / 4.f);

    m_bValidSlice[iOctant] = false;

    // second octant point is the 'trailing edge' (but we calc it first)
    if(bBeginPassed && !bEndPassed)
    {
      TiPoint ptBegin = GetProjectedPoint(fAngleMin);
      m_arScreen[iOctant][1] = m_rect.TopLeft() + ptBegin;
      m_arSurface[iOctant][1] = ptBegin;
      m_bValidSlice[iOctant] = true;
    }
    else if(!bBeginPassed && m_fBeginSlice < fAngleMax && !bEndPassed)
    {
      TiPoint ptBegin = GetProjectedPoint(m_fBeginSlice);
      m_arScreen[iOctant][1] = m_rect.TopLeft() + ptBegin;
      m_arSurface[iOctant][1] = ptBegin;
      bBeginPassed = true;
      m_bValidSlice[iOctant] = true;
    }
    else
    {

    }

    // first octant point is the 'leading edge' 
    if(bBeginPassed && m_fEndSlice >= fAngleMax)
    {
      // there are more triangles to go
      TiPoint ptEnd = GetProjectedPoint(fAngleMax);
      m_arScreen[iOctant][0] = m_rect.TopLeft() + ptEnd;
      m_arSurface[iOctant][0] = ptEnd;
    }
    else if(bBeginPassed && m_fEndSlice < fAngleMax)
    {
      // this is the last triangle
      TiPoint ptEnd = GetProjectedPoint(m_fEndSlice);
      m_arScreen[iOctant][0] = m_rect.TopLeft() + ptEnd;
      m_arSurface[iOctant][0] = ptEnd;
      bEndPassed = true;
    }

    // third octant point is always the center of the rectangle
    m_arScreen[iOctant][2] = m_rect.CenterPoint();
    m_arSurface[iOctant][2].Set(m_rect.Width() / 2, m_rect.Height() / 2);

    fAngleMin = fAngleMax;
  }

}

void TiCircleDraw::Init(TiRect rectScreen, const char8 *pszSurface, float32 fBegin, float32 fEnd)
{
  m_rect = rectScreen;
  m_idSurface = SyHashResourceID(pszSurface);
  SetAngles(fBegin, fEnd);
}

void TiCircleDraw::Draw() const
{
  for(int i = 0; i < c_nMaxTriangles; i++)
  {
    if(m_bValidSlice[i])
    {
      TiUI::Instance()->TriangleBlit(m_idSurface, m_arScreen[i], m_arSurface[i]);
    }
  }
}
