﻿using System.Collections.Generic;
using Neuralyzer.Core;
using Neuralyzer.Transport;
using Newtonsoft.Json;
using UnityEngine;

namespace Neuralyzer.Components
{
  public class NStateTracker : MonoBehaviour, ITrackable
  {
    #region Itrackable properties //to get around interfaces not liking fields
    public string prefab
    {
      get { return Prefab; }
      set { Prefab = value; }
    }

    public string id
    {
      get { return Id; }
      set { Id = value; }
    }
    public bool isLocal { get; set; }
    #endregion
    [Tooltip("Only set for scene objects. Use something descriptive and unique")]
    public string Id;
    [HideInInspector]
    public string ownerId;
    [Tooltip("Only set this true if the object does not need to be loaded at runtime. eg: an object built into the scene")]
    public bool isSceneObject;
    [Tooltip("This is the name of the prefab that will be spawned on remote machines. It does NOT need to match the local prefab")]
    public string Prefab;
    [HideInInspector]
    public new Transform transform;
    public float lerpSpeed;
    [Tooltip("Lock this Tracker to another transform without parenting")]
    public Transform constrainToTransform;
    [Tooltip("Should this object be destroyed on the server when the player logs out")]
    public bool destroyOnLogout;
    private Vector3 actualPosition;
    private Vector3 actualLook = Vector3.forward;

    public void Awake()
    {
      transform = GetComponent<Transform>();
    }

    public void Start()
    {
      if (isSceneObject)
        Init();
    }

    public void Init()
    {
      if (!NeuraManager.Instance.AddSceneObject(this)
      ) //try to add to global state and if object already exists destroy this one
      {
        Destroy(gameObject);
        return;
      }
      isLocal = NeuraCore.Instance.config.Username == ownerId;
    }

    public RoomObject ToRoomObject()
    {
      return new RoomObject
      {
        id = id,
        props = new Dictionary<string, object>
        {
          {"ownerId", ownerId},
          {"prefab", prefab},
          {"position", transform.position},
          {"lookDirection", transform.forward},
          {"disposable", destroyOnLogout }
        }
      };
    }

    public void UpdateFromRoomObject(RoomObject stateRoomObject)
    {
      //should call moveto and lookat
      MoveTo(JsonConvert.DeserializeObject<Vector3>(stateRoomObject.props["position"].ToString()));
      LookAt(JsonConvert.DeserializeObject<Vector3>(stateRoomObject.props["lookDirection"].ToString()));
    }

    public void MoveTo(Vector3 newPos)
    {
      actualPosition = newPos;
    }

    public void LookAt(Vector3 newLook)
    {
      actualLook = newLook;
    }

    public void Update()
    {
      if (constrainToTransform)
      {
        actualPosition = constrainToTransform.position;
        actualLook = constrainToTransform.forward;
      }
      if (!constrainToTransform && isLocal) return;
      if (transform.position != actualPosition)
        transform.position = Vector3.MoveTowards(transform.position, actualPosition, lerpSpeed * Time.deltaTime);
      if (transform.forward != actualLook)
        transform.forward = Vector3.MoveTowards(transform.forward, actualLook, lerpSpeed * Time.deltaTime);
    }
  }
}
