// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "GameFramework/Actor.h"
#include <map>
#include "Protein.hpp"
#include "ResidueConnections.hpp"
#include "ProteinActor.generated.h"

UCLASS()
class PDB_VIEWER_API AProteinActor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AProteinActor();

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	
	// Called every frame
	virtual void Tick( float DeltaSeconds ) override;

	enum
	{
		MATERIAL_OPAQUE = 0,
		MATERIAL_TRANSLUSCENT,
		MATERIAL_COUNT
	};

	/*
	 * rootBox - every component is related to this one. It also shows the target bounding box of model preview.
	 */
	UPROPERTY()
	UBoxComponent* rootBox;

	/*
	 * Protein parent - it's the effective 0,0,0 position of the whole protein model. Every component representing a part
	 * of the protein will be attached to this one. When "NormalizeMesh()" is called, this will be moved and scaled in relation
	 * to the root component.
	 */
	UPROPERTY()
	USphereComponent* proteinParent;

	/*
	 * Materials to be used for atom models. They will be instantiated in order to change the color for each element present
	 * in the preview.
	 */
	UPROPERTY()
	UMaterialInterface* material[MATERIAL_COUNT];

	/**
	 * Mesh to be used for a UStaticMeshComponent representing an Atom.
	 */
	UPROPERTY()
	UStaticMesh* sphereMesh = nullptr;

	/**
	 * Mesh to be used for a UStaticMeshComponent representing a connection between atoms
	 */
	UPROPERTY()
	UStaticMesh* stickMesh = nullptr;

	/**
	 * Copies the protein given as argument and prepares to render one of its models
	 */
	void Init(Protein _protein);

	/**
	 * Loads a protein from hardcoded location and prepares its model
	 */
	void CustomInit();
	/**
	 * Shows or hides hydrogen atoms
	 */
	void ToggleHydrogen();

	/**
	 * Loads a protein file from a file under the given path, then initializes the model
	 */
	UFUNCTION(BlueprintCallable, Category = "Protein")
	void LoadFromFile(FString path);

	/**
	 * Returns a list of model serial numbers contained in the loaded protein.
	 */
	UFUNCTION(BlueprintCallable, Category = "Protein")
	TArray<FString> GetModelSerials();

	/** 
	 * Changes the visualization to selected model, or fails silently if modelIndex is out of range.
	 */
	UFUNCTION(BlueprintCallable, Category = "Protein")
	void ShowModel(int modelIndex);

	/**
	 * Normalizes the visual components of the model in order to fit them in the bounding box.
	 * Returns the transformation used for normalization of the visuals.
	 */
	UFUNCTION(BlueprintCallable, Category = "Position")
	FTransform NormalizeMesh();


private:
	static const std::string RESIDUE_FILLER_DATA_PATH;
	bool showHydrogen = false;
	Protein protein;
	std::vector<UStaticMeshComponent*> atomVisualVector;
	std::vector<UStaticMeshComponent*> stickVisualVector;
	std::map<std::string, UMaterialInstanceDynamic*> cachedMaterials[MATERIAL_COUNT];
	int selectedModel;
	void UpdateVisibility();
	void ClearComponents();
	void GenerateComponentsForModel(Model& model);
};
