﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Reflection;
using UnityEngine;
using ProtoBuf.Meta;
using ProtoBuf;
using Gaazar;


[Remote]
[RequireComponent(typeof(NettedIdentity))]
public class NettedBehavior:MonoBehaviour,INetted
{
    static List<Type> registeredTypes;
    public int objID { get; set; }
    public NettedBehavior gObj { get; set; }
    public Connection owner { get; set; }
    public MethodInfo[] rpcMethods;
    public List<MemberInfo> syncVars;
    [HideInInspector]
    public NettedIdentity nettedID;
    public bool strictSyncorized;
    public virtual void MonoStart() { }
    public virtual void MonoDestroy() { }

    void INetted.Receive(MemoryStream ms)
    {
        if (OnReceivePrep()) return;
        RuntimeTypeModel.Default.Deserialize(ms,this,this.GetType());
        OnReceivePost();
    }
    bool INetted.Send(out byte[] data)
    {
        data = null;
        if (OnSendPrep()) return false;
        MemoryStream ms = new MemoryStream();
        RuntimeTypeModel.Default.Serialize(ms,this);
        data = ms.ToArray();
        ms.Dispose();
        OnSendPost();
        return true;
    }

    private void Start()
    {
        if (registeredTypes == null)
            registeredTypes = new List<Type>();
        gObj = this;
        nettedID = gameObject.GetComponent<NettedIdentity>();
        nettedID.netteds.Add(this);
        List<MethodInfo> methods = new List<MethodInfo>();
        List<PropertyInfo> properties = new List<PropertyInfo>();
        foreach (var m in GetType().GetMethods())
        {
            var a = m.GetCustomAttribute<RPCMethodAttribute>();
            if (a != null)
            {
                methods.Add(m);
            }
        }
        foreach (var m in GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic))
        {
            var a = m.GetCustomAttribute<RPCMethodAttribute>();
            if (a != null)
            {
                methods.Add(m);
            }
        }
        rpcMethods = methods.ToArray();
        List<MemberInfo> fields = new List<MemberInfo>();
        foreach (var m in GetType().GetFields())
        {
            var a = m.GetCustomAttribute<SyncVarAttribute>();
            if (a != null)
            {
                fields.Add(m);
            }
        }
        foreach (var m in GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic))
        {
            var a = m.GetCustomAttribute<SyncVarAttribute>();
            if (a != null)
            {
                fields.Add(m);
            }
        }
        foreach (var m in GetType().GetProperties())
        {
            var a = m.GetCustomAttribute<SyncVarAttribute>();
            if (a != null)
            {
                fields.Add(m);
            }
        }
        foreach (var m in GetType().GetProperties(BindingFlags.Instance | BindingFlags.NonPublic))
        {
            var a = m.GetCustomAttribute<SyncVarAttribute>();
            if (a != null)
            {
                fields.Add(m);
            }
        }

        syncVars = fields;
        if(!registeredTypes.Contains(this.GetType()))
        {
            registeredTypes.Add(this.GetType());
            MetaType mt = RuntimeTypeModel.Default.Add(this.GetType(),false);
            int i;
            for ( i = 0; i < syncVars.Count; i++)
            {
                mt.AddField(i+1,syncVars[i].Name);
            }
            OnConstructTypePrep(mt,i+1);

        }
        MonoStart();
        
    }
    public virtual void OnSendPost(bool force = false) { }
    public virtual bool OnSendPrep(bool force = false) { return false; }
    public virtual void OnReceivePost() { }
    public virtual bool OnReceivePrep() { return false; }
    public virtual void OnConstructTypePrep(MetaType mt,int index)
    { 
    
    }
    private void OnDestroy()
    {
        gameObject.GetComponent<NettedIdentity>().netteds.Remove(this);
        MonoDestroy();
    }
}
