Park/Paths/PathTilemap.cs
using HC3.Terrain;
using System;

namespace HC3;

/// <summary>
/// A mask representing which directions a path tile connects to.
/// </summary>
[Flags]
public enum PathMask
{
	Up = 1 << 1,
	Right = 1 << 2,
	Down = 1 << 3,
	Left = 1 << 4,

	All = Up | Right | Down | Left
}

/// <summary>
/// A resource defining the tilemap for path pieces. This isn't really exclusive to paths, it can really be for any tile.
/// </summary>
[AssetType( Name = "Tilemap", Extension = "tilemap" )]
public class PathTileMap : GameResource
{
	public class Tileset
	{
		[Property] public GameObject Single { get; set; }
		[Property] public GameObject Straight { get; set; }
		[Property] public GameObject Corner { get; set; }
		[Property] public GameObject Split { get; set; } // 3 way
		[Property] public GameObject Crossing { get; set; } // 4 way
		[Property] public GameObject End { get; set; }
		[Property] public GameObject Stair { get; set; }

		// Todo:
		// Building connector
		// Support pillars

		[Hide]
		Dictionary<PathMask, (GameObject, Vector3)> Tiles = new();

		public void Init()
		{
			Tiles.TryAdd( 0, (Single, Vector3.Forward) );
			Tiles.TryAdd( PathMask.Up | PathMask.Down, (Straight, Vector3.Left) );
			Tiles.TryAdd( PathMask.Left | PathMask.Right, (Straight, Vector3.Forward) );

			// Corners
			Tiles.TryAdd( PathMask.Down | PathMask.Right, (Corner, Vector3.Forward) );
			Tiles.TryAdd( PathMask.Down | PathMask.Left, (Corner, Vector3.Right) );
			Tiles.TryAdd( PathMask.Up | PathMask.Right, (Corner, Vector3.Left) );
			Tiles.TryAdd( PathMask.Up | PathMask.Left, (Corner, Vector3.Backward) );

			// End caps
			Tiles.TryAdd( PathMask.Left, (End, Vector3.Backward) );
			Tiles.TryAdd( PathMask.Right, (End, Vector3.Forward) );
			Tiles.TryAdd( PathMask.Up, (End, Vector3.Left) );
			Tiles.TryAdd( PathMask.Down, (End, Vector3.Right) );

			// 3 splits
			Tiles.TryAdd( PathMask.Up | PathMask.Left | PathMask.Right, (Split, Vector3.Backward) );
			Tiles.TryAdd( PathMask.Down | PathMask.Left | PathMask.Right, (Split, Vector3.Forward) );
			Tiles.TryAdd( PathMask.Up | PathMask.Down | PathMask.Left, (Split, Vector3.Right) );
			Tiles.TryAdd( PathMask.Up | PathMask.Down | PathMask.Right, (Split, Vector3.Left) );

			// 4 split
			Tiles.TryAdd( PathMask.Up | PathMask.Left | PathMask.Right | PathMask.Down, (Crossing, Vector3.Forward) );
		}

		public (GameObject, Vector3) GetTile( PathMask mask, Vector2Int stairDirection )
		{
			if ( stairDirection == 0 || !Stair.IsValid() )
			{
				if ( Tiles.TryGetValue( mask, out var t ) )
					return t;

				Log.Info( $"Couldn't find path for {mask} ({(int)mask})" );
				return (Single, Vector3.Forward);
			}

			return (Stair, (Vector2)stairDirection);
		}
	}

	[Property] public Tileset Default { get; set; }
	[Property] public Tileset Elevated { get; set; }

	protected override void PostLoad()
	{
		Default.Init();
		Elevated.Init();
	}

	public (GameObject, Vector3) GetTile( PathMask mask, Vector2Int stairDirection, bool elevated )
	{
		var tileset = elevated ? Elevated : Default;
		return (tileset ?? Default).GetTile( mask, stairDirection );
	}
}

public static class PathMaskExtensions
{
	public static PathMask Rotate90DegreesClockwise( this PathMask mask )
	{
		return (PathMask)(((int)mask << 1) | ((int)mask >> 3)) & PathMask.All;
	}

	public static bool CanConnectTile( this PathMask mask, TileEdge edge )
	{
		return (mask & edge.ToPathMask()) != 0;
	}

	public static IEnumerable<TileEdge> GetEdges( this PathMask mask ) =>
		GridManager.AllEdges.Where( x => mask.CanConnectTile( x ) );

	public static int GetCount( this PathMask mask )
	{
		var count = 0;

		foreach ( var edge in GridManager.AllEdges )
		{
			if ( mask.CanConnectTile( edge ) )
			{
				++count;
			}
		}

		return count;
	}
}