/*
*     ___                       ___
*    / _ \___ _    _____ ____  / _ \_______ __    __
*   / ___/ _ \ |/|/ / -_) __/ / // / __/ _ `/ |/|/ /
*  /_/   \___/__,__/\__/_/   /____/_/  \_,_/|__,__/
*
*
*   Original Code by John Garrison
*   Written June 18-23, 1999
*   To be released under the LGPL
*
*   Drawing of sprites.
*
*   See LICENSE.TXT for more LGPL info
*/
#include <stdlib.h>
#include <math.h>
#include <string.h>

#include "SDL/SDL.h"
#include "powerdraw.h"
#include "sprites.h"


///////////////////////////////////////
//          SPRITE ROUTINES          //
///////////////////////////////////////

PD_Sprite *PD_SpriteInit (int NumFrames, int Width, int Height)
{
   PD_Sprite *sprite;

   sprite = (PD_Sprite *) malloc (sizeof(PD_Sprite));

   sprite->x = 0;
   sprite->y = 0;
   sprite->frame = (PD_Surface **) malloc(PD_MAXFRAMES * sizeof(PD_Surface));
   sprite->width = Width;
   sprite->height = Height;
   sprite->curr_frame = 0;
   sprite->num_frames = NumFrames;
   sprite->clear_color = PD_CreateColor(Screen, 0x00, 0x00, 0x00);
   sprite->alpha = 0;
   sprite->visible = ISTRUE;

   return (sprite);
}



int PD_GetSpriteFrame (PD_Sprite *Sprite, const char *File, int FrameNumber)
{
   /* Make sure we are within our limits */
   if (FrameNumber > PD_MAXFRAMES) {
      fprintf(stderr, "FrameNumber is larger than MaxFrames\n");
   exit(1);
   }

   /* Add to num_frames if needed */
   if (FrameNumber > (Sprite->num_frames+1)) Sprite->num_frames = FrameNumber;

   /* Load the image */
   Sprite->frame[FrameNumber] = PD_LoadImage(File);

   return 0;
}



int PD_DrawSprite (PD_Sprite *Sprite, PD_Surface *Surface, int StartFrame, int FrameAdvance)
{
   if (Sprite->visible == ISFALSE)
      return 0;

   /* Make sure we are within our limits */
   if (StartFrame > PD_MAXFRAMES)
      StartFrame = Sprite->curr_frame;

   StartFrame += FrameAdvance;
   if (StartFrame >= Sprite->num_frames)
      StartFrame -= Sprite->num_frames;
   Sprite->curr_frame = StartFrame;

   PD_BlitTransparent (Sprite->frame[StartFrame], Surface, 0, 0, Sprite->x, Sprite->y, Sprite->width, Sprite->height, Sprite->clear_color, Sprite->alpha);

   return 0;
}



int PD_DestroySprite (PD_Sprite *Sprite)
{
   free(Sprite->frame);
   free(Sprite);

   return 0;
}



/////////////////////////////////////////////////
//           SPRITE FLIPPING ROUTINES          //
/////////////////////////////////////////////////

int PD_DrawSpriteVFlipped (PD_Sprite *Sprite, PD_Surface *Surface, int StartFrame, int FrameAdvance)
{
   PD_Surface *flipped;

   if (Sprite->visible == ISFALSE)
      return 0;

   /* Make sure we are within our limits */
   if (StartFrame > PD_MAXFRAMES)
      StartFrame = Sprite->curr_frame;

   StartFrame += FrameAdvance;
   if (StartFrame >= Sprite->num_frames)
      StartFrame -= Sprite->num_frames;
   Sprite->curr_frame = StartFrame;

   flipped = PD_SurfaceVFlipped(Sprite->frame[StartFrame]);
   PD_BlitTransparent (flipped, Surface, 0, 0, Sprite->x, Sprite->y, Sprite->width, Sprite->height, Sprite->clear_color, Sprite->alpha);

   PD_DestroySurface(flipped);

   return 0;
}



int PD_DrawSpriteHFlipped (PD_Sprite *Sprite, PD_Surface *Surface, int StartFrame, int FrameAdvance)
{
   PD_Surface *flipped;

   if (Sprite->visible == ISFALSE)
      return 0;

   /* Make sure we are within our limits */
   if (StartFrame > PD_MAXFRAMES)
      StartFrame = Sprite->curr_frame;

   StartFrame += FrameAdvance;
   if (StartFrame >= Sprite->num_frames)
      StartFrame -= Sprite->num_frames;
   Sprite->curr_frame = StartFrame;

   flipped = PD_SurfaceHFlipped(Sprite->frame[StartFrame]);
   PD_BlitTransparent (flipped, Surface, 0, 0, Sprite->x, Sprite->y, Sprite->width, Sprite->height, Sprite->clear_color, Sprite->alpha);

   PD_DestroySurface(flipped);

   return 0;
}



int PD_DrawSpriteVHFlipped (PD_Sprite *Sprite, PD_Surface *Surface, int StartFrame, int FrameAdvance)
{
   PD_Surface *flipped;

   if (Sprite->visible == ISFALSE)
      return 0;

   /* Make sure we are within our limits */
   if (StartFrame > PD_MAXFRAMES)
      StartFrame = Sprite->curr_frame;

   StartFrame += FrameAdvance;
   if (StartFrame >= Sprite->num_frames)
      StartFrame -= Sprite->num_frames;
   Sprite->curr_frame = StartFrame;

   flipped = PD_SurfaceVHFlipped(Sprite->frame[StartFrame]);
   PD_BlitTransparent (flipped, Surface, 0, 0, Sprite->x, Sprite->y, Sprite->width, Sprite->height, Sprite->clear_color, Sprite->alpha);

   PD_DestroySurface(flipped);

   return 0;
}



///////////////////////////////////////////////
//          SPRITE EFFECTS ROUTINES          //
///////////////////////////////////////////////

int PD_DrawSpriteRotated (PD_Sprite *Sprite, PD_Surface *Surface, int StartFrame, int FrameAdvance, int Angle)
{
   PD_Surface *surface;
   int middlex, middley;
   int originx, originy;

   if (Sprite->visible == ISFALSE)
      return 0;

   /* Make sure we are within our limits */
   if (StartFrame > PD_MAXFRAMES)
      StartFrame = Sprite->curr_frame;

   StartFrame += FrameAdvance;
   if (StartFrame >= Sprite->num_frames)
      StartFrame -= Sprite->num_frames;
   Sprite->curr_frame = StartFrame;

   surface = PD_RotateSurface(Sprite->frame[StartFrame], Angle);

   originx = Sprite->width>>1;
   originy = Sprite->height>>1;

   middlex = Sprite->x + originx;
   middley = Sprite->y + originy;

   PD_BlitTransparent (surface, Surface, 0, 0, middlex - (surface->w>>1), middley - (surface->h>>1), surface->w, surface->h, Sprite->clear_color, Sprite->alpha);

   PD_DestroySurface(surface);

   return 0;
}



int PD_DrawSpriteRotatedAround (PD_Sprite *Sprite, PD_Surface *Surface, int StartFrame, int FrameAdvance, int X, int Y, int Angle)
{
   int x, y;

   if (Sprite->visible == ISFALSE)
      return 0;

   /* Make sure we are within our limits */
   if (StartFrame > PD_MAXFRAMES)
      StartFrame = Sprite->curr_frame;

   StartFrame += FrameAdvance;
   if (StartFrame >= Sprite->num_frames)
      StartFrame -= Sprite->num_frames;
   Sprite->curr_frame = StartFrame;

   x = Sprite->x - X;
   y = Sprite->y - Y;

   Sprite->x = x*cos((float)Angle*0.017453292) - y*sin((float)Angle*0.017453292);
   Sprite->y = x*sin((float)Angle*0.017453292) + y*cos((float)Angle*0.017453292);

   Sprite->x += X;
   Sprite->y += Y;

   PD_BlitTransparent (Sprite->frame[StartFrame], Surface, 0, 0, Sprite->x, Sprite->y, Sprite->width, Sprite->height, Sprite->clear_color, Sprite->alpha);

   return 0;
}



int PD_DrawSpriteXSheared (PD_Sprite *Sprite, PD_Surface *Surface, int StartFrame, int FrameAdvance, int XShear)
{
   PD_Surface *surface;

   if (Sprite->visible == ISFALSE)
      return 0;

   /* Make sure we are within our limits */
   if (StartFrame > PD_MAXFRAMES)
      StartFrame = Sprite->curr_frame;

   StartFrame += FrameAdvance;
   if (StartFrame >= Sprite->num_frames)
      StartFrame -= Sprite->num_frames;
   Sprite->curr_frame = StartFrame;

   surface = PD_XShearSurface (Sprite->frame[StartFrame], XShear);

   PD_BlitTransparent (surface, Surface, 0, 0, Sprite->x, Sprite->y, surface->w, surface->h, Sprite->clear_color, Sprite->alpha);
   PD_DestroySurface(surface);

   return 0;
}



int PD_DrawSpriteYSheared (PD_Sprite *Sprite, PD_Surface *Surface, int StartFrame, int FrameAdvance, int YShear)
{
   PD_Surface *surface;

   if (Sprite->visible == ISFALSE)
      return 0;

   /* Make sure we are within our limits */
   if (StartFrame > PD_MAXFRAMES)
      StartFrame = Sprite->curr_frame;

   StartFrame += FrameAdvance;
   if (StartFrame >= Sprite->num_frames)
      StartFrame -= Sprite->num_frames;
   Sprite->curr_frame = StartFrame;

   surface = PD_YShearSurface (Sprite->frame[StartFrame], YShear);

   PD_BlitTransparent (surface, Surface, 0, 0, Sprite->x, Sprite->y, surface->w, surface->h, Sprite->clear_color, Sprite->alpha);
   PD_DestroySurface(surface);

   return 0;
}

int PD_DrawSpriteScaled (PD_Sprite *Sprite, PD_Surface *Surface, int StartFrame, int FrameAdvance, long Width, long Height)
{
   PD_Surface *surface;

   if (Sprite->visible == ISFALSE)
      return 0;

   /* Make sure we are within our limits */
   if (StartFrame > PD_MAXFRAMES)
      StartFrame = Sprite->curr_frame;

   StartFrame += FrameAdvance;
   if (StartFrame >= Sprite->num_frames)
      StartFrame -= Sprite->num_frames;
   Sprite->curr_frame = StartFrame;

   surface = PD_ScaleSurface (Sprite->frame[StartFrame], Width, Height);

   PD_BlitTransparent (surface, Surface, 0, 0, Sprite->x, Sprite->y, surface->w, surface->h, Sprite->clear_color, Sprite->alpha);
   PD_DestroySurface(surface);

   return 0;
}

int PD_DrawSpriteScaledBy (PD_Sprite *Sprite, PD_Surface *Surface, int StartFrame, int FrameAdvance, float XFactor, float YFactor)
{
   PD_Surface *surface;

   if (Sprite->visible == ISFALSE)
      return 0;

   /* Make sure we are within our limits */
   if (StartFrame > PD_MAXFRAMES)
      StartFrame = Sprite->curr_frame;

   StartFrame += FrameAdvance;
   if (StartFrame >= Sprite->num_frames)
      StartFrame -= Sprite->num_frames;
   Sprite->curr_frame = StartFrame;

   surface = PD_ScaleSurfaceBy (Sprite->frame[StartFrame], XFactor, YFactor);

   PD_BlitTransparent (surface, Surface, 0, 0, Sprite->x, Sprite->y, surface->w, surface->h, Sprite->clear_color, Sprite->alpha);
   PD_DestroySurface(surface);

   return 0;
}
