using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Serialization;

[RequireComponent(typeof(MeshRenderer)), RequireComponent(typeof(MeshFilter))]
public class GrassChunk : MonoBehaviour {

    [SerializeField] private Material materialLOD0;
    public Material MaterialLOD0 {
        get { return materialLOD0; }
        set {
            materialLOD0 = value;
            UpdateLODAssets();
        }
    }

    [SerializeField] private Mesh meshLOD0;
    public Mesh MeshLOD0 {
        get { return meshLOD0; }
        set {
            meshLOD0 = value;
            UpdateLODAssets();
            UpdateLOD(); // a new mesh may change the AABB which may affect the LOD
        }
    }

    [SerializeField] private bool enableShadowsLOD0;
    public bool EnableShadowsLOD0 {
        get { return enableShadowsLOD0; }
        set {
            enableShadowsLOD0 = value;
            UpdateLODAssets();
        }
    }

    [SerializeField] private Material materialLOD1;
    public Material MaterialLOD1 {
        get { return materialLOD1; }
        set {
            materialLOD1 = value;
            UpdateLODAssets();
        }
    }

    [SerializeField] private Mesh meshLOD1;
    public Mesh MeshLOD1 {
        get { return meshLOD1; }
        set {
            meshLOD1 = value;
            UpdateLODAssets();
            UpdateLOD(); // a new mesh may change the AABB which may affect the LOD
        }
    }

    [SerializeField] private bool enableShadowsLOD1;
    public bool EnableShadowsLOD1 {
        get { return enableShadowsLOD1; }
        set {
            enableShadowsLOD1 = value;
            UpdateLODAssets();
        }
    }

    [SerializeField] private Material materialLOD2;
    public Material MaterialLOD2 {
        get { return materialLOD2; }
        set {
            materialLOD2 = value;
            UpdateLODAssets();
        }
    }

    [SerializeField] private Mesh meshLOD2;
    public Mesh MeshLOD2 {
        get { return meshLOD2; }
        set {
            meshLOD2 = value;
            UpdateLODAssets();
            UpdateLOD(); // a new mesh may change the AABB which may affect the LOD
        }
    }

    [SerializeField] private bool enableShadowsLOD2;
    public bool EnableShadowsLOD2 {
        get { return enableShadowsLOD2; }
        set {
            enableShadowsLOD2 = value;
            UpdateLODAssets();
        }
    }

    private int lod = -1; // set to -1 so LOD.set does not exit early when initially called with 0
    public int LOD {
        get { return lod; }
        set {
            if (lod == value) return;

            lod = value;
            UpdateLODAssets();
        }
    }

    private void Update() {
        UpdateLOD();
    }

    public void UpdateLOD() {
        var meshRenderer = GetComponent<MeshRenderer>();
        if (Camera.main) {
            // using meshRenderer.bounds may cause a loop where LOD switches constantly
            var sqrCamDist = meshRenderer.bounds.SqrDistance(Camera.main.transform.position);
            if (sqrCamDist > 20f * 20f) {
                LOD = 2;
            }
            else if (sqrCamDist > 5f * 5f) {
                LOD = 1;
            }
            else {
                LOD = 0;
            }
        }
    }

    private void UpdateLODAssets() {
        var meshRenderer = GetComponent<MeshRenderer>();
        var meshFilter = GetComponent<MeshFilter>();
        switch (lod) {
            case 0:
                meshRenderer.sharedMaterial = materialLOD0;
                meshRenderer.shadowCastingMode = EnableShadowsLOD0 ? ShadowCastingMode.On : ShadowCastingMode.Off;
                meshFilter.sharedMesh = meshLOD0;
                break;
            case 1:
                meshRenderer.sharedMaterial = materialLOD1;
                meshRenderer.shadowCastingMode = EnableShadowsLOD1 ? ShadowCastingMode.On : ShadowCastingMode.Off;
                meshFilter.sharedMesh = meshLOD1;
                break;
            case 2:
                meshRenderer.sharedMaterial = materialLOD2;
                meshRenderer.shadowCastingMode = EnableShadowsLOD2 ? ShadowCastingMode.On : ShadowCastingMode.Off;
                meshFilter.sharedMesh = meshLOD2;
                break;
        }
    }

    private void OnDrawGizmos() {
        Gizmos.color = new Color(1f, 0.7f, 0f, 0.5f);
        var meshRenderer = GetComponent<MeshRenderer>();
        var bounds = meshRenderer.bounds;
        Gizmos.DrawWireCube(bounds.center, bounds.size);
    }
}
