#pragma once

#include "olcPixelGameEngine.h"
#include "olcPGEX_TransformedView.h"
#include "Constant.h"
#include "Image.h"
#include "Sound.h"
#include "olcPGEX_AudioSource.h"
#include "DebuffIcon.h"
#include "CollectionPoint.h"
#include "Resources.h"
#include "Textbox.h"
#include "Memory.h"
#include "olcPGEX_QuickGUI.h"

enum class UnitType{
	LeftShifter,
	RightShifter,
	BitRestorer,
	MemorySwapper,
	Corrupter,
	MemoryAllocator,
	RAMBank,
	MemoryGuard,
	Refresher,
	Turret,
	_Platform,
};

struct Marker{
	size_t index;
	size_t size;
};

struct Unit{
public:
	Unit(PixelGameEngine*pge,std::vector<Memory>memory,vf2d pos,float radius,Renderable&img,Pixel targetLineColor,Pixel attackingLineColor,bool friendly=false,bool moveable=true,bool friendlyInteractable=false,bool enemyInteractable=true);
	virtual~Unit();
	int GetHealth();
	int GetRange();
	int GetAtkSpd();
	int GetMoveSpd();
	int GetProcedure();
	int GetMemorySize();
	std::vector<bool>memory;
	std::vector<bool>ghostMemory;
	std::vector<bool>savedMemory;
	virtual void Update(PixelGameEngine*pge,std::vector<std::unique_ptr<Audio>>&SOUNDS,std::vector<std::shared_ptr<Unit>>&queuedUnits);
	virtual void Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits)=0;
	virtual void Draw(TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES);
	virtual void DrawHud(TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES);
	void _DrawHud(TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool unitMetersGreyedOut);
	virtual void OnDeath(std::vector<std::unique_ptr<Audio>>&SOUNDS);
	bool IsFriendly();
	bool IsSelected();
	void Select();
	void Deselect();
	vf2d GetPos();
	bool IsDead();
	vf2d GetUnitSize();
	void SetTargetUnit(std::weak_ptr<Unit>target);
	void SetTargetLocation(vf2d targetLoc);
	void SetPos(vf2d newPos);
	void AttemptAttack(std::weak_ptr<Unit>attacker,std::weak_ptr<Unit>unit,std::vector<std::shared_ptr<Unit>>&otherUnits,std::vector<DebuffIcon>&debuffIcons,std::vector<std::unique_ptr<Renderable>>&IMAGES);
	bool InFogOfWar();
	bool GhostInFogOfWar();
	void HideGhost();
	vf2d GetGhostPos();
	void _Update(PixelGameEngine*pge,std::vector<std::unique_ptr<Audio>>&SOUNDS,Resources&player_resources,Resources&enemy_resources,std::vector<std::shared_ptr<Unit>>&queuedUnits,std::array<float,5>&resourceGainTimer,std::vector<ResourceGainIcon>&resourceGainIcons,std::vector<std::unique_ptr<Renderable>>&IMAGES);
	bool IsMoveable();
	void DrawRangeIndicator(PixelGameEngine*pge,TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES);
	bool CanInteractWithEnemies();
	bool CanInteractWithAllies();
	Renderable&GetImage();
	virtual void RunAI(PixelGameEngine*pge);
	void _RunAI(PixelGameEngine*pge);
	virtual void Attacked(std::weak_ptr<Unit>attacker);
	void _Attacked(std::weak_ptr<Unit>attacker);
	std::weak_ptr<Unit>GetCurrentTarget();
	void DrawUnitDamageStats(PixelGameEngine*pge,TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES);
	bool AutoAcquiresFriendlyTargets();
	bool CanMove();
	void SetTargetCollectionPoint(std::weak_ptr<CollectionPoint>targetCP,std::weak_ptr<Unit>self_ptr);
	Pixel GetUnitColor();
	virtual void UpdateGUIState(TileTransformedView&game,Resources&player_resources,Textbox&displayBox,bool&hovered,int totalUsedMemory,int availableMemory);
	virtual bool ClickHandled(TileTransformedView&game,Resources&player_resources,std::vector<std::shared_ptr<Unit>>&units,std::vector<std::unique_ptr<Renderable>>&IMAGES); //If you return true here, then the left click does not pass back to the main Virus Attack class.
	bool IsAllocator();
	void SetBuildUnit(float buildTime,std::shared_ptr<Unit>finalUnit);
	bool IsBuilding();
	void SetGuardTime(float time);
	bool IsGuarded();
	void SaveMemory();
	bool IsPlatform();
	bool IsAttached();
	bool IsRAMBank();
	Unit*GetBuildUnit();
	Marker health={};
	Marker range={};
	Marker atkSpd={};
	Marker moveSpd={};
	Marker procedure={};

	std::vector<bool>& operator <<=(const int n){
		for(int i=0;i<GetMemorySize()-1;i++){
			memory[i]=memory[i+1];
		}
		memory[GetMemorySize()-1]=0;
		return memory;
	}

	std::vector<bool>& operator >>=(const int n){
		for(int i=GetMemorySize()-1;i>0;i--){
			memory[i]=memory[i-1];
		}
		memory[0]=0;
		return memory;
	}
protected:
	bool friendly;
	Renderable&img;
	std::weak_ptr<Unit>target;
	std::weak_ptr<Unit>appliedTarget;
	vf2d targetLoc=CONSTANT::UNSELECTED;
	bool InRange(std::shared_ptr<Unit>target);
	bool InRange(Unit*target);
	bool InRange(vf2d pos);
	float reloadTimer=0;
	bool attackFailed=false;
	bool autoAcquireFriendlyTarget=true;
	bool isAllocator=false;
	bool isPlatform=false;
	std::shared_ptr<Unit>buildTransformUnit;
	float buildTime=0;
	bool isRAMBank=false;
private:
	Renderable targetingLine;
	Renderable attackingLine;
	Pixel targetLineCol,attackingLineCol;
	vf2d pos;
	vf2d ghostPos;
	float radius;
	int GetBits(Marker&m);
	bool selected=false;
	bool dead=false;
	void _Attack(std::weak_ptr<Unit>attacker,std::weak_ptr<Unit>finalTarget,std::vector<std::shared_ptr<Unit>>&otherUnits);
	bool moveable=true;
	bool friendlyInteractable=false;
	bool enemyInteractable=true;
	float lineShift=0;
	void ApplyMatrixEffect(Renderable&r);
	std::weak_ptr<CollectionPoint>attachedPoint;
	std::weak_ptr<CollectionPoint>attachTarget;
	bool willAttachWhenReachingDestination=false;
	std::weak_ptr<Unit>self_ptr;
	float collectionTime=0;
	UnitType type;
	float guardTime=0;
};

struct LeftShifter:Unit{
	LeftShifter(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly=false,bool moveable=true);
	void Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits)override;
	static std::vector<Memory> resourceCost;
	static std::string unitName;
	static std::string unitDescription;
};

struct RightShifter:Unit{
	RightShifter(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly=false,bool moveable=true);
	void Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits)override;
	static std::vector<Memory> resourceCost;
	static std::string unitName;
	static std::string unitDescription;
};

struct BitRestorer:Unit{
	BitRestorer(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly=false,bool moveable=true);
	void Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits)override;
	void AttemptToHealOtherAllies(std::vector<std::shared_ptr<Unit>>&otherUnits);
	static std::vector<Memory> resourceCost;
	static std::string unitName;
	static std::string unitDescription;
};

struct MemorySwapper:Unit{
	MemorySwapper(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly=false,bool moveable=true);
	void Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits)override;
	static std::vector<Memory> resourceCost;
	static std::string unitName;
	static std::string unitDescription;
};

struct Corrupter:Unit{
	Corrupter(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly=false,bool moveable=true);
	void Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits)override;
	static std::vector<Memory> resourceCost;
	static std::string unitName;
	static std::string unitDescription;
};

struct MemoryAllocator:Unit{
	MemoryAllocator(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly=false,bool moveable=true);
	void Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&units)override;
	void Update(PixelGameEngine*pge,std::vector<std::unique_ptr<Audio>>&SOUNDS,std::vector<std::shared_ptr<Unit>>&queuedUnits)override;
	void Draw(TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES)override;
	static std::vector<Memory> resourceCost;
	static std::string unitName;
	static std::string unitDescription;
};

struct RAMBank:Unit{
	vf2d randomOffset;
	Renderable img;
	Renderable&originalImg;
	Renderable&matrixImg;
	bool soundStarted=false;
	int soundHandle=-1;
	QuickGUI::Manager allocatorManager;
	QuickGUI::ImageButton*allocatorButton;
	RAMBank(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly=false);
	void Update(PixelGameEngine*pge,std::vector<std::unique_ptr<Audio>>&SOUNDS,std::vector<std::shared_ptr<Unit>>&queuedUnits)override;
	void Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits)override;
	void Draw(TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES)override;
	void OnDeath(std::vector<std::unique_ptr<Audio>>&SOUNDS)override;
	bool ClickHandled(TileTransformedView&game,Resources&player_resources,std::vector<std::shared_ptr<Unit>>&units,std::vector<std::unique_ptr<Renderable>>&IMAGES)override;
	void UpdateGUIState(TileTransformedView&game,Resources&player_resources,Textbox&displayBox,bool&hovered,int totalUsedMemory,int availableMemory)override;
	void DrawHud(TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES)override;
	static std::vector<Memory> resourceCost;
	static std::string unitName;
	static std::string unitDescription;
};

struct _Platform:Unit{
	_Platform(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly=false,bool moveable=true);
	void Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits)override;
	void Update(PixelGameEngine*pge,std::vector<std::unique_ptr<Audio>>&SOUNDS,std::vector<std::shared_ptr<Unit>>&queuedUnits)override;
	void Draw(TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES)override;
	static std::vector<Memory> resourceCost;
	static std::string unitName;
	static std::string unitDescription;
};

struct MemoryGuard:Unit{
	MemoryGuard(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly=false,bool moveable=true);
	void Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits)override;
	static std::vector<Memory> resourceCost;
	static std::string unitName;
	static std::string unitDescription;
};

struct Refresher:Unit{
	Refresher(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly=false,bool moveable=true);
	void Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits)override;
	static std::vector<Memory> resourceCost;
	static std::string unitName;
	static std::string unitDescription;
};

struct Turret:Unit{
	Turret(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly=false,bool moveable=true);
	void Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits)override;
	static std::vector<Memory> resourceCost;
	static std::string unitName;
	static std::string unitDescription;
};