using System;
using System.Collections.Generic;
using UnityEngine;

namespace InfiniteWorld
{
    public static class RuntimeWorldProfileFactory
    {
        public static WorldAutotileProfile CreateFallbackProfile()
        {
            WorldAutotileProfile profile = ScriptableObject.CreateInstance<WorldAutotileProfile>();
            profile.name = "RuntimeFallbackWorldProfile";

            Color grass = new Color(0.35f, 0.62f, 0.26f, 1f);
            Color wallFill = new Color(0.48f, 0.37f, 0.22f, 1f);
            Color wallBorder = new Color(0.29f, 0.21f, 0.12f, 1f);

            profile.baseGroundTile = ProceduralWorldArt.CreateSolidTile("base_grass", grass, 0.06f);
            profile.wallTiles.center = ProceduralWorldArt.CreateFeatureTile("wall_center", wallFill, wallBorder, false, false, false, false, InnerCornerMask.None, true);
            profile.wallTiles.top = ProceduralWorldArt.CreateFeatureTile("wall_top", wallFill, wallBorder, true, false, false, false, InnerCornerMask.None, true);
            profile.wallTiles.right = ProceduralWorldArt.CreateFeatureTile("wall_right", wallFill, wallBorder, false, true, false, false, InnerCornerMask.None, true);
            profile.wallTiles.bottom = ProceduralWorldArt.CreateFeatureTile("wall_bottom", wallFill, wallBorder, false, false, true, false, InnerCornerMask.None, true);
            profile.wallTiles.left = ProceduralWorldArt.CreateFeatureTile("wall_left", wallFill, wallBorder, false, false, false, true, InnerCornerMask.None, true);
            profile.wallTiles.outerTopLeft = ProceduralWorldArt.CreateFeatureTile("wall_outer_tl", wallFill, wallBorder, true, false, false, true, InnerCornerMask.None, true);
            profile.wallTiles.outerTopRight = ProceduralWorldArt.CreateFeatureTile("wall_outer_tr", wallFill, wallBorder, true, true, false, false, InnerCornerMask.None, true);
            profile.wallTiles.outerBottomRight = ProceduralWorldArt.CreateFeatureTile("wall_outer_br", wallFill, wallBorder, false, true, true, false, InnerCornerMask.None, true);
            profile.wallTiles.outerBottomLeft = ProceduralWorldArt.CreateFeatureTile("wall_outer_bl", wallFill, wallBorder, false, false, true, true, InnerCornerMask.None, true);
            profile.wallTiles.innerTopLeft = ProceduralWorldArt.CreateFeatureTile("wall_inner_tl", wallFill, wallBorder, false, false, false, false, InnerCornerMask.TopLeft, true);
            profile.wallTiles.innerTopRight = ProceduralWorldArt.CreateFeatureTile("wall_inner_tr", wallFill, wallBorder, false, false, false, false, InnerCornerMask.TopRight, true);
            profile.wallTiles.innerBottomRight = ProceduralWorldArt.CreateFeatureTile("wall_inner_br", wallFill, wallBorder, false, false, false, false, InnerCornerMask.BottomRight, true);
            profile.wallTiles.innerBottomLeft = ProceduralWorldArt.CreateFeatureTile("wall_inner_bl", wallFill, wallBorder, false, false, false, false, InnerCornerMask.BottomLeft, true);

            profile.environmentTiles.Add(new EnvironmentTileEntry
            {
                id = "Bush",
                tile = ProceduralWorldArt.CreateDecorationTile("env_bush", new Color(0.15f, 0.45f, 0.18f, 1f), new Color(0.23f, 0.58f, 0.25f, 1f), DecorationPattern.Bush),
                weight = 1.4f
            });
            profile.environmentTiles.Add(new EnvironmentTileEntry
            {
                id = "FlowerBlue",
                tile = ProceduralWorldArt.CreateDecorationTile("env_flower_blue", new Color(0.44f, 0.62f, 0.96f, 1f), new Color(0.98f, 0.91f, 0.52f, 1f), DecorationPattern.Flower),
                weight = 0.75f
            });
            profile.environmentTiles.Add(new EnvironmentTileEntry
            {
                id = "Rock",
                tile = ProceduralWorldArt.CreateDecorationTile("env_rock", new Color(0.48f, 0.48f, 0.52f, 1f), new Color(0.69f, 0.69f, 0.74f, 1f), DecorationPattern.Rocks),
                weight = 0.9f
            });

            return profile;
        }

        public static List<ChunkTemplate> CreateFallbackTemplates(int size)
        {
            List<ChunkTemplate> templates = new List<ChunkTemplate>
            {
                CreateCross(size),
                CreateStraightHorizontal(size),
                CreateStraightVertical(size),
                CreateCorner(size, true, true, false, false),
                CreateCorner(size, false, true, true, false),
                CreateCorner(size, false, false, true, true),
                CreateCorner(size, true, false, false, true),
                CreateTJunction(size, true, true, true, false),
                CreateTJunction(size, false, true, true, true),
                CreateTJunction(size, true, false, true, true),
                CreateTJunction(size, true, true, false, true)
            };

            return templates;
        }

        private static ChunkTemplate CreateCross(int size)
        {
            ChunkTemplate template = CreateTemplate("Runtime_Cross", size, size, true, true, true, true);
            DrawRoom(template, 3, 3, size - 6, size - 6);
            DrawPillar(template, 4, 4);
            DrawPillar(template, size - 5, 4);
            DrawPillar(template, 4, size - 5);
            DrawPillar(template, size - 5, size - 5);
            ScatterEnvironment(template, 3);
            return template;
        }

        private static ChunkTemplate CreateStraightHorizontal(int size)
        {
            ChunkTemplate template = CreateTemplate("Runtime_Straight_H", size, size, false, true, false, true);
            DrawRoom(template, 2, 4, size - 4, size - 8);
            DrawSideAlcoves(template, horizontal: true);
            ScatterEnvironment(template, 2);
            return template;
        }

        private static ChunkTemplate CreateStraightVertical(int size)
        {
            ChunkTemplate template = CreateTemplate("Runtime_Straight_V", size, size, true, false, true, false);
            DrawRoom(template, 4, 2, size - 8, size - 4);
            DrawSideAlcoves(template, horizontal: false);
            ScatterEnvironment(template, 2);
            return template;
        }

        private static ChunkTemplate CreateCorner(int size, bool top, bool right, bool bottom, bool left)
        {
            ChunkTemplate template = CreateTemplate($"Runtime_Corner_{top}_{right}_{bottom}_{left}", size, size, top, right, bottom, left);
            DrawRoom(template, 3, 3, size - 6, size - 6);
            if (!top)
            {
                DrawWallLine(template, 5, size - 5, size - 6, size - 5);
            }
            if (!right)
            {
                DrawWallLine(template, size - 5, 5, size - 5, size - 6);
            }
            if (!bottom)
            {
                DrawWallLine(template, 5, 4, size - 6, 4);
            }
            if (!left)
            {
                DrawWallLine(template, 4, 5, 4, size - 6);
            }
            ScatterEnvironment(template, 2);
            return template;
        }

        private static ChunkTemplate CreateTJunction(int size, bool top, bool right, bool bottom, bool left)
        {
            ChunkTemplate template = CreateTemplate($"Runtime_T_{top}_{right}_{bottom}_{left}", size, size, top, right, bottom, left);
            DrawRoom(template, 2, 2, size - 4, size - 4);
            DrawPillar(template, size / 2, size / 2);
            ScatterEnvironment(template, 3);
            return template;
        }

        private static ChunkTemplate CreateTemplate(string name, int width, int height, bool top, bool right, bool bottom, bool left)
        {
            ChunkTemplate template = ScriptableObject.CreateInstance<ChunkTemplate>();
            template.name = name;
            template.Resize(width, height);
            template.exitTop = top;
            template.exitRight = right;
            template.exitBottom = bottom;
            template.exitLeft = left;
            template.ApplyBorderWallsFromExits(3);
            return template;
        }

        private static void DrawRoom(ChunkTemplate template, int xMin, int yMin, int width, int height)
        {
            for (int x = xMin; x < xMin + width; x++)
            {
                template.SetWall(x, yMin, true);
                template.SetWall(x, yMin + height - 1, true);
            }

            for (int y = yMin; y < yMin + height; y++)
            {
                template.SetWall(xMin, y, true);
                template.SetWall(xMin + width - 1, y, true);
            }

            template.CarveExit(ChunkExit.Top, 3);
            template.CarveExit(ChunkExit.Right, 3);
            template.CarveExit(ChunkExit.Bottom, 3);
            template.CarveExit(ChunkExit.Left, 3);
        }

        private static void DrawPillar(ChunkTemplate template, int x, int y)
        {
            for (int py = -1; py <= 1; py++)
            {
                for (int px = -1; px <= 1; px++)
                {
                    template.SetWall(x + px, y + py, true);
                }
            }
        }

        private static void DrawSideAlcoves(ChunkTemplate template, bool horizontal)
        {
            if (horizontal)
            {
                DrawWallLine(template, template.width / 2 - 1, 5, template.width / 2 - 1, 7);
                DrawWallLine(template, template.width / 2 + 1, template.height - 8, template.width / 2 + 1, template.height - 6);
            }
            else
            {
                DrawWallLine(template, 5, template.height / 2 - 1, 7, template.height / 2 - 1);
                DrawWallLine(template, template.width - 8, template.height / 2 + 1, template.width - 6, template.height / 2 + 1);
            }
        }

        private static void DrawWallLine(ChunkTemplate template, int x0, int y0, int x1, int y1)
        {
            int dx = Math.Sign(x1 - x0);
            int dy = Math.Sign(y1 - y0);
            int x = x0;
            int y = y0;

            while (true)
            {
                template.SetWall(x, y, true);
                if (x == x1 && y == y1)
                {
                    break;
                }

                if (x != x1)
                {
                    x += dx;
                }

                if (y != y1)
                {
                    y += dy;
                }
            }
        }

        private static void ScatterEnvironment(ChunkTemplate template, int spacing)
        {
            for (int y = 2; y < template.height - 2; y += spacing + 2)
            {
                for (int x = 2; x < template.width - 2; x += spacing + 3)
                {
                    if (!template.GetWall(x, y))
                    {
                        template.SetEnvironment(x, y, ((x + y) & 1) == 0);
                    }
                }
            }
        }
    }
}
