using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.ComponentModel;

namespace TMC_Tool
{
  [Serializable]
  public class TmcTables
  {
    public TmcTables(TMCData data)
    {
      this.ObjData = new ObservableCollection<ObjectData>();
      this.VIData = new ObservableCollection<VtxIdxGrpData>();
      this.Nodes = new List<NodeData>();

      this.TexCount = (byte)data.Tex.Count;
      this.Textures = new ObservableCollection<byte>();
      for (byte i = 0; i < this.TexCount; i++) this.Textures.Add(i);
      this.MtlCols = new ObservableCollection<int>();
      for (byte i = 0; i < data.ColGrp.Count; i++) this.MtlCols.Add(i);
      this.Matecps = new ObservableCollection<int>();
      if (data.MateCp != null) for(byte i = 0; i < data.MateCp.Count + 1; i++) this.Matecps.Add(i);
      this.Mcamtrls = new ObservableCollection<int>();
      if (data.Mat != null) for (int i = -1; i < data.Mat.Count; i++) this.Mcamtrls.Add(i);
      this.ShadowParams = new int[] { 1, 2, 4 };
      this.TexTypes = new byte[] { 0, 1, 2, 3 };
      this.Z1s = new string[] { "02", "04", "06", "08", "0A", "0C", "0E", "10" };
      this.Z2s = new int[] { 0, 1, 2 };
      this.UVCounts = new byte[] { 0, 1, 2, 3, 4 };

      TexTypesSet = new Dictionary<int, string[]>();
      TexTypesSet[1] = new string[] { "0", "0 3" };
      TexTypesSet[2] = new string[] { "0 0", "0 1", "0 2", "0 3", "0 0 3", "0 1 3" };
      TexTypesSet[3] = new string[] { "0 0 1", "0 0 2", "0 0 3", "0 1 2", "0 1 3", "0 0 1 3", "0 0 2 3", "0 1 2 3" };
      TexTypesSet[4] = new string[] { "0 0 1 2", "0 0 1 3", "0 0 2 3", "0 1 2 3" };

      this.ObjCounts = new Dictionary<int, int>();

      byte count = 0;
      int objIndex = 0;

      foreach (var grp in data.ObjGrp)
      {
        if (grp.ChildCount == 0) continue;

        var newObj = new ObjectData();
        newObj.SetObjectData(data, grp, null, count, -1);
        this.ObjCounts[newObj.ID] = 0;

        this.ObjData.Add(newObj);
        count++;
        objIndex = 0;

        foreach (var obj in grp.Obj)
        {
          newObj = new ObjectData();
          newObj.SetObjectData(data, grp, obj, count, objIndex);
          this.ObjCounts[newObj.Grp]++;

          this.ObjData.Add(newObj);
          count++;
          objIndex++;
        }
      }

      for (byte i = 0; i < data.VtxGrp.Count; i++)
      {
        var newVI = new VtxIdxGrpData();
        newVI.SetVxtIdxGrpData(data, i);
        this.VIData.Add(newVI);
      }

      foreach (var node in data.Node)
      {
        var newNode = new NodeData(node);
        foreach (var idx in data.Hie[node.Index].Children)
        {
          newNode.Children.Add(data.Node[idx].Name);
        }
        if (node.ObjIndex != -1 && data.ObjGrp[node.ObjIndex].ChildCount == 0) newNode.ObjIndex = -1;
        this.Nodes.Add(newNode);
      }
    }

    [Serializable]
    public class ObjectData : INotifyPropertyChanged
    {
      public ObjectData()
      {
      }

      public void SetObjectData(TMCData data, ObjectGroup grp, ObjectPart obj, byte count, int objIndex)
      {
        this.VtxIdxGruops = new ObservableCollection<byte>();
        this.TexTypes = new ObservableCollection<string>();

        this.IsEditable = false;
        this.IsDeleted = false;
        this.IsVgrpChanged = false;
        this.Index = count;
        this.OriginalObjIndex = objIndex;

        this.Node = -1;
        this.Toggle = -1;
        //this.Glasses = -1;
        this.UVCount = -1;
        this.TexType = -1;
        this.CastShadow = -1;
        this.ReceiveShadow = -1;

        this.RecentTexs = new byte?[4];
        this.RecentTexTypes = new char?[4];

        if (obj == null)
        {
          this.ID = (byte)grp.ID;
          this.OriginalGrpIndex = grp.ID;
          this.Name = grp.Name;
          this.Node = grp.Node;

          uint totalVtxCount = 0;
          uint totalIdxCount = 0;
          foreach (var decl in grp.Decl)
          {
            totalVtxCount += decl.VtxCount;
            totalIdxCount += decl.IdxCount;
          }
          this.VtxCount = totalVtxCount;
          this.IdxCount = totalIdxCount;

          this.Grp = -1;

          if (data.ObjGrp.Count > 1 &&
            grp.Name != "WGT_body" &&
            (
              !(data.ObjGrp.Where(objGeo => objGeo.Name == "WGT_body").Count() == 0 &&
                System.Text.RegularExpressions.Regex.IsMatch(grp.Name, @"WGT_(.*body1?|.*cos|.*cos_all|.*cos_[a-z]{0,3}[0-9]{3}|rtm|battle|woman_m|.uku1?|[0-9]p)[^0-9]*$|MOT00_Hips")) ||
              System.Text.RegularExpressions.Regex.IsMatch(grp.Name, @"WGT_body_base")
            )
          )
          {
            this.Toggle = 0; //0xBB474F4B
            //this.Glasses = 0; //0xA50A10E2
            if (grp.NodeCp != -1)
            {
              foreach (var cp in data.NodeCp[grp.NodeCp].cp)
              {
                if (cp.Data1 == 0xBB474F4B && cp.Param == 0)
                {
                  this.Toggle = 1;
                }
                else if (cp.Data1 == 0xBB474F4B && cp.Param == 1)
                {
                  this.Toggle = 2;
                }
                else if (cp.Data1 == 0xA50A10E2 && cp.Param == 0)
                {
                  this.Toggle = 3;
                }
                else if (cp.Data1 == 0xA50A10E2 && cp.Param == 1)
                {
                  this.Toggle = 4;
                }
              }
            }
          }
          this.Transparent = -1;

          this.Z1 = data.ObjInfo[grp.ID].Z1.ToString("X2");
          this.Z2 = data.ObjInfo[grp.ID].Z2;

        }
        else
        {
          for (byte i = 0; i < grp.Decl.Count; i++) this.VtxIdxGruops.Add((byte)grp.Decl[i].VtxGrpIndex);

          this.Name = grp.Name + "_" + obj.ID.ToString("x");
          this.Grp = grp.ID;
          this.OriginalGrpIndex = grp.ID;
          this.ID = (byte)obj.ID;

          //this.OriginalDecl = (byte)obj.DeclIndex;
          this.VtxGrp = (byte)obj.VtxGrpIndex;
          //this.IdxGrp = (byte)grp.Decl[obj.DeclIndex].IdxGrpIndex;
          this.VtxCount = obj.VtxCount;
          this.IdxCount = obj.IdxCount;
          this.TexCount = (byte)obj.TexCount;
          this.UVCount = (int)data.VtxGrp[(byte)obj.VtxGrpIndex].UVCount;

          byte?[] texs = new byte?[4];
          string checkTexType = "";
          for (byte i = 0; i < 4; i++)
          {
            if (obj.TexCount > i)
            {
              texs[i] = (byte)obj.Tex[i].Num;
              checkTexType += obj.Tex[i].Type;
            }
          }
          this.Tex1 = texs[0];
          this.Tex2 = texs[1];
          this.Tex3 = texs[2];
          this.Tex4 = texs[3];

          this.SetTexType(this.UVCount, checkTexType);

          this.EnvironmentMap = false;
          if (obj.TexParams[2] % 8 == 1)
          {
            this.EnvironmentMap = true;
          }
          if (checkTexType.IndexOf("3") != -1)
          {
            this.ChangeableEnvMap = true;
          }

          if (obj.Transparent1 != 0)
            this.Transparent = 1;
          else if (obj.Transparent2 != 0)
            this.Transparent = 2;
          else
            this.Transparent = 0;

          this.DoubleSided = (obj.DoubleSided == 0) ? false : true;
          this.MtrCol = obj.MtrColor;

          if (data.Itable != null) this.Matecp = data.Itable[grp.ID].Indices[obj.ID];
          if (data.McaIdx != null) this.Mcamtrl = data.McaIdx[grp.ID].MatID[obj.ID];

          this.Z1 = data.ObjInfo[grp.ID].Info[obj.ID].Z1.ToString("X2");
          this.Z2 = data.ObjInfo[grp.ID].Info[obj.ID].Z2;

          this.CastShadow = data.ObjInfo[grp.ID].Info[obj.ID].Params[2];
          this.ReceiveShadow = data.ObjInfo[grp.ID].Info[obj.ID].Params[3];

          this.IsEditable = (this.VtxCount == 0 && this.IdxCount == 0) ? true : false;
        }

      }

      // Common
      public byte Index { get; set; }
      private byte _id;
      public byte ID
      {
        get { return _id; }
        set
        {
          _id = value;
          OnPropertyChanged("ID");
        }
      }
      public int OriginalObjIndex { get; set; }
      public int Grp { get; set; }
      public int OriginalGrpIndex { get; set; }
      //public string Name { get; set; }
      private string _name;
      public string Name
      {
        get { return _name; }
        set
        {
          _name = value;
          OnPropertyChanged("Name");
        }
      }
      private uint? _vtxCount;
      public uint? VtxCount
      {
        get { return _vtxCount; }
        set
        {
          _vtxCount = value;
          OnPropertyChanged("VtxCount");
        }
      }
      private uint? _idxCount;
      public uint? IdxCount
      {
        get { return _idxCount; }
        set
        {
          _idxCount = value;
          OnPropertyChanged("IdxCount");
        }
      }

      private bool _isEditable;
      public bool IsEditable
      {
        get { return _isEditable; }
        set
        {
          _isEditable = value;
          OnPropertyChanged("IsEditable");
        }
      }
      private bool _isDeleted;
      public bool IsDeleted
      {
        get { return _isDeleted; }
        set
        {
          _isDeleted = value;
          OnPropertyChanged("IsDeleted");
        }
      }
      private bool _isVgrpChanged;
      public bool IsVgrpChanged
      {
        get { return _isVgrpChanged; }
        set
        {
          _isVgrpChanged = value;
          OnPropertyChanged("IsVgrpChanged");
        }
      }
      private bool _isAdded;
      public bool IsAdded
      {
        get { return _isAdded; }
        set
        {
          _isAdded = value;
          OnPropertyChanged("IsAdded");
        }
      }

      // Object Group
      public int Node { get; set; }
      private int _toggle;
      public int Toggle
      {
        get { return _toggle; }
        set
        {
          _toggle = value;
          OnPropertyChanged("Toggle");
        }
      }
      //private int _glasses;
      //public int Glasses
      //{
      //  get { return _glasses; }
      //  set
      //  {
      //    _glasses = value;
      //    OnPropertyChanged("Glasses");
      //  }
      //}

      // Object
      //public byte? OriginalDecl { get; set; }
      private byte? vtxGrp;
      public byte? VtxGrp
      {
        get { return vtxGrp; }
        set
        {
          vtxGrp = value;
          OnPropertyChanged("VtxGrp");
        }
      }
      //public byte? IdxGrp { get; set; }
      public byte? TexCount { get; set; }
      private int _uvCount;
      public int UVCount
      {
        get { return _uvCount; }
        set
        {
          _uvCount = value;
          OnPropertyChanged("UVCount");
        }
      }
      private byte? _tex1;
      public byte? Tex1
      {
        get { return _tex1; }
        set
        {
          _tex1 = value;
          OnPropertyChanged("Tex1");
        }
      }
      private byte? _tex2;
      public byte? Tex2
      {
        get { return _tex2; }
        set
        {
          _tex2 = value;
          OnPropertyChanged("Tex2");
        }
      }
      private byte? _tex3;
      public byte? Tex3
      {
        get { return _tex3; }
        set
        {
          _tex3 = value;
          OnPropertyChanged("Tex3");
        }
      }
      private byte? _tex4;
      public byte? Tex4
      {
        get { return _tex4; }
        set
        {
          _tex4 = value;
          OnPropertyChanged("Tex4");
        }
      }
      public byte?[] RecentTexs { get; set; }
      public char?[] RecentTexTypes { get; set; }
      private int _texType;
      public int TexType
      {
        get { return _texType; }
        set
        {
          _texType = value;
          OnPropertyChanged("TexType");
        }
      }
      private bool? _environmentMap;
      public bool? EnvironmentMap
      {
        get { return _environmentMap; }
        set
        {
          _environmentMap = value;
          OnPropertyChanged("EnvironmentMap");
        }
      }
      private bool _changeableEnvMap;
      public bool ChangeableEnvMap
      {
        get { return _changeableEnvMap; }
        set
        {
          _changeableEnvMap = value;
          OnPropertyChanged("ChangeableEnvMap");
        }
      }
      private int _transparent;
      public int Transparent
      {
        get { return _transparent; }
        set
        {
          _transparent = value;
          OnPropertyChanged("Transparent");
        }
      }
      private bool? _doubleSided;
      public bool? DoubleSided
      {
        get { return _doubleSided; }
        set
        {
          _doubleSided = value;
          OnPropertyChanged("DoubleSided");
        }
      }
      private int? _mtrCol;
      public int? MtrCol
      {
        get { return _mtrCol; }
        set
        {
          _mtrCol = value;
          OnPropertyChanged("MtrCol");
        }
      }
      private int? _matecp;
      public int? Matecp
      {
        get { return _matecp; }
        set
        {
          _matecp = value;
          OnPropertyChanged("Matecp");
        }
      }
      private int? _mcamtrl;
      public int? Mcamtrl
      {
        get { return _mcamtrl; }
        set
        {
          _mcamtrl = value;
          OnPropertyChanged("Mcamtrl");
        }
      }
      private string _z1;
      public string Z1
      {
        get { return _z1; }
        set
        {
          _z1 = value;
          OnPropertyChanged("Z1");
        }
      }
      private int _z2;
      public int Z2
      {
        get { return _z2; }
        set
        {
          _z2 = value;
          OnPropertyChanged("Z2");
        }
      }
      private int _castShadow;
      public int CastShadow
      {
        get { return _castShadow; }
        set
        {
          _castShadow = value;
          OnPropertyChanged("CastShadow");
        }
      }
      private int _receiveShadow;
      public int ReceiveShadow
      {
        get { return _receiveShadow; }
        set
        {
          _receiveShadow = value;
          OnPropertyChanged("ReceiveShadow");
        }
      }

      public void SetTexType(int uvCount, string checkTexType)
      {
        if (this.TexTypes.Count != 0) this.TexTypes.Clear();
        this.TexType = -1;

        if (uvCount > 0)
        {
          for (int i = 0; i < TexTypesSet[uvCount].Length; i++) this.TexTypes.Add(TexTypesSet[uvCount][i]);

          for (int i = 0; i < this.TexTypes.Count; i++)
          {
            if (checkTexType == TexTypesSet[uvCount][i].Replace(" ", ""))
            {
              this.TexType = i;
              break;
            }
          }

          if (this.TexType == -1)
          {
            this.TexType = this.TexTypes.Count;
            this.TexTypes.Add("Error");
          }
        }
      }



      public ObservableCollection<byte> VtxIdxGruops { get; set; }
      public ObservableCollection<string> TexTypes { get; set; }

      public event PropertyChangedEventHandler PropertyChanged;

      private void OnPropertyChanged(string name)
      {
        if (PropertyChanged != null)
        {
          PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
      }

      public ObjectData Clone()
      {
        ObjectData cloned = (ObjectData)MemberwiseClone();

        // 参照型フィールドの複製を作成する
        if (this.RecentTexs != null)
        {
          cloned.RecentTexs = (byte?[])this.RecentTexs.Clone();
        }
        if (this.RecentTexTypes != null)
        {
          cloned.RecentTexTypes = (char?[])this.RecentTexTypes.Clone();
        }

        return cloned;
      }
    }

    [Serializable]
    public class VtxIdxGrpData : INotifyPropertyChanged
    {
      public VtxIdxGrpData()
      {
        this.IdxGrp = -1;
        this.OriginalIdxGrp = -1;
        this.ObjID = -1;
      }

      public void SetVxtIdxGrpData(TMCData data, byte idx)
      {
        this.IsEditable = false;
        this.Index = idx;
        this.OriginalIndex = idx;

        foreach (ObjectGroup grp in data.ObjGrp)
        {
          ushort[] declVtxCounts = new ushort[grp.Decl.Count];
          uint[] declIdxCounts = new uint[grp.Decl.Count];
          byte[] useCounts = new byte[grp.Decl.Count];

          if (grp.ChildCount == 0) continue;

          foreach (ObjectPart obj in grp.Obj)
          {
            declVtxCounts[obj.DeclIndex] += obj.VtxCount;
            declIdxCounts[obj.DeclIndex] += obj.IdxCount;
            useCounts[obj.DeclIndex] += 1;
          }

          for (byte i = 0; i < grp.Decl.Count; i++)
          {
            if (grp.Decl[i].VtxGrpIndex == idx)
            {
              this.IdxGrp = grp.Decl[i].IdxGrpIndex;
              this.OriginalIdxGrp = grp.Decl[i].IdxGrpIndex;
              this.ObjName = grp.Name;
              this.ObjID = grp.ID;
              this.Use = useCounts[i];
              this.OriginalDecl = i;
              this.VtxCount = declVtxCounts[i];
              this.IdxCount = declIdxCounts[i];

              this.IsEditable = (this.VtxCount == 0 && this.IdxCount == 0) ? true : false;
              break;
            }
          }
        }

        if (this.ObjID == -1) return;

        var vtxGrp = data.VtxGrp[idx];

        this.Datasize = String.Format("0x{0:X2}", (byte)vtxGrp.DataSize);

        this.Position = vtxGrp.Position;
        this.PositionType = DeclType(vtxGrp.PositionType);
        this.Normal = vtxGrp.Normal;
        this.NormalType = DeclType(vtxGrp.NormalType);
        this.BWeights = vtxGrp.BlendWeight;
        this.BWeightsType = DeclType(vtxGrp.BlendWeightType);
        this.BIndices = vtxGrp.BlendIndices;
        this.BIndicesType = DeclType(vtxGrp.BlendIndicesType);
        this.Tangent = vtxGrp.Tangent;
        this.TangentType = DeclType(vtxGrp.TangentType);

        this.UVCount = (byte)vtxGrp.UVCount;
      }

      private static string DeclType(int num)
      {
        string type = "";
        switch (num)
        {
          case 2:
            type = "Float3";
            break;
          case 3:
            type = "Float4";
            break;
          case 5:
            type = "Byte4";
            break;
          case 13:
            type = "Dec4";
            break;
          default:
            type = "Other";
            break;
        }
        return type;
      }

      private bool _isEditable;
      public bool IsEditable
      {
        get { return _isEditable; }
        set
        {
          _isEditable = value;
          OnPropertyChanged("IsEditable");
        }
      }
      private bool _isChanged;
      public bool IsChanged
      {
        get { return _isChanged; }
        set
        {
          _isChanged = value;
          OnPropertyChanged("IsChanged");
        }
      }
      private bool _isAdded;
      public bool IsAdded
      {
        get { return _isAdded; }
        set
        {
          _isAdded = value;
          OnPropertyChanged("IsAdded");
        }
      }
      private int _index;
      public int Index
      {
        get { return _index; }
        set
        {
          _index = value;
          OnPropertyChanged("Index");
        }
      }
      public int OriginalIndex { get; set; }
      public int IdxGrp { get; set; }
      public int OriginalIdxGrp { get; set; }
      //public ushort VtxCount { get; set; }
      //public uint   IdxCount { get; set; }
      private ushort _vtxCount;
      public ushort VtxCount
      {
        get { return _vtxCount; }
        set
        {
          _vtxCount = value;
          OnPropertyChanged("VtxCount");
        }
      }
      private uint _idxCount;
      public uint IdxCount
      {
        get { return _idxCount; }
        set
        {
          _idxCount = value;
          OnPropertyChanged("IdxCount");
        }
      }

      public string ObjName { get; set; }
      public int ObjID { get; set; }
      private byte _use;
      public byte Use
      {
        get { return _use; }
        set
        {
          _use = value;
          OnPropertyChanged("Use");
        }
      }
      public byte? OriginalDecl { get; set; }
      private string _datasize;
      public string Datasize
      {
        get { return _datasize; }
        set
        {
          _datasize = value;
          OnPropertyChanged("Datasize");
        }
      }
      public bool Position { get; set; }
      //public bool Normal { get; set; }
      //public bool BWeights { get; set; }
      //public bool BIndices { get; set; }
      //public bool Tangent { get; set; }
      private bool _normal;
      public bool Normal
      {
        get { return _normal; }
        set
        {
          _normal = value;
          OnPropertyChanged("Normal");
        }
      }
      private bool _bWeights;
      public bool BWeights
      {
        get { return _bWeights; }
        set
        {
          _bWeights = value;
          OnPropertyChanged("BWeights");
        }
      }
      private bool _bIndices;
      public bool BIndices
      {
        get { return _bIndices; }
        set
        {
          _bIndices = value;
          OnPropertyChanged("BIndices");
        }
      }
      private bool _tangent;
      public bool Tangent
      {
        get { return _tangent; }
        set
        {
          _tangent = value;
          OnPropertyChanged("Tangent");
        }
      }
      private byte _uvCount;
      public byte UVCount
      {
        get { return _uvCount; }
        set
        {
          _uvCount = value;
          OnPropertyChanged("UVCount");
        }
      }
      public string PositionType { get; set; }
      //public string NormalType { get; set; }
      //public string BWeightsType { get; set; }
      //public string BIndicesType { get; set; }
      //public string TangentType { get; set; }
      private string _normalType;
      public string NormalType
      {
        get { return _normalType; }
        set
        {
          _normalType = value;
          OnPropertyChanged("NormalType");
        }
      }
      private string _bWeightsType;
      public string BWeightsType
      {
        get { return _bWeightsType; }
        set
        {
          _bWeightsType = value;
          OnPropertyChanged("BWeightsType");
        }
      }
      private string _bIndicesType;
      public string BIndicesType
      {
        get { return _bIndicesType; }
        set
        {
          _bIndicesType = value;
          OnPropertyChanged("BIndicesType");
        }
      }
      private string _tangentType;
      public string TangentType
      {
        get { return _tangentType; }
        set
        {
          _tangentType = value;
          OnPropertyChanged("TangentType");
        }
      }



      public ObservableCollection<byte> VtxIdxGruops { get; set; }

      public event PropertyChangedEventHandler PropertyChanged;

      private void OnPropertyChanged(string name)
      {
        if (PropertyChanged != null)
        {
          PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
      }
    }


    public class NodeData
    {
      public NodeData(Nodes node)
      {
        this.Children = new SortedSet<string>();

        this.OriginalIndex = node.Index;
        this.Name = node.Name;
        this.ObjIndex = node.ObjIndex;
        this.IsAdded = false;
      }

      //public int Index { get; set; }
      public int OriginalIndex { get; set; }
      public string Name { get; set; }
      public int ObjIndex { get; set; }
      public SortedSet<string> Children { get; set; }
      public bool IsAdded { get; set; }
    }

    public TmcTables Clone()
    {
      // シリアル化した内容を保持しておくためのMemoryStreamを作成
      using (MemoryStream stream = new MemoryStream())
      {
        // バイナリシリアル化を行うためのフォーマッタを作成
        BinaryFormatter f = new BinaryFormatter();

        // 現在のインスタンスをシリアル化してMemoryStreamに格納
        f.Serialize(stream, this);

        // ストリームの位置を先頭に戻す
        stream.Position = 0L;

        // MemoryStreamに格納されている内容を逆シリアル化する
        return (TmcTables)f.Deserialize(stream);
      }
    }

    public ObjectData AddObjectData(ObjectData referObj, int destIdx)
    {
      var newObj = referObj.Clone();

      newObj.VtxCount = 0;
      newObj.IdxCount = 0;

      if (newObj.Grp != -1)
      {
        newObj.VtxIdxGruops = new ObservableCollection<byte>();
        foreach (var vigrp in referObj.VtxIdxGruops)
        {
          newObj.VtxIdxGruops.Add(vigrp);
        }

        if (referObj.TexTypes.Count != 0)
        {
          string checkTexType = newObj.TexTypes[(int)newObj.TexType].Replace(" ", "");
          newObj.TexTypes = new ObservableCollection<string>();
          newObj.SetTexType(newObj.UVCount, checkTexType);
        }

        newObj.IsEditable = true;
        newObj.IsVgrpChanged = true;
      }

      this.ObjData.Insert(destIdx, newObj);

      return newObj;
    }

    public ObjectData AddObjectData(ObjectData referObj)
    {
      int idx = this.ObjData.IndexOf(referObj);
      return AddObjectData(referObj, idx + 1);
    }

    public void ResetObjectIndex()
    {
      byte index = 0;
      byte objGrpIndex = 0;
      byte objIndex = 0;
      string grpName = "";
      foreach (var obj in this.ObjData)
      {
        obj.Index = index;
        index++;

        if (obj.Grp == -1)
        {
          obj.ID = objGrpIndex;
          objGrpIndex++;
          objIndex = 0;
          grpName = obj.Name;
          continue;
        }

        obj.Grp = objGrpIndex - 1;
        obj.ID = objIndex;
        obj.Name = grpName + "_" + objIndex.ToString("x");
        objIndex++;
      }
    }

    public void RebuildTexData(VtxIdxGrpData data, TMCData tmcData)
    {
      foreach (ObjectData obj in this.ObjData)
      {
        if (obj.VtxGrp == data.Index && obj.UVCount != data.UVCount)
        {
          obj.UVCount = data.UVCount;
          string checkTexType = ChangeTexCount(obj, tmcData.ObjGrp[obj.OriginalGrpIndex].Obj[obj.OriginalObjIndex]);
          obj.SetTexType(data.UVCount, checkTexType);

          if (checkTexType.IndexOf("3") != -1)
            obj.ChangeableEnvMap = true;
          else
            obj.ChangeableEnvMap = false;
        }
      }
    }

    public void ChangeTexType(ObjectData obj, int val, TMCData tmcData)
    {
      ChangeTexCount(obj, tmcData.ObjGrp[obj.OriginalGrpIndex].Obj[obj.OriginalObjIndex]);

      if (obj.TexTypes[val].IndexOf("3") != -1)
        obj.ChangeableEnvMap = true;
      else
        obj.ChangeableEnvMap = false;
    }

    public string ChangeTexCount(ObjectData obj, ObjectPart objPart)
    {
      string checkTexType = "";
      string curTexType = "";
      int texCount = obj.UVCount;
      if (obj.TexTypes.Count != 0)
      {
        if (obj.TexTypes[(int)obj.TexType] == "Error")
        {
          if (obj.UVCount > 0)
            return TexTypesSet[obj.UVCount][0].Replace(" ", "");
          else
            return "";
        }

        curTexType = obj.TexTypes[(int)obj.TexType].Replace(" ", "");

        if (obj.UVCount > 0)
        {
          int idx = Array.FindIndex(TexTypesSet[obj.UVCount].ToArray(), elem => elem == obj.TexTypes[(int)obj.TexType]);
          if (idx != -1)
          {
            texCount = curTexType.Length;
          }
        }
      }
      byte?[] texs = new byte?[] { obj.Tex1, obj.Tex2, obj.Tex3, obj.Tex4 };

      if (obj.TexCount > texCount)
      {
        for (int i = 0; i < 4; i++)
        {
          if (texCount > i)
          {
            checkTexType += curTexType[i];
          }
          else
          {
            if (texs[i] != null) obj.RecentTexs[i] = texs[i];
            if (curTexType.Length > i) obj.RecentTexTypes[i] = curTexType[i];
            texs[i] = null;
          }
        }
      }
      else
      {
        for (int i = 0; i < 4; i++)
        {
          if (obj.TexCount > i)
          {
            checkTexType += curTexType[i];
          }
          else
          {
            if (texCount > i)
            {
              if (obj.RecentTexs[i] != null)
                texs[i] = obj.RecentTexs[i];
              else if (objPart.TexCount > i)
                texs[i] = (byte)objPart.Tex[i].Num;
              else
                texs[i] = 0;
            }
            if (obj.RecentTexTypes[i] != null)
            {
              checkTexType += obj.RecentTexTypes[i];
            }
            else if (objPart.TexCount > i)
            {
              checkTexType += (byte)objPart.Tex[i].Type;
            }
          }
        }

        string tempTexType = "";
        if (texCount == 4)
        {
          if (checkTexType.IndexOf("3") != -1)
            tempTexType += "3";

          if (checkTexType.IndexOf("2") != -1 || checkTexType.IndexOf("1") == -1)
            tempTexType = "2" + tempTexType;

          if (checkTexType.IndexOf("1") != -1 || checkTexType.IndexOf("2") == -1)
            tempTexType = "1" + tempTexType;

          if (tempTexType.Length == 1)
          {
            if (tempTexType.IndexOf("1") == -1)
              tempTexType = "1" + tempTexType;
            else
              tempTexType = tempTexType + "2";
          }

          while (tempTexType.Length < 4)
          {
            tempTexType = "0" + tempTexType;
          }
          checkTexType = tempTexType;
        }
        else if (texCount == 3)
        {
          if (checkTexType.Length != 3)
          {
            if (checkTexType.IndexOf("3") != -1)
              tempTexType += "3";
            else if (checkTexType.IndexOf("2") != -1)
              tempTexType = "2" + tempTexType;

            if (checkTexType.IndexOf("1") != -1 || tempTexType.Length == 0)
              tempTexType = "1" + tempTexType;

            while (tempTexType.Length < 3)
            {
              tempTexType = "0" + tempTexType;
            }
            checkTexType = tempTexType;
          }
        }
        else if (texCount == 2)
        {
          if (checkTexType.Length != 2)
          {
            checkTexType = "00";
          }
        }
        else
        {
          checkTexType = "0";
        }
      }

      obj.Tex1 = texs[0];
      obj.Tex2 = texs[1];
      obj.Tex3 = texs[2];
      obj.Tex4 = texs[3];

      obj.TexCount = (byte)texCount;

      return checkTexType;
    }

    public void ReCalcGrpVyxIdxCount()
    {
      ushort[] declVtxCounts = new ushort[this.VIData.Count];
      uint[] declIdxCounts = new uint[this.VIData.Count];

      foreach (var obj in this.ObjData)
      {
        if (obj.Grp == -1) continue;

        declVtxCounts[(int)obj.VtxGrp] += (ushort)obj.VtxCount;
        declIdxCounts[(int)obj.VtxGrp] += (uint)obj.IdxCount;
      }

      for (byte i = 0; i < this.VIData.Count; i++)
      {
        this.VIData[i].VtxCount = declVtxCounts[i];
        this.VIData[i].IdxCount = declIdxCounts[i];
        if (this.VIData[i].VtxCount == 0 && this.VIData[i].ObjID != -1)
          this.VIData[i].IsEditable = true;
        else
          this.VIData[i].IsEditable = false;
      }
    }

    public void ReCalcGrpUseCount(int? grpID)
    {
      byte[] useCounts = new byte[this.VIData.Count];

      foreach (ObjectData obj in this.ObjData)
      {
        if (obj.Grp != -1) useCounts[(int)obj.VtxGrp] += 1;
      }

      foreach (VtxIdxGrpData viData in this.VIData)
      {
        if (grpID == null || viData.ObjID == grpID) viData.Use = useCounts[viData.Index];
      }
    }

    public void ReCalcDataSize(VtxIdxGrpData data)
    {
      int datasize = 0;
      if (data.Position)
      {
        if (data.PositionType == "Float3")
          datasize += 12;
        else
          datasize += 16;
      }
      if (data.Normal)
      {
        if (data.NormalType == "Float3")
          datasize += 12;
        else
          datasize += 16;
      }
      if (data.BWeights)
      {
        if (data.BWeightsType == "Float4")
          datasize += 16;
        else
          datasize += 4;
      }
      if (data.BIndices) datasize += 4;
      if (data.Tangent) datasize += 16;
      datasize += data.UVCount * 4;

      data.Datasize = String.Format("0x{0:X2}", datasize);
    }

    public int InsertVIData(VtxIdxGrpData referData, int destIdx, int objID)
    {
      if (objID == -1)
      {
        objID = referData.ObjID;
      }

      if (destIdx == -1)
      {
        Dictionary<int, int> objEndIndices = new Dictionary<int, int>();
        int startIndex = 0;
        int curID = this.VIData[0].ObjID;
        for (byte i = 1; i < this.VIData.Count; i++)
        {
          if (curID == this.VIData[i].ObjID)
          {
            startIndex++;
          }
          else
          {
            objEndIndices[this.VIData[i - 1].ObjID] = startIndex;
            startIndex++;
          }
        }
        objEndIndices[this.VIData[this.VIData.Count - 1].ObjID] = startIndex;

        destIdx = objEndIndices[objID] + 1;
      }

      VtxIdxGrpData newVI = new VtxIdxGrpData();
      newVI.Index = -1;
      newVI.IsEditable = true;
      newVI.IsChanged = false;
      newVI.IsAdded = true;
      newVI.OriginalIndex = referData.OriginalIndex;
      newVI.OriginalDecl = referData.OriginalDecl;
      newVI.OriginalIdxGrp = referData.OriginalIdxGrp;
      newVI.VtxCount = 0;
      newVI.IdxCount = 0;
      newVI.ObjName = referData.ObjName;
      newVI.ObjID = objID;
      newVI.Use = 0;
      newVI.Datasize = referData.Datasize;
      newVI.Position = referData.Position;
      newVI.Normal = referData.Normal;
      newVI.BWeights = referData.BWeights;
      newVI.BIndices = referData.BIndices;
      newVI.Tangent = referData.Tangent;
      newVI.UVCount = referData.UVCount;
      newVI.PositionType = referData.PositionType;
      newVI.NormalType = referData.NormalType;
      newVI.BWeightsType = referData.BWeightsType;
      newVI.BIndicesType = referData.BIndicesType;
      newVI.TangentType = referData.TangentType;

      this.VIData.Insert(destIdx, newVI);

      for (int i = this.VIData.Count - 1; i >= 0; i--)
      {
        foreach (var obj in this.ObjData)
        {
          // ObjDataのVtxGrpを再設定
          if (obj.Grp != -1 && obj.VtxGrp == this.VIData[i].Index) obj.VtxGrp = (byte)i;
        }
        // VIDataのIndexを再設定
        this.VIData[i].Index = i;
      }

      return destIdx;
    }

    public int InsertVIData(VtxIdxGrpData referData)
    {
      return InsertVIData(referData, -1, -1);
    }

    public void ResetVtxIdxGruops()
    {
      // ObjDataのVtxIdxGruopsを再設定
      foreach (var obj in this.ObjData)
      {
        if (obj.Grp != -1)
        {
          byte vtxGrp = (byte)obj.VtxGrp;
          obj.VtxIdxGruops.Clear();
          foreach (var data in this.VIData)
          {
            if (obj.Grp == data.ObjID) obj.VtxIdxGruops.Add((byte)data.Index);
          }
          obj.VtxGrp = vtxGrp;
        }
      }
    }

    public void ResetVIDataIdxGrp()
    {
      int count = 0;
      for (int i = 0; i < this.VIData.Count; i++)
      {
        if (this.VIData[i].Use == 0) continue;

        this.VIData[i].IdxGrp = count;
        count++;
      }
    }

    public void ResetNodesObjIndex()
    {
      for (int i = 0; i < this.Nodes.Count; i++)
      {
        if (this.Nodes[i].ObjIndex == -1) continue;

        int idx = Array.FindIndex(this.ObjData.ToArray(), elem => elem.Name == this.Nodes[i].Name);
        this.Nodes[i].ObjIndex = this.ObjData[idx].ID;
      }
    }



    public Dictionary<int, int> ObjCounts { get; set; }

    public byte? TexCount { get; set; }
    public ObservableCollection<byte> Textures { get; set; }
    public ObservableCollection<int> MtlCols { get; set; }
    public ObservableCollection<int> Matecps { get; set; }
    public ObservableCollection<int> Mcamtrls { get; set; }
    public int[] ShadowParams { get; set; }
    public byte[] TexTypes { get; set; }
    public string[] Z1s { get; set; }
    public int[] Z2s { get; set; }
    public byte[] UVCounts { get; set; }

    public ObservableCollection<ObjectData> ObjData { get; set; }
    public ObservableCollection<VtxIdxGrpData> VIData { get; set; }
    public List<NodeData> Nodes { get; set; }

    public static Dictionary<int, string[]> TexTypesSet { get; set; }
  }
  
  public class DisplayType
  {
    private string _type;
    public string Type
    {
      get { return _type; }
      set { _type = value; }
    }
  }



  public class MtrColTables
  {
    public MtrColTables(TMCData data, TmcTables tables)
    {
      this.MtrCols = new ObservableCollection<MtrCol>();

      foreach (var grp in data.ColGrp)
      {
        byte count = this.ReculcMtrColUse(grp, tables);
        var mtrCol = new MtrCol(grp, count);
        this.MtrCols.Add(mtrCol);
      }
    }

    public class MtrCol : INotifyPropertyChanged
    {
      public MtrCol(MtrColorGroup grp, byte count)
      {
        this.Colors = new List<float[]>();

        if (grp == null) return;

        this.Index = grp.ID;
        this.OriginalIndex = grp.ID;
        this.Use = count;

        foreach (var col in grp.Color)
        {
          var color = new float[col.Length];
          Array.Copy(col, color, col.Length);
          this.Colors.Add(color);
        }
      }



      private int _index;
      public int Index
      {
        get { return _index; }
        set
        {
          _index = value;
          OnPropertyChanged("Index");
        }
      }
      public int OriginalIndex { get; set; }
      private int _count;
      public int Count
      {
        get { return _count; }
        set
        {
          _count = value;
          OnPropertyChanged("Count");
        }
      }
      private byte _use;
      public byte Use
      {
        get { return _use; }
        set
        {
          _use = value;
          OnPropertyChanged("Use");
        }
      }

      public List<float[]> Colors { get; set; }



      public event PropertyChangedEventHandler PropertyChanged;

      private void OnPropertyChanged(string name)
      {
        if (PropertyChanged != null)
        {
          PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
      }
    }

    public byte ReculcMtrColUse(MtrColorGroup col, TmcTables tables)
    {
      byte count = 0;
      foreach (var obj in tables.ObjData)
      {
        if (obj.Grp != -1)
        {
          if (obj.MtrCol == col.ID) count++;
        }
      }
      return count;
    }

    public void ResetMtrColIndex()
    {
      int count = 0;
      foreach (var mtrCol in this.MtrCols)
      {
        mtrCol.Index = count;
        count++;
      }
    }

    public ObservableCollection<MtrCol> MtrCols { get; set; }
  }



  public class CustompTables
  {
    public CustompTables(TMCData data, TmcTables tables)
    {
      this.Customps = new ObservableCollection<CustomParameters>();
      this.Params = new ObservableCollection<Parameters>();

      foreach (var matecp in data.MateCp)
      {
        byte count = this.ReculcCustompUse(matecp, tables);
        CustomParameters newCp = new CustomParameters(matecp, count);
        this.Customps.Add(newCp);
      }
    }



    public class CustomParameters : INotifyPropertyChanged
    {
      public CustomParameters(Customp customp, byte count)
      {
        this.cp = new List<CustompParam>();

        if (customp == null) return;

        this.Index = customp.Index;
        this.OriginalIndex = customp.Index;
        this.Use = count;

        foreach (var cp in customp.cp)
        {
          var newCp = new CustompParam();
          newCp.Index = cp.Index;
          newCp.Data1 = cp.Data1;
          newCp.Data2 = cp.Data2;
          newCp.Param = cp.Param;
          this.cp.Add(newCp);
        }
        this.Count = this.cp.Count;
      }



      private int _index;
      public int Index
      {
        get { return _index; }
        set
        {
          _index = value;
          OnPropertyChanged("Index");
        }
      }
      public int OriginalIndex { get; set; }
      private int _count;
      public int Count
      {
        get { return _count; }
        set
        {
          _count = value;
          OnPropertyChanged("Count");
        }
      }
      private byte _use;
      public byte Use
      {
        get { return _use; }
        set
        {
          _use = value;
          OnPropertyChanged("Use");
        }
      }
      public List<CustompParam> cp { get; private set; }



      public event PropertyChangedEventHandler PropertyChanged;

      private void OnPropertyChanged(string name)
      {
        if (PropertyChanged != null)
        {
          PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
      }
    }

    public class Parameters : INotifyPropertyChanged
    {
      public Parameters()
      {
        this.TypeVal = 0x854D87AE;
        this.Type = "0x" + this.TypeVal.ToString("X8") + " : Unknown";
        this.Data2 = 1;
        this.Param = 1;
      }

      public void Setparameters(CustompParam param)
      {
        this.Parent = param;
        string type = "Unknown";
        switch (param.Data1)
        {
          case 0x60121B1E:
            type = "Wet(Tex)";
            break;
          //case 0xD66548AD:
          //  type = "Wet(Col)";
          //  break;
          case 0xEB53FEEF:
            type = "Skin";
            break;
        }
        this.TypeVal = param.Data1;
        this.Type = "0x" + this.TypeVal.ToString("X8") + " : " + type;
        this.Data2 = param.Data2;
        this.Param = param.Param;
      }



      public CustompParam Parent;
      private string _type;
      public string Type
      {
        get { return _type; }
        set
        {
          _type = value;
          OnPropertyChanged("Type");
        }
      }
      public uint TypeVal;
      public int Data2;
      private int _param;
      public int Param
      {
        get { return _param; }
        set
        {
          _param = value;
          OnPropertyChanged("Param");
          if (this.Parent != null) this.Parent.Param = value;
        }
      }

      public event PropertyChangedEventHandler PropertyChanged;

      private void OnPropertyChanged(string name)
      {
        if (PropertyChanged != null)
        {
          PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
      }
    }

    public byte ReculcCustompUse(Customp cp, TmcTables tables)
    {
      byte count = 0;
      foreach (var obj in tables.ObjData)
      {
        if (obj.Grp != -1)
        {
          if (obj.Matecp == cp.Index) count++;
        }
      }
      return count;
    }

    public void SetParams(CustomParameters customp)
    {
      if (this.Params.Count != 0) this.Params.Clear();
      foreach (var param in customp.cp)
      {
        Parameters newParam = new Parameters();
        newParam.Setparameters(param);
        this.Params.Add(newParam);
      }
    }

    public void ResetCustompIndex()
    {
      int count = 0;
      foreach (var customp in this.Customps)
      {
        customp.Index = count;
        count++;
      }
    }

    public void ReflectChangeParams(int index)
    {
      this.Customps[index].cp.Clear();
      int count = 0;
      foreach (var param in this.Params)
      {
        var newCp = new CustompParam();
        newCp.Index = count;
        newCp.Data1 = param.TypeVal;
        newCp.Data2 = param.Data2;
        newCp.Param = param.Param;
        param.Parent = newCp;
        this.Customps[index].cp.Add(newCp);
        count++;
      }
      this.Customps[index].Count = this.Customps[index].cp.Count;
    }

    private void SortParamsByType()
    {
      this.Params = new ObservableCollection<CustompTables.Parameters>(this.Params.OrderBy(n => n.Type));
    }

    public ObservableCollection<CustomParameters> Customps { get; set; }
    public ObservableCollection<Parameters> Params { get; set; }
  }



  public class GettingMaterial
  {
    public GettingMaterial()
    {
      this.ColTarget = -1;
      this.MatTarget = -1;
    }

    public void SetColData(uint start, uint offset, byte[] bin, int target)
    {
      this.Col = new MtrColorGroup(start, offset, bin);

      if (target == -1)
        this.ColAdd = true;
      else
        this.ColTarget = target;
    }

    public void SetMateCpData(uint start, uint offset, byte[] bin, int target)
    {
      this.MateCp = new Customp(start, offset, bin);

      if (target == -1)
        this.MateCpAdd = true;
      else
        this.MateCpTarget = target;
    }

    public void SetMatData(uint start, uint offset, byte[] bin, int target)
    {
      this.Mat = new Material(start, offset, bin);

      if (target == -1)
        this.MatAdd = true;
      else
        this.MatTarget = target;
    }

    public void SetObjTexData(ObjectPart obj, byte[] bin)
    {
      this.TexParams = new byte[4];
      for (byte i = 0; i < 4; i++)
      {
        this.TexParams[i] = obj.TexParams[i];
      }

      this.Tex = new List<ObjectTextureData>();
      for (byte i = 0; i < obj.TexCount; i++)
      {
        var newTex = new ObjectTextureData();
        newTex.Offset = BitConverter.ToInt32(bin, (int)(obj.Start + 0x10 + (i * 4)));
        newTex.ID = BitConverter.ToInt32(bin, (int)(obj.Start + newTex.Offset));
        newTex.Type = BitConverter.ToInt32(bin, (int)(obj.Start + newTex.Offset + 4));
        newTex.Num = BitConverter.ToInt32(bin, (int)(obj.Start + newTex.Offset + 8));
        for (int l = 0; l < 25; l++)
        {
          newTex.Data[l] = BitConverter.ToUInt32(bin, (int)(obj.Start + newTex.Offset + 0x0C + (4 * l)));
        }
        this.Tex.Add(newTex);
      }
    }

    public bool ColAdd { get; set; }
    public bool MateCpAdd { get; set; }
    public bool MatAdd { get; set; }
    public int ColTarget { get; set; }
    public int MateCpTarget { get; set; }
    public int MatTarget { get; set; }
    public byte[] TexParams { get; set; }

    public MtrColorGroup Col { get; private set; }
    public Customp MateCp { get; private set; }
    public Material Mat { get; private set; }
    public List<ObjectTextureData> Tex { get; private set; }
  }

}
