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

[Serializable]
public struct AnimationInfo
{
    public Texture2D Sheet;
    public int Frames;
    public Vector2 Offset;
    public string Name;
}

[SelectionBase]
public class AnimationHandler : MonoBehaviour {
    public float PixelScale = 0.1f;

    public AnimationInfo[] Animations;
    public GameObject Target;

    private Material mat;

    private Mesh mesh;

    private AnimationInfo animationInfo;

    private Vector3 spriteSize = new Vector2();

	// Use this for initialization
	void Start () {
        MeshFilter mf = Target.GetComponentInChildren<MeshFilter>();
        MeshRenderer rend = Target.GetComponentInChildren<MeshRenderer>();
        mat = rend.material;

        animationInfo = Animations[0];
        UpdateMat();

        mesh = new Mesh();
        mesh.SetVertices(verts);

        mesh.SetUVs(0, uvs);

        mesh.SetTriangles(new int[]
        {
            0, 1, 2,
            0, 2, 3
        }, 0);

        UpdateMesh();

        mf.sharedMesh = mesh;
	}

    private void UpdateMat()
    {
        spriteSize = new Vector3(animationInfo.Sheet.width * 0.25f, animationInfo.Sheet.height / animationInfo.Frames, 1);
        mat.SetTexture("_MainTex", animationInfo.Sheet);
    }

    private List<Vector3> verts = new List<Vector3>
        {
            new Vector3(1, 1, 0),
            new Vector3(1, -1, 0),
            new Vector3(-1, -1, 0),
            new Vector3(-1, 1, 0)
        };

    private List<Vector2> uvs = new List<Vector2>
        {
            new Vector2(1, 1),
            new Vector2(1, 0),
            new Vector2(0, 0),
            new Vector2(0, 1)
        };

    private void UpdateMesh()
    {
        Vector3 offset = new Vector3(animationInfo.Offset.x, spriteSize.y - animationInfo.Offset.y, 0);
        Vector3 spriteSizeMult = new Vector3(spriteSize.x, spriteSize.y);
        verts[0] = (Vector3.Scale(new Vector3(1, 1, 0), spriteSize) - offset) * PixelScale;
        verts[1] = (Vector3.Scale(new Vector3(1, 0, 0), spriteSize) - offset) * PixelScale;
        verts[2] = (Vector3.Scale(new Vector3(0, 0, 0), spriteSize) - offset) * PixelScale;
        verts[3] = (Vector3.Scale(new Vector3(0, 1, 0), spriteSize) - offset) * PixelScale;
        mesh.SetVertices(verts);

        float uStart = ((int)Dir * spriteSize.x) / animationInfo.Sheet.width;
        float uEnd = uStart + spriteSize.x / animationInfo.Sheet.width;

        float vStart = (Frame * spriteSize.y) / animationInfo.Sheet.height;
        float vEnd = vStart + spriteSize.y / animationInfo.Sheet.height;

        

        uvs[0] = new Vector3(uEnd, vEnd);
        uvs[1] = new Vector3(uEnd, vStart);
        uvs[2] = new Vector3(uStart, vStart);
        uvs[3] = new Vector3(uStart, vEnd);
        mesh.SetUVs(0, uvs);

        mesh.RecalculateNormals();
        mesh.RecalculateBounds();
    }

    public enum Direction
    {
        DownRight = 0,
        UpRight,
        UpLeft,
        DownLeft
    }

    public void SetState(string animationName, Direction dir, int frame)
    {
        bool changed = false;
        if (animationName != animationInfo.Name)
        {
            changed = true;
            animationInfo = Animations.First(a => a.Name == animationName);
            UpdateMat();
        }

        if (this.Dir != dir)
        {
            changed = true;
            this.Dir = dir;
        }

        if (this.Frame != frame)
        {
            changed = true;
            this.Frame = frame;
        }

        if (changed)
        {
            UpdateMesh();
        }
    }

    public void SetDir(Direction dir)
    {
        if (this.Dir != dir)
        {
            this.Dir = dir;
            UpdateMesh();
        }
    }

    public void SetFrame(int frame)
    {
        if (this.Frame != frame)
        {
            this.Frame = frame;
            UpdateMesh();
        }
    }

    public Direction Dir = Direction.DownRight;
    public int Frame = 0;
	
	// Update is called once per frame
	void Update () {
		
	}
}
