﻿using Gaazar;
using ProtoBuf;
using ProtoBuf.Meta;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Policy;
using UnityEditor;
using UnityEngine;
using UnityEngine.PlayerLoop;
using UnityEngine.Rendering;
using UnityEngine.UI;

[RequireComponent(typeof(MeshRenderer))]
public class NettedRenderer : NettedBehavior
{
    // Start is called before the first frame update
    //[SyncVar]
    //public bool Rigidbody;
    //[SyncVar] 
   // public bool mesh;
    //[SyncVar]
    //public bool meshRenderer;
    //public static readonly string[] SHADERKEYWORDS = { "_NORMALMAP", "_ALPHATEST_ON", "_ALPHAPREMULTIPLY_ON", "_EMISSION", "_METALLICSPECGLOSSMAP", "_SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A", "_OCCLUSIONMAP", "_SPECULARHIGHLIGHTS_OFF", "_ENVIRONMENTREFLECTIONS_OFF", "_SPECULAR_SETUP", "_RECEIVE_SHADOWS_OFF" };

    //Rigidbody rb_;
    //MeshFilter m_;
    MeshRenderer mr_;

    //[SyncVar]
    //Rigidbody rb;
    [SyncVar]
    string meshName;
    [SyncVar]
    MaterialPacker[] mr;

    [HideInInspector]
    public bool needUpdate;

    //test
    byte[] save;
    //testend

    void Update()
    {
        /*
        if (Input.GetKeyDown(KeyCode.T))
        {
            MemoryStream ms = new MemoryStream();
            OnSendPrep();
            RuntimeTypeModel.Default.Serialize(ms,this);
            save = ms.ToArray();
            UnityEngine.Debug.Log(save.Length+"|"+BitConverter.ToString(save));
        }
        if (Input.GetKeyDown(KeyCode.F))
        {
            MemoryStream ms = new MemoryStream(save);
            OnReceivePrep();
            RuntimeTypeModel.Default.Deserialize(ms,this,this.GetType());
            OnReceivePost();
        }
        if (Input.GetKeyDown(KeyCode.V))
        {
            //NettedManager.CreateInstance("insurgent",999,"DynamicSpawnPrefab");
        }*/
    }
    public override void OnConstructTypePrep(MetaType mt,int index)
    {
        //mt.Add(index,"transform");
    }
    override public bool OnSendPrep(bool force)
    {
        if (!(force || needUpdate)) return true;
        /*rb = null;mr = null;meshName = "";
        if (Rigidbody)
        {
            if (rb_ == null) rb_ = GetComponent<Rigidbody>();
            rb = rb_;
        }
        if (mesh)
        {
            if (m_ == null) m_ = GetComponent<MeshFilter>();
            meshName = m_.sharedMesh.name;
        }*/
        //if (meshRenderer)//TODO: Optimise: check and modify not reconstruct
        {
            List<MaterialPacker> mps = new List<MaterialPacker>();
            if (mr_ == null) mr_ = GetComponent<MeshRenderer>();
            foreach (var i in mr_.sharedMaterials)
            {
                mps.Add(new MaterialPacker(i));
            }
            mr = mps.ToArray();
            //mr = rb_;
        }
        return false;
    }
    public override void OnReceivePost()
    {
        Material[] nwmats = new Material[mr.Length];
        for (int i = 0; i < mr.Length;i++)
        {
            nwmats[i] = new Material(NettedManager.LoadAsset<Material>(mr[i].name));
            //nwmats[i] = new Material(Shader.Find(mr[i].shaderName));
            nwmats[i].name = mr[i].name;
            /*for (int n = 0; n < SHADERKEYWORDS.Length; n++)
            {
                if(mr[i].enabledKeyword[n])
                    nwmats[i].EnableKeyword(SHADERKEYWORDS[n]);
            }*/
            if (nwmats[i] == null)
            {
                //Debug.LogError(mr[i].shaderName);
            }
            //nwmats[i].name = mr[i].name;
            for (int n = 0; n < mr[i].value.Length; n++)
            {
                int id = nwmats[i].shader.GetPropertyNameId(n);
                //UnityEngine.Debug.Log(nwmats[i].shader.GetPropertyType(n)+"|"+(ShaderPropertyType)mr[i].type[n]);
                ShaderPropertyType type = nwmats[i].shader.GetPropertyType(n);// (ShaderPropertyType)mr[i].type[n];
                //MemoryStream ms;
                //nwmats[i].EnableKeyword(nwmats[i].shaderKeywords[n]);
                if (mr[i].value[n].TextureName == "~!N") continue;
                switch (type)
                {
                    case ShaderPropertyType.Color:
                        //Color c = new Color();
                        //ms = new MemoryStream(mr[i].value[n].bytes);
                        //RuntimeTypeModel.Default.Deserialize(ms,c,typeof(Color));
                        nwmats[i].SetColor(id, mr[i].value[n].Vector);
                        break;
                    case ShaderPropertyType.Float:
                        nwmats[i].SetFloat(id, mr[i].value[n].Vector.x);
                        break;
                    case ShaderPropertyType.Range:
                        nwmats[i].SetFloat(id, mr[i].value[n].Vector.x);
                        break;
                    case ShaderPropertyType.Vector:
                        //ms = new MemoryStream(mr[i].value[n].bytes);
                        //RuntimeTypeModel.Default.Deserialize(ms, v, v.GetType());
                        nwmats[i].SetVector(id, mr[i].value[n].Vector);
                        break;
                    case ShaderPropertyType.Texture:
                        if (mr[i].value[n].TextureName == null)
                        {
                            nwmats[i].SetTexture(id, null);
                            break;
                        }
                        string textureName = mr[i].value[n].TextureName;
                        Debug.Log(mr[i].value[n].TextureName);
                        Texture txt = NettedManager.LoadAsset<Texture>(textureName);
                        nwmats[i].SetTexture(id, txt);
                        //TODO: find the texture from aseetbudle/resources and applt it to the matarial;above

                        break;
                    default:
                        break;
                }
            }
            //nwmats[i].SetOverrideTag("",);
            /*for(int j = 0;j<nwmats[i].passCount;j++)
                Debug.Log(nwmats[i].GetPassName(j));
            */
        }
        //if (mesh)
        mr_.materials = nwmats;
        //TODO: find the mesh from assetbundle/resources by its name and apply it;
    }
    public override bool OnReceivePrep()
    {
        mr = new MaterialPacker[0];
        return false;
    }

    // Update is called once per frame
}
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
class MaterialPacker 
{
    //public string shaderName;
    public string name;
    //public string[] keyWords;
    //public int[] type;
    public Bytes[] value;
    //public bool[] enabledKeyword = new bool[NettedBasic.SHADERKEYWORDS.Length];
    //public string[] ovrtag;
    //public string[] ovrtagval;
    Material mat;
    public MaterialPacker() { }
    public MaterialPacker(Material m)
    {
        mat = m;
        Update();
    }
    Color co;
    public void Update()
    {
        Material m = mat;
        name = mat.name;
        //shaderName = mat.shader.name;
        //UnityEngine.Debug.Log(shaderName);
        List<string> kw = new List<string>();
        List<int> tn = new List<int>();
        List<Bytes> bs = new List<Bytes>();
        //m.GetShaderPassEnabled
        /*for(int i = 0;i < NettedBasic.SHADERKEYWORDS.Length;i++)
        {
            enabledKeyword[i] = m.IsKeywordEnabled(NettedBasic.SHADERKEYWORDS[i]);
        }*/

        /*for (int i = 0; i < m.passCount; i++)
        {
            Debug.Log(m.GetShaderPassEnabled(m.GetPassName(i)));
        }*/
            
        for (int i = 0; i < m.shader.GetPropertyCount(); i++)
        {
            
            int id = m.shader.GetPropertyNameId(i);
            //Debug.Log(id+ "||"+ m.shader.GetPropertyType(i));
            //kw.Add(id);
            tn.Add((int)m.shader.GetPropertyType(i));
            Bytes b;
            float temp;
            //MemoryStream ms = new MemoryStream();
            switch (m.shader.GetPropertyType(i))
            {
                case ShaderPropertyType.Color:
                    Color c = m.GetColor(id);
                    //RuntimeTypeModel.Default.Serialize(ms, c);
                    b = new Bytes(c);
                    break;
                case ShaderPropertyType.Range:
                    temp = m.GetFloat(id);
                    b = new Bytes(new Vector4(temp, temp, temp, temp));
                    break;
                case ShaderPropertyType.Float:
                    temp = m.GetFloat(id);
                    b = new Bytes(new Vector4(temp, temp, temp, temp));
                    break;
                case ShaderPropertyType.Vector:
                    //RuntimeTypeModel.Default.Serialize(ms, m.GetVector(id));
                    b = new Bytes(m.GetVector(id));
                    break;
                case ShaderPropertyType.Texture:
                    if (m.GetTexture(id) == null)
                    {
                        b = new Bytes("~!N");
                    }
                    else
                    {
                        b = new Bytes(m.GetTexture(id).name);
                    }
                    break;

                default:
                    b = new Bytes("~!N");
                    break;
            }
            bs.Add(b);
        }
        //keyWords = kw.ToArray();
        //type = tn.ToArray();
        value = bs.ToArray();
    }
}

[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
public class Bytes
{
    //public bool isNull = false;
    //public byte[] bytes;
    public string TextureName;
    public Vector4 Vector;
    public Bytes() { }
    public Bytes(Color c) { Vector = c;  }
    public Bytes(Vector4 v) { Vector = v; }
    public Bytes(string s) { TextureName = s; }
    //public Bytes(byte[] b) { bytes = b; }
    //public Bytes(bool isnull) { isNull = isnull; /*bytes = new byte[] { 0};*/ }

}

[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
class ColorC
{
    public float r, g, b, a;
    public ColorC() { }
    public ColorC(float r_, float g_, float b_, float a_) { r = r_;g = g_;b = b_;a = a_; }
    public ColorC(Color c) { r = c.r;g = c.g;b = c.g;a = c.a; }
}