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

public enum OFFSET_DIR {
    forward = 0,
    left = 3,
    right = 1,
    back = 2
}

public class ManualTick : MonoBehaviour
{
    [Task]
    void AlignDirection () {
        // if (Task.isDebugging) 
        if (Task.isDebugging) Debug.Log("ManualTick.Move "+Task.getState);
        if (Task.isStartingOrRunning) {
            transform.rotation = Quaternion.Euler(0,0,direction*90);
            Task.SetSucceeded();
        }
    }

    [Task]
    void Move (int x, int y) {
        // if (Task.isDebugging) 
        if (Task.isDebugging) Debug.Log("ManualTick.Move "+Task.getState);
        if (Task.isStartingOrRunning) {
            transform.position += new Vector3(x,y,0);
            Task.SetSucceeded();
        }
    }

    [Task]
    void CheckFree (int x, int y) {
        if (Task.isStartingOrRunning) {
            Vector3 sourcePos = transform.position;
            Vector3 offset = new Vector3(x,y,0);
            RaycastHit hitInfo;
            if (Physics.SphereCast(sourcePos, 0.4f, offset, out hitInfo, offset.magnitude)) {
                if (Task.isDebugging) Debug.Log("ManualTick.CheckFree: t " + hitInfo + " " + sourcePos + " " + offset);
                Task.SetFailed();
            } else {
                if (Task.isDebugging) Debug.Log("ManualTick.CheckFree: f " + hitInfo + " " + sourcePos + " " + offset);
                Task.SetSucceeded();
            }
        }
    }

    public float updateInterval = 1.0f;
    private float accum = 0.0f; // FPS accumulated over the interval
    private int frames = 0; // Frames drawn over the interval
    private float timeleft = 1.0f; // Left time for current interval
    private float fps = 15.0f; // Current FPS
    private float lastSample = 0;
    private int gotIntervals = 0;

    [Task]
    void FpsCounter () {
        if (Task.isStartingOrRunning) {
            ++frames;
            float newSample = Time.realtimeSinceStartup;
            float deltaTime = newSample - lastSample;
            lastSample = newSample;
    
            timeleft -= deltaTime;
            accum += 1.0f / deltaTime;
    
            // Interval ended - update GUI text and start new interval
            if (timeleft <= 0.0f)
            {
                // display two fractional digits (f2 format)
                fps = accum / frames;
                timeleft = updateInterval;
                accum = 0.0f;
                frames = 0;
                ++gotIntervals;
            }

            Task.log = "FPS: " + fps.ToString("f2");
            
            // Task.data = Time.time;
            // Task.log = $"log {Time.time.ToString()} \n newline";
            Task.SetSucceeded();
        }
    }

    [Task]
    void DebugLog (string s) {
        if (Task.isStartingOrRunning) {
            if (Task.isDebugging) {
                Debug.Log(s);
            }
            Task.data = Time.time;
            Task.log = $"log {Time.time.ToString()} \n newline";
            Task.SetSucceeded();
        }
    }


    public int direction = 2;

    static Dictionary<int, Vector2> directions = new Dictionary<int, Vector2>() {
        {3, new Vector2(1,0)}, // right?
        {2, new Vector2(0,-1)}, // down
        {1, new Vector2(-1,0)}, // left
        {0, new Vector2(0,1)} // up
    };

    [Task]
    void CheckDirectionFree () {
        if (Task.isStartingOrRunning) {
            Vector3 sourcePos = transform.position;
            Vector3 offset = directions[direction];
            RaycastHit hitInfo;
            if (Physics.SphereCast(sourcePos, 0.4f, offset, out hitInfo, offset.magnitude)) {
                if (Task.isDebugging) Debug.Log("ManualTick.CheckFree: t " + direction+ " " + hitInfo + " " + sourcePos + " " + offset);
                Task.SetFailed();
            } else {
                if (Task.isDebugging) Debug.Log("ManualTick.CheckFree: f " + direction+ " " + hitInfo + " " + sourcePos + " " + offset);
                Task.SetSucceeded();
            }
        }
    }


    [Task]
    void CheckDirectionFree  (int forwardOffset, int sideOffset) {
        if (Task.isStartingOrRunning) {
            if (CheckDirection(forwardOffset, sideOffset)) {
                Task.SetFailed();
            }
            else {
                Task.SetSucceeded();
            }
        }
    }

    [Task]
    void CheckDirectionOccupied (int forwardOffset, int sideOffset) {
        if (Task.isStartingOrRunning) {
            if (CheckDirection(forwardOffset, sideOffset)) {
                Task.SetSucceeded();
            }
            else {
                Task.SetFailed();
            }
        }
    }

    bool CheckDirection (int forwardOffset, int sideOffset) {
        Vector3 sourcePos = transform.position;
        Vector3 forwardOffsetVector = directions[direction] * forwardOffset;
        // int sideDirection = (sideOffset>0)?-1:1;
        Vector3 sideOffsetVector = directions[((direction-1+4)%4)] * sideOffset;
        Vector3 offset = forwardOffsetVector + sideOffsetVector;
        // RaycastHit hitInfo;
        // Debug.Log($"ManualTick.CheckFree: {direction} source {sourcePos} offset {offset}={forwardOffsetVector}+{sideOffsetVector}");
        // hit 
        // if (Physics.SphereCast(sourcePos+offset, 0.4f, Vector3.zero, out hitInfo, 0)) {
        if (Physics.Raycast(sourcePos, offset, offset.magnitude)) {
            return true;
        } 
        // no hit
        else {
            return false;
        }
    }

    [Task]
    void CheckDirectionFree (OFFSET_DIR dir_offset) {
        if (Task.isStartingOrRunning) {
            Vector3 sourcePos = transform.position;
            int final_dir = ((direction+(int)dir_offset+4)%4);
            Vector3 offset = directions[final_dir];
            RaycastHit hitInfo;
            if (Physics.SphereCast(sourcePos, 0.4f, offset, out hitInfo, offset.magnitude)) {
                if (Task.isDebugging) Debug.Log("ManualTick.CheckFree: t " + offset + " " + direction + " " + dir_offset + "(" + (int)dir_offset + ") " + final_dir + " " + hitInfo + " " + sourcePos + " " + offset);
                Task.SetFailed();
            } else {
                if (Task.isDebugging) Debug.Log("ManualTick.CheckFree: f " + offset + " " + direction + " " + final_dir + " " + hitInfo + " " + sourcePos + " " + offset);
                Task.SetSucceeded();
            }
        }
    }

    [Task]
    void CheckDirectionOccupied (OFFSET_DIR dir_offset) {
        if (Task.isStartingOrRunning) {
            Vector3 sourcePos = transform.position;
            int final_dir = ((direction+(int)dir_offset+4)%4);
            Vector3 offset = directions[final_dir];
            RaycastHit hitInfo;
            if (Physics.SphereCast(sourcePos, 0.4f, offset, out hitInfo, offset.magnitude)) {
                if (Task.isDebugging) Debug.Log("ManualTick.CheckFree: t " + offset + " " + direction + " " + dir_offset + "(" + (int)dir_offset + ") " + final_dir + " " + hitInfo + " " + sourcePos + " " + offset);
                Task.SetSucceeded();
                // Task.SetFailed();
            } else {
                if (Task.isDebugging) Debug.Log("ManualTick.CheckFree: f " + offset + " " + direction + " " + final_dir + " " + hitInfo + " " + sourcePos + " " + offset);
                Task.SetFailed();
            }
        }
    }


    [Task]
    void TurnRight () {
        if (Task.isStartingOrRunning) {
            direction = ((direction - 1) + 4) % 4;
            TurnCharacter();
            if (Task.isDebugging) Debug.Log("ManualTick.TurnRight"+direction);
            Task.SetSucceeded();
        }
    }

    [Task]
    void TurnLeft () {
        if (Task.isStartingOrRunning) {
            direction = (direction + 1) % 4;
            TurnCharacter();
            if (Task.isDebugging) Debug.Log("ManualTick.TurnLeft"+direction);
            Task.SetSucceeded();
        }
    }

    [Task]
    void TurnRandom () {
        if (Task.isDebugging) Debug.Log("ManualTick.TurnRandom "+Task.getState);
        if (Task.isStartingOrRunning) {
            int randomDirection = (Random.Range(0,2) * 2 - 1);
            direction = (direction + 4 + randomDirection) % 4;
            TurnCharacter();
            if (Task.isDebugging) Debug.Log("ManualTick.TurnRandom: "+randomDirection);
            Task.SetSucceeded();
        }
    }

    void TurnCharacter () {
        transform.localRotation = Quaternion.Euler(0,0,direction*90);
    }

    [Task]
    void MoveDirection () {
        if (Task.isStartingOrRunning) {
            if (Task.isDebugging) Debug.Log("ManualTick.MoveDirection "+direction);
            Vector3 offset = directions[direction];
            transform.position += offset;
            Task.SetSucceeded();
        }
    }


    [Task]
    void ChanceIn100 (float percentage) {
        if (Task.isStartingOrRunning) {
            if (Task.isDebugging) Debug.Log("ManualTick.Chance "+direction);
            if (percentage > Random.Range(0f, 100f)) {
                Task.SetSucceeded();
            }
            else {
                Task.SetFailed();
            }
        }
    }

    [Task]
    void Fail () {
        if (Task.isStartingOrRunning) {
            Task.SetFailed();
        }
    }
}