using UnityEngine;
using System;
using System.Collections;

abstract class Note
{
    // define common note properties

    public double Time;
    public HandType HandType;

    protected Level level;
    protected NoteObject noteObject;

    // get only, make this into property?
    public bool Active;

    // interpret note option
    protected abstract void FromBmsNum(String num);
    protected abstract NoteObject CreateNoteObjectImpl();

    // instantiate associated game object

    public void CreateNoteObject()
    {
        noteObject = CreateNoteObjectImpl();
    }

    public abstract void Judge(double time);

    public void Update(float remainingTime)
    {
        if (remainingTime < -1 || remainingTime > 5)
        {
            noteObject.gameObject.SetActive(false);
            Active = false;
        }
        else
        {
            Active = true;
            noteObject.gameObject.SetActive(true);
        }
        noteObject.SetPosition(remainingTime);
    }

    // factory method
    // may return null if given note is not supported
    public static Note Create(BmsNote bn, double bpm, Level parent)
    {
        Note note = null;
        HandType handType = HandType.None;

        switch (bn.lane)
        {
            case "11": // 1p first lane: FORWARD LEFT HAND
                note = new ForwardNote();
                handType = HandType.Left;
                break;
            case "12": // 1p second lane: FORWARD RIGHT HAND
                note = new ForwardNote();
                handType = HandType.Right;
                break;
            case "13": // 1p third lane: REAR LEFT HAND
                note = new EdgeNote();
                handType = HandType.Left;

                break;
            case "14": // 1p forth lane: REAR RIGHT HAND
                note = new EdgeNote();
                handType = HandType.Right;

                break;
            //case "15": // 1p fifth lane: EDGE LEFT HAND
            //    handType = HandType.Left;

            //    break;
            //case "16": // 1p sixth lane: EDGE RIGHT HAND
            //    handType = HandType.Right;

            //    break;

            // add action notes 

            default:
                return null;
        }

        note.FromBmsNum(bn.num);
        note.HandType = handType;
        note.Time = bn.barTime * (60.0 / bpm * 4);

        note.level = parent;

        note.CreateNoteObject();

        return note;
    }
}

class ForwardNote : Note
{
    private float x, y;

    protected override NoteObject CreateNoteObjectImpl()
    {
        var obj = MonoBehaviour.Instantiate<ForwardNoteObject>(PlayEngine.inst.ForwardNoteObject);
        obj.Init(x, y, HandType);
        return obj;
    }

    protected override void FromBmsNum(string num)
    {
        Debug.Log(num);
        // since 00 is used for empty notes, 1 ~ Z is valid range
        x = charToCoord(num[0]) / 35.0f;
        y = charToCoord(num[1]) / 35.0f;
    }

    private int charToCoord(char a)
    {
        if (char.IsDigit(a))
        {
            return a - '1';
        }
        else if (char.IsLetter(a))
        {
            return char.ToLower(a) - 'a' + 9;
        }

        throw new ArgumentException("wrong data");
    }

    public override void Judge(double time)
    {
        if (Math.Abs(time - this.Time) < 0.5)
        {
            var obj = MonoBehaviour.Instantiate(PlayEngine.inst.explosion);
            obj.transform.position = noteObject.transform.position;
        }
    }
}

class RearNote : Note
{
    protected override NoteObject CreateNoteObjectImpl()
    {
        throw new NotImplementedException();
    }

    protected override void FromBmsNum(string num)
    {
        throw new NotImplementedException();
    }

    public override void Judge(double time)
    {
        
    }
}

class EdgeNote : Note
{
    private int index;

    protected override NoteObject CreateNoteObjectImpl()
    {
        var obj = MonoBehaviour.Instantiate<EdgeNoteObject>(PlayEngine.inst.EdgeNoteObjects[index]);
        obj.Init(HandType);
        return obj;
    }

    protected override void FromBmsNum(string num)
    {
        index = charToIndex(num);
    }

    private int charToIndex(string hex)
    {
        if (hex[0] != 'E')
        {
            throw new ArgumentException("wrong data");
        }
        if (char.IsDigit(hex[1]))
        {
            return hex[1] - '0';
        }

        throw new ArgumentException("wrong data");
    }

    private int GetPrefabIndex()
    {
        return 1;
    }

    public override void Judge(double time)
    {

    }
}

public enum NoteType
{
    Front,
    Rear,
    Edge,
    Action
}

public enum HandType
{
    Left,
    Right,
    None
}