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

                File        : PICKING.CPP
                Author      : Marc Villemain
                Last update : 13 September 1996

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


#include "tde.h"

// Precision de la souris en pixels
#define C_PREC_SOURIS TDE_M_FloatToValue((float)3.0) 

// Distance euclidienne entre deux points 2D
#define M_DistancePointPoint(A,B) TDE_M_Sqrt(TDE_M_Add(TDE_M_Sq(TDE_M_Sub(A->xX,B->xX)), \
                                                       TDE_M_Sq(TDE_M_Sub(A->xY,B->xY))))



static long lViewPortW, lViewPortH;                                        
static TDE_tdsSuperObjectMatrix stSprMatrix;

void fn_vChangeRepere ( TDE_tdsVertex *p_stP,
                        TDE_tdsVertex *p_stA,
                        TDE_tdsVertex *p_stB,
                        TDE_tdsVertex *p_stC,
                        TDE_tdsVertex *p_stR )
{
  TDE_tdsVertex stAB, stAC, stAP;
  TDE_tdxValue denominateur;
  
  // calcul des vecteurs de base du triangle
  TDE_vSubVertexToVertex( p_stB, p_stA, &stAB) ;
  TDE_vSubVertexToVertex( p_stC, p_stA, &stAC) ;
  TDE_vSubVertexToVertex( p_stP, p_stA, &stAP) ;

  // coordonnee de p_stP dans le repere A,AB,AC
  denominateur = TDE_M_Sub(TDE_M_Mul(stAB.xX,stAC.xY),TDE_M_Mul(stAB.xY,stAC.xX));

  p_stR->xX = TDE_M_Div(TDE_M_Sub(TDE_M_Mul(stAP.xX,stAC.xY),TDE_M_Mul(stAP.xY,stAC.xX)),
                        denominateur);
  p_stR->xY = TDE_M_Div(TDE_M_Sub(TDE_M_Mul(stAB.xX,stAP.xY),TDE_M_Mul(stAB.xY,stAP.xX)),
                        denominateur);
}


BOOL GLI_bPickTriangle( TDE_tdsVertex *p_stSouris,
                        TDE_tdsVertex *p_stA,
                        TDE_tdsVertex *p_stB,
                        TDE_tdsVertex *p_stC )
{
  TDE_tdsVertex stRet;

  fn_vChangeRepere(p_stSouris, p_stA, p_stB, p_stC, &stRet);

  /* on teste si le point est dans le triangle A,B,C */
  if ( ( stRet.xX >= TDE_M_FloatToValue( 0.0 ))
    && ( stRet.xY >= TDE_M_FloatToValue( 0.0 ))
    && ( TDE_M_Add( stRet.xX, stRet.xY ) <= TDE_M_FloatToValue( 1.0 )))
    return TRUE;
  else
    return FALSE;
}

BOOL GLI_bPickPoly4( TDE_tdsVertex *p_stSouris,
                     TDE_tdsVertex *p_stA,
                     TDE_tdsVertex *p_stB,
                     TDE_tdsVertex *p_stC,
                     TDE_tdsVertex *p_stD )
{
  BOOL ret1,ret2;

  ret1 = GLI_bPickTriangle(p_stSouris,p_stA,p_stB,p_stC)
      || GLI_bPickTriangle(p_stSouris,p_stA,p_stC,p_stD);
  
  ret2 = GLI_bPickTriangle(p_stSouris,p_stD,p_stA,p_stB)
      || GLI_bPickTriangle(p_stSouris,p_stD,p_stB,p_stC);

  return ret1 && ret2;
}


BOOL GLI_bPickPoint( TDE_tdsVertex *p_stSouris,
                     TDE_tdsVertex *p_stA )
{
  return M_DistancePointPoint(p_stSouris, p_stA) <= C_PREC_SOURIS;
}


BOOL GLI_bPickSegment( TDE_tdsVertex *p_stSouris,
                       TDE_tdsVertex *p_stP1,
                       TDE_tdsVertex *p_stP2 )
{
  TDE_tdsVertex stA,stB,stC,stD; // vecteurs du quadrilatere
  TDE_tdsVertex stDir,stNor; // vecteurs directeur et normal

  // calcul des vecteurs directeur et normal
  TDE_vSubVertexToVertex( p_stP2, p_stP1, &stDir) ;
  stNor.xX = TDE_M_Neg(stDir.xY);
  stNor.xY = stDir.xX;

  // on leur donne la longueur C_PREC_SOURIS
  TDE_vNormerVertex(&stDir, &stDir);
  TDE_vNormerVertex(&stNor, &stNor);
  TDE_vMulVertexToValue(&stDir, C_PREC_SOURIS, &stDir);
  TDE_vMulVertexToValue(&stNor, C_PREC_SOURIS, &stNor);

  stA.xX = TDE_M_Sub( TDE_M_Add( p_stP1->xX, stNor.xX ), stDir.xX );
  stA.xY = TDE_M_Sub( TDE_M_Add( p_stP1->xY, stNor.xY ), stDir.xY );;

  stB.xX = TDE_M_Sub( TDE_M_Sub( p_stP1->xX, stNor.xX ), stDir.xX );
  stB.xY = TDE_M_Sub( TDE_M_Sub( p_stP1->xY, stNor.xY ), stDir.xY );;

  stC.xX = TDE_M_Add( TDE_M_Sub( p_stP2->xX, stNor.xX ), stDir.xX );
  stC.xY = TDE_M_Add( TDE_M_Sub( p_stP2->xY, stNor.xY ), stDir.xY );;

  stD.xX = TDE_M_Add( TDE_M_Add( p_stP2->xX, stNor.xX ), stDir.xX );
  stD.xY = TDE_M_Add( TDE_M_Add( p_stP2->xY, stNor.xY ), stDir.xY );

  return GLI_bPickPoly4(p_stSouris, &stA, &stB, &stC, &stD);
}


BOOL GLI_bPickSprite( TDE_tdsVertex *p_stSouris,
                      TDE_tdsSprite *p_stSprite )
{
  TDE_tdsRect  stRect;
  TDE_tdxValue xScaledSpriteW, xScaledSpriteH, xSpriteCenterY;
  TDE_tdxValue xSpriteCornerX, xSpriteCornerY, xSpriteCenterX;

  xScaledSpriteW = TDE_M_Mul(lViewPortW,p_stSprite->stDim.xX);
  xScaledSpriteH = TDE_M_Mul(lViewPortH,p_stSprite->stDim.xY);
  xSpriteCenterX = TDE_M_Mul(lViewPortW,stSprMatrix.stTranslateVertex.xX);
  xSpriteCenterY = TDE_M_Mul(lViewPortH,stSprMatrix.stTranslateVertex.xY);  
 
  xSpriteCornerX = xSpriteCenterX - TDE_M_Div(xScaledSpriteW,2);
  xSpriteCornerY = xSpriteCenterY - TDE_M_Div(xScaledSpriteH,2);

  iTDE_vFillsRect(&stRect, xSpriteCornerX, xSpriteCornerY, xScaledSpriteW, xScaledSpriteH);

  return GLI_bPickPoly4( p_stSouris, 
                         &stRect.tdsSommet[0],
                         &stRect.tdsSommet[1],
                         &stRect.tdsSommet[2],
                         &stRect.tdsSommet[3] );
}

BOOL GLI_bPickSpriteAbs( TDE_tdsVertex *p_stSouris,
                         TDE_tdsSprite *p_stSprite,
                         TDE_tdxPixel *p_Color  )
{
  long          decal;   
  TDE_tdsRect  stRect;
  TDE_tdxValue xScaledSpriteW, xScaledSpriteH, xSpriteCenterY;
  TDE_tdxValue xSpriteCornerX, xSpriteCornerY, xSpriteCenterX;
 
  xScaledSpriteW = p_stSprite->stDim.xX;
  xScaledSpriteH = p_stSprite->stDim.xY;
  xSpriteCenterX = stSprMatrix.stTranslateVertex.xX;
  xSpriteCenterY = stSprMatrix.stTranslateVertex.xY;
  
  xSpriteCornerX = xSpriteCenterX - TDE_M_Div(xScaledSpriteW,2);
  xSpriteCornerY = xSpriteCenterY - TDE_M_Div(xScaledSpriteH,2);

  iTDE_vFillsRect(&stRect, xSpriteCornerX, xSpriteCornerY, xScaledSpriteW, xScaledSpriteH);

  decal = TDE_M_ValueToLong(p_stSprite->stDim.xX) * (TDE_M_ValueToLong(p_stSouris->xY)-TDE_M_ValueToLong(stRect.tdsSommet[0].xY)) + TDE_M_ValueToLong((p_stSouris->xX)-TDE_M_ValueToLong(stRect.tdsSommet[0].xX));
  if( (decal < 0) || (decal >= p_stSprite->stDim.xX*p_stSprite->stDim.xY) )
  {
        *p_Color = 0;
        return 0;
  }
  else
        *p_Color = p_stSprite->v_pData[decal];
 

  return GLI_bPickPoly4( p_stSouris, 
                         &stRect.tdsSommet[0],
                         &stRect.tdsSommet[1],
                         &stRect.tdsSommet[2],
                         &stRect.tdsSommet[3] );
}


BOOL GLI_bPickLine( TDE_tdsVertex *p_stSouris,
                    TDE_tdsLine *p_stLine )
{
  TDE_tdxAngle xAngle;
  TDE_tdsVertex stP1, stP2;

  // calcul des sommets P1 et P2 du segment
  TDE_vGetTranslateVertexMatrix(&stSprMatrix, &stP1);
  TDE_vGetAngleMatrix(&stSprMatrix, &xAngle);
  stP2.xX = TDE_M_Mul( p_stLine->xLength, TDE_M_Cos(xAngle) );
  stP2.xY = TDE_M_Mul( p_stLine->xLength, TDE_M_Sin(xAngle) );
  TDE_vAddVertexToVertex(&stP1, &stP2, &stP2);

  return GLI_bPickSegment( p_stSouris, &stP1, &stP2 );
}


BOOL GLI_bIsObjectPick( TDE_tdsVertex *p_stSouris,
                        TDE_tdeType eType,
                        void *p_stObject,
                        TDE_tdxPixel *p_Color )
{
  switch(eType)
  {
    case TDE_eOT_SPRITE :
      //return GLI_bPickSprite( p_stSouris, (TDE_tdsSprite *)p_stObject );
      return GLI_bPickSpriteAbs( p_stSouris, (TDE_tdsSprite *)p_stObject, p_Color );
      break;

    case TDE_eOT_LINE :
      return GLI_bPickLine( p_stSouris, (TDE_tdsLine *)p_stObject );
      break;

    default :
      return FALSE;
  }
}


/*On trouve dans a_tdxSprObjPick[] les indices des fils successifs 
  pour arriver a l'objet sous la souris
  le premier fils porte le numero 1 */
BOOL TDE_bGetSuperObjectPick( GLD_tdpstViewportAttributes p_stViewAttrib,
                              TDE_tdsVertex *p_stSouris,
                              TDE_tdstPickInfo *p_stPickInfo )
{
  TDE_tdsSuperObject *p_stSprObjInter, *p_stSprObjPick;
  BOOL ret = FALSE;
  TDE_tdxPixel xColor;
  int i;

  p_stPickInfo->iNbSprPick = 0;
  lViewPortW = p_stViewAttrib->dwWidth;
  lViewPortH = p_stViewAttrib->dwHeight;
  p_stSprObjPick = NULL;

  for(i=0 ; i<TDE_kMAXPRIORITY ; i++)
  {
    p_stSprObjInter = TDE_g_apZList[i];

    while (p_stSprObjInter != NULL) 
    {
      stSprMatrix = p_stSprObjInter->stModifiedMatrix;
    
      ret = GLI_bIsObjectPick(p_stSouris, p_stSprObjInter->eType, p_stSprObjInter->p_vPointer, &xColor);
      if(ret)
      {
            int j;
        
            j=p_stPickInfo->iNbSprPick-1;
            while(j >= 0)
            {
                   if( j < (TDE_kMAXPICK-1) )
                        p_stPickInfo->aDEF_stInfo[j+1] = p_stPickInfo->aDEF_stInfo[j];
                   j--;
            }

            if ( j < (TDE_kMAXPICK-1) )
            {
                  p_stPickInfo->aDEF_stInfo[j+1].p_stSprObj = p_stSprObjInter;
                  p_stPickInfo->aDEF_stInfo[j+1].xColor = xColor;
                  if ( p_stPickInfo->iNbSprPick < TDE_kMAXPICK )
                        p_stPickInfo->iNbSprPick++;
            }
      }

      p_stSprObjInter = p_stSprObjInter->p_stNextZList;
    }
  }

  if(p_stPickInfo->iNbSprPick != 0)
  {
    ret = TRUE;
  }

  return ret;
}
