using GadgetCore.API;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using Random = System.Random;

namespace SpacePlanet
{
  public static class GameObjectSpaceWorldGeneratorExtension
  {
    public static void AddToWorld(this GameObject obj, GameObject g)
    {
      obj.transform.parent = g.transform;
    }
    public static void AddObjectToWorld(this GameObject obj, GameObject g, List<GameObject> objList)
    {
      obj.transform.parent = g.transform;
      objList.Add(obj);
    }
  }

  public class SpaceWorldGenerator
  {

    private static int W = 32; //128 + 64;
    private static int H = 32;//128 - 32;

    private static GameObject GetBaseBlock(float w, float h, float posX, float posY, Texture2D texture)
    {
      GameObject gameObject = (GameObject)UnityEngine.Object.Instantiate(Resources.Load("z/midChunk0"), new Vector3((float)(242 + posX - 60), posY - 60, 5f), Quaternion.Euler(0f, 180f, 180f));
      gameObject.transform.localScale = new Vector3(w, h, 1);
      BoxCollider collider = gameObject.GetComponentInChildren<BoxCollider>();
      collider.size = new Vector3(2, 2, 5);
      Renderer renderer = gameObject.GetComponentInChildren<Renderer>();
      renderer.material = new Material(Shader.Find("Transparent/Diffuse"))
      {
        mainTexture = texture
      };
      return gameObject;
    }

    private static GameObject GetBorderBlock(float w, float h, float posX, float posY, float moveX, float moveY)
    {
      GameObject gameObject = (GameObject)UnityEngine.Object.Instantiate(Resources.Load("z/midChunk0"), new Vector3((float)(242 + posX), posY, 5f), Quaternion.Euler(0f, 180f, 180f));
      gameObject.transform.localScale = new Vector3(w, h, 1);
      BoxCollider collider = gameObject.GetComponentInChildren<BoxCollider>();
      collider.isTrigger = true;
      collider.size = new Vector3(2, 2, 1);
      Renderer renderer = gameObject.GetComponentInChildren<Renderer>();
      renderer.material = new Material(Shader.Find("Transparent/Diffuse"))
      {
        mainTexture = null
      };
      return gameObject;
    }

    private static GameObject GetStarTextureObject(Texture2D texture)
    {
      GameObject gameObject = (GameObject)UnityEngine.Object.Instantiate(Resources.Load("z/midChunk0"), new Vector3(0f, 0f, 20f), Quaternion.Euler(0f, 180f, 180f));
      gameObject.layer = 0;
      gameObject.transform.localScale = new Vector3(32, 32, 1);
      BoxCollider collider = gameObject.GetComponentInChildren<BoxCollider>();
      collider.isTrigger = true;
      collider.size = new Vector3(2, 2, 1);
      Renderer renderer = gameObject.GetComponentInChildren<Renderer>();
      renderer.material = new Material(Shader.Find("Transparent/Diffuse"))
      {
        mainTexture = texture
      };
      gameObject.AddComponent<StarTextureScript>();
      return gameObject;
    }

    private static float GetSizeForPixels(int px)
    {
      return 1f / 16f * px;
    }

    private static void MarkGeneration(int x, int y, int[][] arr)
    {
      if (x >= 0 && x < W && y >= 0 && y < H)
        arr[x][y] = 1;
    }
    private static bool CheckGeneration(int x, int y, int[][] arr)
    {
      if (x >= 0 && x < W && y >= 0 && y < H)
        if (arr[x][y] == 0)
          return true;
        else return false;
      return true;
    }

    private static IEnumerator SpawnEnemy(string s, Vector3 pos)
    {
      yield return new WaitForSeconds(1);
      Network.Instantiate(Resources.Load(s), pos, Quaternion.identity, 0);
      yield break;
    }

    public static void StartGenarateWorld(SpawnerScript s, int[] param)
    {
      s.StartCoroutine(GenarateWorld(s, param));
    }
    public static IEnumerator GenarateWorld(SpawnerScript s, int[] param)
    {
      yield return new WaitForSeconds(0.1f);

      long seed = 0;
      string seedString = "";

      for (int i = 1; i < param.Length; i++)
      {
        seed += param[i];
        seed *= 10;
      }
      for (int i = 1; i < param.Length; i++)
        seedString += " " + param[i];

      Core.logger.Log("Generating world with seed:" + seed + " - " + seed.GetHashCode() + " - " + seedString);

      Random random = new Random(seed.GetHashCode());
      Random serverRandom = new Random();

      //s.backLights.SetActive(true);
      s.mainLight.SetActive(true);
      s.mainLight.GetComponentInChildren<Light>().color = new Color(1f, 1f, 1f, 1f);

      Texture2D textureParalex0 = GadgetCoreAPI.LoadTexture2D("bSpacebg0.png");
      Texture2D textureMeteor1 = GadgetCoreAPI.LoadTexture2D("meteor1.png");
      Texture2D textureMeteor3 = GadgetCoreAPI.LoadTexture2D("meteor3.png");
      Texture2D textureMeteor4 = GadgetCoreAPI.LoadTexture2D("meteor4.png");
      Texture2D textureStars = GadgetCoreAPI.LoadTexture2D("stars.png");

      var fieldChunks = typeof(SpawnerScript).GetField("chunks", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
      var chunks = fieldChunks.GetValue(s) as GameObject[];
      var baseObject = new GameObject();
      var chunk = baseObject.AddComponent<Chunk>();
      chunks[0] = baseObject;

      var objects = new List<GameObject>();

      GetStarTextureObject(textureStars).AddToWorld(baseObject);

      int[][] genSpots = new int[W][];
      for (int i = 0; i < W; i++)
        genSpots[i] = new int[H];

      {
        int x = 15;
        int y = 10;
        for (int xOff = -2; xOff < 3; xOff++)
          for (int yOff = -2; yOff < 6; yOff++)
            MarkGeneration(x + xOff, y + yOff, genSpots);
        GetBaseBlock(GetSizeForPixels(54), GetSizeForPixels(54), x * 4, y * 4, textureMeteor1).AddToWorld(baseObject);
      }
      {
        int r = 0;
        int overflow = 0;
        int x = random.Next(W / 2) + W / 2;
        int y = random.Next(H / 2) + H / 2;

        bool addLoopPortal = false;
        foreach (var gadget in GadgetCore.API.Gadgets.ListAllEnabledGadgets())
          if (gadget.Info?.ModName == "Loop Portal")
          {
            addLoopPortal = true;
            break;
          }

        while (overflow < 1000 && r < 3)
        {
          overflow++;
          bool noGen = false;
          for (int xOff = -1; xOff < 2; xOff++)
            for (int yOff = -1; yOff < 4; yOff++)
              if (!CheckGeneration(x + xOff, y + yOff, genSpots))
                noGen = true;
          if (!noGen)
          {
            for (int xOff = -1; xOff < 2; xOff++)
              for (int yOff = -1; yOff < 4; yOff++)
                MarkGeneration(x + xOff, y + yOff, genSpots);
            GetBaseBlock(GetSizeForPixels(54), GetSizeForPixels(54), x * 4, y * 4, textureMeteor1).AddToWorld(baseObject);
            if (Network.isServer)
            {
              GameScript.endPortal[r] = (GameObject)Network.Instantiate((GameObject)Resources.Load("portal"), new Vector3(242 + x * 4 - 60, y * 4 - 60 + GetSizeForPixels(54) + 1.85f, 0f), Quaternion.identity, 0);
              GameScript.endPortalUA[r] = GameScript.endPortal[r].transform.GetChild(0).gameObject;
              GameScript.endPortal[r].GetComponent<NetworkView>().RPC("Activate", RPCMode.All, new object[0]);
              GameScript.endPortalUA[r].GetComponent<NetworkView>().RPC("Set", RPCMode.AllBuffered, new object[] { 0, 0, r });
              GameScript.endPortal[r].AddToWorld(baseObject);
            }
            r++;
          }
          x = Mathf.Min(x + random.Next(5) - 2, W);
          y = Mathf.Min(y + random.Next(5) - 2, H);
        }
        while (addLoopPortal && overflow < 5000)
        {
          overflow++;
          bool noGen = false;
          for (int xOff = -1; xOff < 2; xOff++)
            for (int yOff = -1; yOff < 4; yOff++)
              if (!CheckGeneration(x + xOff, y + yOff, genSpots))
                noGen = true;
          if (!noGen)
          {
            for (int xOff = -1; xOff < 2; xOff++)
              for (int yOff = -1; yOff < 4; yOff++)
                MarkGeneration(x + xOff, y + yOff, genSpots);
            GetBaseBlock(GetSizeForPixels(54), GetSizeForPixels(54), x * 4, y * 4, textureMeteor1).AddToWorld(baseObject);
            if (Network.isServer)
            {
              var i = (GameObject)Network.Instantiate((GameObject)Resources.Load("portal"), new Vector3(242 + x * 4 - 60, y * 4 - 60 + GetSizeForPixels(54) + 1.85f, 0f), Quaternion.identity, 0);
              var iUA = i.transform.GetChild(0).gameObject;
              i.GetComponent<NetworkView>().RPC("Activate", RPCMode.All, new object[0]);
              iUA.GetComponent<NetworkView>().RPC("Set", RPCMode.AllBuffered, new object[] { SpawnerScript.curBiome, 0, 4 });
              i.transform.parent = GameScript.endPortal[0].transform;
            }
            break;
          }
          x = Mathf.Min(x + random.Next(5) - 2, W);
          y = Mathf.Min(y + random.Next(5) - 2, H);
        }
      }
      {
        int r = 0;
        int overflow = 0;

        while (overflow < 15000 && r < 700)
        {
          overflow++;
          int i = random.Next(6);
          int x = random.Next(W);
          int y = random.Next(H);

          bool noGen = false;
          switch (i)
          {
            case 0:
            case 1:
            case 2:
              for (int xOff = -1; xOff < 2; xOff++)
                for (int yOff = -1; yOff < 2; yOff++)
                  if (!CheckGeneration(x + xOff, y + yOff, genSpots))
                    noGen = true;
              break;
            case 3:
            case 4:
              for (int xOff = -1; xOff < 2; xOff++)
                for (int yOff = -1; yOff < 2; yOff++)
                  if (!CheckGeneration(x + xOff, y + yOff, genSpots))
                    noGen = true;
              break;
            case 5:
              for (int xOff = -2; xOff < 3; xOff++)
                for (int yOff = -2; yOff < 3; yOff++)
                  if (!CheckGeneration(x + xOff, y + yOff, genSpots))
                    noGen = true;
              break;
          }
          if (!noGen)
          {
            r++;
            switch (i)
            {
              case 0:
              case 1:
              case 2:
                for (int xOff = -1; xOff < 2; xOff++)
                  for (int yOff = -1; yOff < 2; yOff++)
                    MarkGeneration(x + xOff, y + yOff, genSpots);
                GetBaseBlock(GetSizeForPixels(54), GetSizeForPixels(54), x * 4, y * 4, textureMeteor1).AddToWorld(baseObject);
                if (Network.isServer)
                {
                  var position = new Vector3((float)(242 + x * 4 - 60), y * 4 - 60 + GetSizeForPixels(54) + 0.425f, 0f);
                  //s.StartCoroutine(SpawnEnemy("e/wasp", position + new Vector3(0, 2, 0)));
                  //var emeny = (GameObject)Network.Instantiate((GameObject)Resources.Load("e/axelarkBall"), position, Quaternion.identity, 0);
                  if(serverRandom.Next(2) == 0)
                  {
                    var obj = (GameObject)Network.Instantiate(Core.objectSpaceOre.Object, position, Quaternion.identity, 0);
                    obj.AddObjectToWorld(baseObject, objects);
                  }
                  else
                  {
                    var obj = (GameObject)Network.Instantiate(Core.objectSpaceOreBig.Object, position, Quaternion.identity, 0);
                    obj.AddObjectToWorld(baseObject, objects);
                  }
                  //(GameObject)Resources.Load("obj/bugspot0")
                  //bugspot.GetComponentInChildren<NetworkView>().RPC("Activate", RPCMode.All, new object[0]);
                  //bugspot.GetComponentInChildren<EnemyScript>().enabled = true;
                  //enemy.SetActive(true);
                  //enemy.layer = 9;
                  // Network.Instantiate(Resources.Load("e/pirate"), position + new Vector3(0, 2, 0), Quaternion.identity, 0);
                  //s.StartCoroutine(SpawnEnemy("e/pirate", position + new Vector3(0, 2, 0)));
                }
                break;
              case 3:
              case 4:
                for (int xOff = -1; xOff < 2; xOff++)
                  for (int yOff = -1; yOff < 2; yOff++)
                    MarkGeneration(x + xOff, y + yOff, genSpots);
                GetBaseBlock(GetSizeForPixels(38), GetSizeForPixels(38), x * 4, y * 4, textureMeteor3).AddToWorld(baseObject);
                break;
              case 5:
                for (int xOff = -2; xOff < 3; xOff++)
                  for (int yOff = -2; yOff < 3; yOff++)
                    MarkGeneration(x + xOff, y + yOff, genSpots);
                GetBaseBlock(GetSizeForPixels(128), GetSizeForPixels(128), x * 4, y * 4, textureMeteor4).AddToWorld(baseObject);
                break;
            }
          }
        }
      }

      var objArray = objects.ToArray();
      chunk.objectiveSpawn = objArray;

      yield break;
    }

    public static void GenarateTown(SpawnerScript s, int[] param)
    {
      long seed = 0;
      string seedString = "";

      for (int i = 1; i < param.Length; i++)
      {
        seed += param[i];
        seed *= 10;
      }
      for (int i = 1; i < param.Length; i++)
        seedString += " " + param[i];

      Core.logger.Log("Generating town with seed:" + seed + " - " + seedString);

      Random random = new Random((int)seed);

      //s.backLights.SetActive(true);
      s.mainLight.SetActive(true);
      s.mainLight.GetComponentInChildren<Light>().color = new Color(1f, 1f, 1f, 1f);

      Texture2D textureParalex0 = GadgetCoreAPI.LoadTexture2D("bSpacebg0.png");
      Texture2D textureMeteor1 = GadgetCoreAPI.LoadTexture2D("meteor1.png");
      Texture2D textureMeteor3 = GadgetCoreAPI.LoadTexture2D("meteor3.png");
      Texture2D textureMeteor4 = GadgetCoreAPI.LoadTexture2D("meteor4.png");
      Texture2D textureStars = GadgetCoreAPI.LoadTexture2D("stars.png");

      var fieldChunks = typeof(SpawnerScript).GetField("chunks", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
      var chunks = fieldChunks.GetValue(s) as GameObject[];
      var baseObject = new GameObject();
      chunks[0] = baseObject;

      GetStarTextureObject(textureStars).AddToWorld(baseObject);

      int[][] genSpots = new int[W][];
      for (int i = 0; i < W; i++)
        genSpots[i] = new int[H];

      if (false)
      {
        int x = 15;
        int y = 10;
        for (int xOff = -2; xOff < 3; xOff++)
          for (int yOff = -2; yOff < 6; yOff++)
            MarkGeneration(x + xOff, y + yOff, genSpots);
        GetBaseBlock(GetSizeForPixels(54), GetSizeForPixels(54), x * 4, y * 4, textureMeteor1).AddToWorld(baseObject);
      }
      {
        int x = 22;
        int y = 10;
        GetBaseBlock(GetSizeForPixels(54), GetSizeForPixels(54), x * 4, y * 4, textureMeteor1).AddToWorld(baseObject);
        if (Network.isServer)
        {
          GameScript.endPortal[1] = (GameObject)Network.Instantiate((GameObject)Resources.Load("portal"), new Vector3(242 + x * 4 - 60, y * 4 - 60 + GetSizeForPixels(54) + 1.85f, 0f), Quaternion.identity, 0);
          GameScript.endPortalUA[1] = GameScript.endPortal[1].transform.GetChild(0).gameObject;
          GameScript.endPortal[1].GetComponent<NetworkView>().RPC("Activate", RPCMode.All, new object[0]);
          GameScript.endPortalUA[1].GetComponent<NetworkView>().RPC("Set", RPCMode.AllBuffered, new object[] { Core.spacePlanetId, 0, 1 });
        }
      }
      {
        int x = 15;
        int y = 10;
        GetBaseBlock(GetSizeForPixels(128), GetSizeForPixels(128), x * 4, y * 4, textureMeteor4).AddToWorld(baseObject);
        if (Network.isServer)
        {
          GameScript.endPortal[3] = (GameObject)Network.Instantiate((GameObject)Resources.Load("portal"), new Vector3(242 + x * 4 - 60, y * 4 - 60 + GetSizeForPixels(128) + 1.85f, 0f), Quaternion.identity, 0);
          GameScript.endPortalUA[3] = GameScript.endPortal[3].transform.GetChild(0).gameObject;
          GameScript.endPortal[3].GetComponent<NetworkView>().RPC("Activate", RPCMode.All, new object[0]);
          GameScript.endPortalUA[3].GetComponent<NetworkView>().RPC("Set", RPCMode.AllBuffered, new object[] { 98, 0, 3 });
        }
      }
      if (false)
      {
        int r = 0;
        int overflow = 0;

        while (overflow < 15000 && r < 700)
        {
          overflow++;
          int i = random.Next(6);
          int x = random.Next(W);
          int y = random.Next(H);

          bool noGen = false;
          switch (i)
          {
            case 0:
            case 1:
            case 2:
              for (int xOff = -1; xOff < 2; xOff++)
                for (int yOff = -1; yOff < 2; yOff++)
                  if (!CheckGeneration(x + xOff, y + yOff, genSpots))
                    noGen = true;
              break;
            case 3:
            case 4:
              for (int xOff = -1; xOff < 2; xOff++)
                for (int yOff = -1; yOff < 2; yOff++)
                  if (!CheckGeneration(x + xOff, y + yOff, genSpots))
                    noGen = true;
              break;
            case 5:
              for (int xOff = -2; xOff < 3; xOff++)
                for (int yOff = -2; yOff < 3; yOff++)
                  if (!CheckGeneration(x + xOff, y + yOff, genSpots))
                    noGen = true;
              break;
          }
          if (!noGen)
          {
            r++;
            switch (i)
            {
              case 0:
              case 1:
              case 2:
                for (int xOff = -1; xOff < 2; xOff++)
                  for (int yOff = -1; yOff < 2; yOff++)
                    MarkGeneration(x + xOff, y + yOff, genSpots);
                GetBaseBlock(GetSizeForPixels(54), GetSizeForPixels(54), x * 4, y * 4, textureMeteor1).AddToWorld(baseObject);
                break;
              case 3:
              case 4:
                for (int xOff = -1; xOff < 2; xOff++)
                  for (int yOff = -1; yOff < 2; yOff++)
                    MarkGeneration(x + xOff, y + yOff, genSpots);
                GetBaseBlock(GetSizeForPixels(38), GetSizeForPixels(38), x * 4, y * 4, textureMeteor3).AddToWorld(baseObject);
                break;
              case 5:
                for (int xOff = -2; xOff < 3; xOff++)
                  for (int yOff = -2; yOff < 3; yOff++)
                    MarkGeneration(x + xOff, y + yOff, genSpots);
                GetBaseBlock(GetSizeForPixels(128), GetSizeForPixels(128), x * 4, y * 4, textureMeteor4).AddToWorld(baseObject);
                break;
            }
          }
        }
      }
    }

  }
}
