//
//  pdext_gfx.h
//  Playdate Simulator
//
//  Created by Dave Hayden on 10/6/17.
//  Copyright © 2017 Panic, Inc. All rights reserved.
//

#ifndef pdext_sprite_h
#define pdext_sprite_h

#if TARGET_EXTENSION

typedef enum {kCollisionTypeSlide, kCollisionTypeFreeze, kCollisionTypeOverlap, kCollisionTypeBounce} SpriteCollisionResponseType;

typedef struct
{
	float x;
	float y;
	float width;
	float height;

} PDRect;

static inline PDRect PDRectMake(float x, float y, float width, float height)
{
	PDRect r = { .x = x, .y = y, .width = width, .height = height };
	return r;
}

#endif

typedef struct {
	float x;
	float y;
} CollisionPoint;

typedef struct {
	int x;
	int y;
} CollisionVector;

typedef struct SpriteCollisionInfo SpriteCollisionInfo;
struct SpriteCollisionInfo
{
	LCDSprite *sprite;		// The sprite being moved
	LCDSprite *other;		// The sprite colliding with the sprite being moved
	SpriteCollisionResponseType responseType;	// The result of collisionResponse
	uint8_t overlaps;		// True if the sprite was overlapping other when the collision started. False if it didn’t overlap but tunneled through other.
	float ti;				// A number between 0 and 1 indicating how far along the movement to the goal the collision occurred
	CollisionPoint move;	// The difference between the original coordinates and the actual ones when the collision happened
	CollisionVector normal;	// The collision normal; usually -1, 0, or 1 in x and y. Use this value to determine things like if your character is touching the ground.
	CollisionPoint touch;	// The coordinates where the sprite started touching other
	PDRect spriteRect;		// The rectangle the sprite occupied when the touch happened
	PDRect otherRect; 		// The rectangle the sprite being collided with occupied when the touch happened
};

typedef struct SpriteQueryInfo SpriteQueryInfo;
struct SpriteQueryInfo
{
	LCDSprite *sprite;			// The sprite being intersected by the segment
								// ti1 and ti2 are numbers between 0 and 1 which indicate how far from the starting point of the line segment the collision happened
	float ti1;					// entry point
	float ti2;					// exit point
	CollisionPoint entryPoint;	// The coordinates of the first intersection between sprite and the line segment
	CollisionPoint exitPoint;	// The coordinates of the second intersection between sprite and the line segment
};

typedef struct LCDSprite LCDSprite;
typedef struct CWCollisionInfo CWCollisionInfo;
typedef struct CWItemInfo CWItemInfo;
typedef void LCDSpriteDrawFunction(LCDSprite* sprite, PDRect bounds, PDRect drawrect);
typedef void LCDSpriteUpdateFunction(LCDSprite* sprite);
typedef SpriteCollisionResponseType LCDSpriteCollisionFilterProc(LCDSprite* sprite, LCDSprite* other);

struct playdate_sprite
{
	void (*setAlwaysRedraw)(int flag);
	void (*addDirtyRect)(LCDRect dirtyRect);
	void (*drawSprites)(void);
	void (*updateAndDrawSprites)(void);

	LCDSprite* (*newSprite)(void);
	void (*freeSprite)(LCDSprite *sprite);
	LCDSprite* (*copy)(LCDSprite *sprite);

	void (*addSprite)(LCDSprite *sprite);
	void (*removeSprite)(LCDSprite *sprite);
	void (*removeSprites)(LCDSprite **sprites, int count);
	void (*removeAllSprites)(void);
	int (*getSpriteCount)(void);

	void (*setBounds)(LCDSprite *sprite, PDRect bounds);
	PDRect (*getBounds)(LCDSprite *sprite);
	void (*moveTo)(LCDSprite *sprite, float x, float y);
	void (*moveBy)(LCDSprite *sprite, float dx, float dy);

	void (*setImage)(LCDSprite *sprite, LCDBitmap *image, LCDBitmapFlip flip);
	LCDBitmap* (*getImage)(LCDSprite *sprite);
	void (*setSize)(LCDSprite *s, float width, float height);
	void (*setZIndex)(LCDSprite *sprite, int16_t zIndex);
	int16_t (*getZIndex)(LCDSprite *sprite);

	void (*setDrawMode)(LCDSprite *sprite, LCDBitmapDrawMode mode);
	void (*setImageFlip)(LCDSprite *sprite, LCDBitmapFlip flip);
	LCDBitmapFlip (*getImageFlip)(LCDSprite *sprite);
	void (*setStencil)(LCDSprite *sprite, LCDBitmap* stencil); // deprecated in favor of setStencilImage()

	void (*setClipRect)(LCDSprite *sprite, LCDRect clipRect);
	void (*clearClipRect)(LCDSprite *sprite);
	void (*setClipRectsInRange)(LCDRect clipRect, int startZ, int endZ);
	void (*clearClipRectsInRange)(int startZ, int endZ);

	void (*setUpdatesEnabled)(LCDSprite *sprite, int flag);
	int (*updatesEnabled)(LCDSprite *sprite);
	void (*setCollisionsEnabled)(LCDSprite *sprite, int flag);
	int (*collisionsEnabled)(LCDSprite *sprite);
	void (*setVisible)(LCDSprite *sprite, int flag);
	int (*isVisible)(LCDSprite *sprite);
	void (*setOpaque)(LCDSprite *sprite, int flag);
	void (*markDirty)(LCDSprite *sprite);

	void (*setTag)(LCDSprite *sprite, uint8_t tag);
	uint8_t (*getTag)(LCDSprite *sprite);

	void (*setIgnoresDrawOffset)(LCDSprite *sprite, int flag);

	void (*setUpdateFunction)(LCDSprite *sprite, LCDSpriteUpdateFunction *func);
	void (*setDrawFunction)(LCDSprite *sprite, LCDSpriteDrawFunction *func);

	void (*getPosition)(LCDSprite *sprite, float *x, float *y);

	// Collisions
	void (*resetCollisionWorld)(void);

	void (*setCollideRect)(LCDSprite *sprite, PDRect collideRect);
	PDRect (*getCollideRect)(LCDSprite *sprite);
	void (*clearCollideRect)(LCDSprite *sprite);

	// caller is responsible for freeing the returned array for all collision methods
	void (*setCollisionResponseFunction)(LCDSprite *sprite, LCDSpriteCollisionFilterProc *func);
	SpriteCollisionInfo* (*checkCollisions)(LCDSprite *sprite, float goalX, float goalY, float *actualX, float *actualY, int *len);			// access results using SpriteCollisionInfo *info = &results[i];
	SpriteCollisionInfo* (*moveWithCollisions)(LCDSprite *sprite, float goalX, float goalY, float *actualX, float *actualY, int *len);
	LCDSprite** (*querySpritesAtPoint)(float x, float y, int *len);
	LCDSprite** (*querySpritesInRect)(float x, float y, float width, float height, int *len);
	LCDSprite** (*querySpritesAlongLine)(float x1, float y1, float x2, float y2, int *len);
	SpriteQueryInfo* (*querySpriteInfoAlongLine)(float x1, float y1, float x2, float y2, int *len);											// access results using SpriteQueryInfo *info = &results[i];
	LCDSprite** (*overlappingSprites)(LCDSprite *sprite, int *len);
	LCDSprite** (*allOverlappingSprites)(int *len);

	// added in 1.7
	void (*setStencilPattern)(LCDSprite* sprite, uint8_t pattern[8]);
	void (*clearStencil)(LCDSprite* sprite);

	void (*setUserdata)(LCDSprite* sprite, void* userdata);
	void* (*getUserdata)(LCDSprite* sprite);

	// added in 1.10
	void (*setStencilImage)(LCDSprite *sprite, LCDBitmap* stencil, int tile);
	
	// 2.1
	void (*setCenter)(LCDSprite* s, float x, float y);
	void (*getCenter)(LCDSprite* s, float* x, float* y);
	
	// 2.7
	void (*setTilemap)(LCDSprite* s, LCDTileMap* tilemap);
	LCDTileMap* (*getTilemap)(LCDSprite* s);
};

#endif /* pdext_sprite_h */
