using System;
using System.IO;
using System.Globalization;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Data;
using System.Data;
using System.Windows.Media;
using System.ComponentModel;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;
using Message;

namespace TMC_Tool
{
  /// <summary>
  /// MainWindow.xaml の相互作用ロジック
  /// </summary>
  public partial class MainWindow : Window
  {
    private static TMCData tmcData;

    private static TmcTables tmcTables;
    //private static TmcTables tmcCopyTables;

    private static byte[] bn;

    private static string tmcPath = "";

    ContextMenu menuDataGridObject = new ContextMenu();
    MenuItem menuItemObjGrpAdd = new MenuItem();
    MenuItem menuItemObjAdd = new MenuItem();
    MenuItem menuItemObjDel = new MenuItem();
    MenuItem menuItemDataDel = new MenuItem();
    MenuItem menuItemDataDelCancel = new MenuItem();
    MenuItem menuItemGetMaterial = new MenuItem();

    ContextMenu menuDataGridVtxIdxGrp = new ContextMenu();
    MenuItem menuItemVtxIdxGrpAdd = new MenuItem();
    MenuItem menuItemVtxIdxGrpDel = new MenuItem();
    MenuItem menuItemVtxIdxGrpDataDel = new MenuItem();
    MenuItem menuItemVtxIdxGrpChangeCancel = new MenuItem();

    private static bool objGrpRebuild = false;

    private static bool appStarted = false;
    private static bool updateChecking = true;
    private static bool modified = false;
    private static bool exitConfirm = true;
    private static Dictionary<string, string> txt = new Dictionary<string, string>();
    private static string langType = "Jpn";

    GetMaterialWindow getMatWindow;
    EditMtrColWindow editMtrColWindow;
    EditMatecpWindow editMatecpWindow;
    InputObjNameWindow inputObjNameWindow;



    public MainWindow()
    {
      InitializeComponent();

      MainWindowTitle();
      changeLanguage();
      MessageWindow.lang(txt);
      GetMaterialWindow.lang(txt);
      EditMtrColWindow.lang(txt);
      EditMatecpWindow.lang(txt);
      InputObjNameWindow.lang(txt);
      getMatWindow = new GetMaterialWindow();
      editMtrColWindow = new EditMtrColWindow();
      editMatecpWindow = new EditMatecpWindow();
      inputObjNameWindow = new InputObjNameWindow();

      DisplayType[] visibleTypes = dgObject.Resources["VisibleTypes"] as DisplayType[];
      visibleTypes[1].Type = txt["Visible"];
      visibleTypes[2].Type = txt["Hidden"];
      visibleTypes[3].Type = txt["Glasses"] + "1";
      visibleTypes[4].Type = txt["Glasses"] + "2";

      BuildContextMenu();

      Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
    }

    private void mainWindow_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
      if (e.LeftButton == MouseButtonState.Pressed) this.DragMove();
    }

    private void mainWindow_Loaded(object sender, RoutedEventArgs e)
    {
      List<string> cmds = System.Environment.GetCommandLineArgs().ToList();
      cmds.RemoveAt(0);
      openDorpFile(cmds);
    }

    private void mainWindow_DragDrop(object sender, DragEventArgs e)
    {
      List<string> filePaths = ((string[])e.Data.GetData(DataFormats.FileDrop, false)).ToList();
      openDorpFile(filePaths);
    }

    private void openDorpFile(List<string> filePaths)
    {
      if (filePaths.Count < 1) return;

      string tmcpath = "";
      List<string> lnkPaths = new List<string>();
      foreach (string path in filePaths)
      {
        if (tmcpath == "" && System.IO.Path.GetExtension(path).ToUpper() == ".TMC")
        {
          tmcpath = path;
        }
        else if (System.IO.Path.GetExtension(path).ToLower() == ".lnk")
        {
          lnkPaths.Add(path);
        }
      }

      if (lnkPaths.Count > 0)
      {
        Type t = Type.GetTypeFromCLSID(new Guid("72C24DD5-D70A-438B-8A42-98424B88AFB8"));
        dynamic shell = Activator.CreateInstance(t);
        List<string> shortcutPaths = new List<string>();
        foreach (string path in lnkPaths)
        {
          var shortcut = shell.CreateShortcut(path);
          shortcutPaths.Add(shortcut.TargetPath);
          System.Runtime.InteropServices.Marshal.FinalReleaseComObject(shortcut);
        }
        System.Runtime.InteropServices.Marshal.FinalReleaseComObject(shell);

        foreach (string path in shortcutPaths)
        {
          if (tmcpath == "" && System.IO.Path.GetExtension(path).ToUpper() == ".TMC")
          {
            tmcpath = path;
            break;
          }
        }
      }

      if (tmcpath != "")
      {
        OpenFile(tmcpath);
      }
    }

    private void OpenFromClipboard()
    {
      var pathText = Clipboard.GetText();
      if (Clipboard.ContainsFileDropList())
      {
        //データを取得する（取得できなかった時はnull）
        System.Collections.Specialized.StringCollection files = Clipboard.GetFileDropList();
        if (files != null) pathText = files[0];
      }
      if (!string.IsNullOrEmpty(pathText) && File.Exists(pathText) && System.IO.Path.GetExtension(pathText).ToUpper() == ".TMC")
      {
        var result = MessageWindow.Show(this, pathText + "\r\n\r\n" + txt["ConfirmOpenFile"], txt["Info"], txt["btnOpen"], txt["Cancel"]);
        if (result == "OK")
        {
          OpenFile(pathText);
        }
        Keyboard.Focus(mainWindow);
      }
    }

    private void openCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
      e.CanExecute = true;
    }
    private void openCommand_Executed(object sender, ExecutedRoutedEventArgs e)
    {
      Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
      dlg.Filter = "TMC Files (.TMC)|*.TMC";
      if (tmcPath != "")
      {
        dlg.InitialDirectory = Path.GetDirectoryName(tmcPath);
        dlg.FileName = Path.GetFileName(tmcPath);
      }
      if (dlg.ShowDialog() == true)
      {
        OpenFile(dlg.FileName);
      }
    }

    private void revertCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
      if (tmcData != null && tmcPath != "" && modified)
        e.CanExecute = true;
      else
        e.CanExecute = false;
    }
    private void revertCommand_Executed(object sender, ExecutedRoutedEventArgs e)
    {
      OpenFile(null);
    }

    private void saveCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
      if (tmcData != null && tmcPath != "" && modified)
        e.CanExecute = true;
      else
        e.CanExecute = false;
    }
    private void saveCommand_Executed(object sender, ExecutedRoutedEventArgs e)
    {
      string result = MessageWindow.Show(this, txt["Overwrite"], txt["Confirm"], txt["OverwriteYes"], txt["Cancel"]);
      if (result == "OK")
      {
        SaveFile(tmcPath);
      }
    }

    private void saveAsCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
      if (tmcData != null)
        e.CanExecute = true;
      else
        e.CanExecute = false;
    }
    private void saveAsCommand_Executed(object sender, ExecutedRoutedEventArgs e)
    {
      Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
      dlg.InitialDirectory = Path.GetDirectoryName(tmcPath);
      dlg.FileName = Path.GetFileName(tmcPath);
      //dlg.DefaultExt = ".TMC";
      dlg.Filter = "TMC Files (.TMC)|*.TMC";

      if (dlg.ShowDialog() == true)
      {
        SaveFile(dlg.FileName);
      }
    }

    private void btnEdtMtrCol_Click(object sender, RoutedEventArgs e)
    {
      int selIndex = -1;
      if (dgObject.SelectedItems.Count == 1)
      {
        var item = dgObject.SelectedItem as TmcTables.ObjectData;
        selIndex = (int)item.MtrCol;
      }

      int colCount = tmcData.ColGrp.Count;
      int[] replaceIndices = Enumerable.Repeat<int>(-1, colCount).ToArray();

      var mtrColTables = editMtrColWindow.Show(this, tmcData, tmcTables, selIndex);
      if (mtrColTables == null) return;

      // tmcData.ColGrpを追加
      foreach (var mtrCol in mtrColTables.MtrCols)
      {
        var newColGrp = new MtrColorGroup(0, 0, null);
        newColGrp.ID = mtrCol.Index;
        foreach (var color in mtrCol.Colors)
        {
          newColGrp.Color.Add(color);
        }

        if (mtrCol.OriginalIndex != -1)
        {
          newColGrp.Offset = tmcData.ColGrp[mtrCol.OriginalIndex].Offset;
          newColGrp.Start = tmcData.ColGrp[mtrCol.OriginalIndex].Start;
          replaceIndices[mtrCol.OriginalIndex] = mtrCol.Index;
        }
        tmcData.ColGrp.Add(newColGrp);
      }
      // 既存のColGrpを削除
      for (int i = 0; i < colCount; i++)
      {
        tmcData.ColGrp.RemoveAt(0);
      }

      // プルダウン再設定 増加時
      if (mtrColTables.MtrCols.Count > colCount)
      {
        int remaining = mtrColTables.MtrCols.Count - colCount;
        while (remaining > 0)
        {
          tmcTables.MtlCols.Add(tmcTables.MtlCols.Count);
          remaining--;
        }
      }

      // 各オブジェクトのMColを差し替え
      foreach (var obj in tmcTables.ObjData)
      {
        if (obj.Grp == -1) continue;

        if (obj.MtrCol == colCount)
        {
          obj.MtrCol = mtrColTables.MtrCols.Count;
        }
        else
        {
          obj.MtrCol = replaceIndices[(int)obj.MtrCol];
        }
      }

      // プルダウン再設定 減少時
      if (mtrColTables.MtrCols.Count < colCount)
      {
        int remaining = colCount - mtrColTables.MtrCols.Count;
        while (remaining > 0)
        {
          tmcTables.MtlCols.RemoveAt(tmcTables.MtlCols.Count - 1);
          remaining--;
        }
      }

      modified = true;
    }

    private void btnEdtMatecp_Click(object sender, RoutedEventArgs e)
    {
      //this.IsEnabled = false;
      int selIndex = -1;
      if (dgObject.SelectedItems.Count == 1)
      {
        var item = dgObject.SelectedItem as TmcTables.ObjectData;
        if (item.Matecp != null && item.Matecp != tmcData.MateCp.Count) selIndex = (int)item.Matecp;
      }

      int cpCount = tmcData.MateCp.Count;
      int[] replaceIndices = Enumerable.Repeat<int>(-1, cpCount).ToArray();

      var cpTables = editMatecpWindow.Show(this, tmcData, tmcTables, selIndex);
      if (cpTables == null) return;

      // tmcData.MateCpを追加
      foreach (var customp in cpTables.Customps)
      {
        var newMateCp = new Customp(0, 0, null);
        newMateCp.Index = customp.Index;
        foreach (var cp in customp.cp)
        {
          newMateCp.cp.Add(cp);
        }

        if (customp.OriginalIndex != -1)
        {
          newMateCp.Offset = tmcData.MateCp[customp.OriginalIndex].Offset;
          newMateCp.Start = tmcData.MateCp[customp.OriginalIndex].Start;
          newMateCp.Size = tmcData.MateCp[customp.OriginalIndex].Size;
          replaceIndices[customp.OriginalIndex] = customp.Index;
        }
        tmcData.MateCp.Add(newMateCp);
      }
      // 既存のmatecpを削除
      for (int i = 0; i < cpCount; i++)
      {
        tmcData.MateCp.RemoveAt(0);
      }

      // プルダウン再設定 増加時
      if (cpTables.Customps.Count > cpCount)
      {
        int remaining = cpTables.Customps.Count - cpCount;
        while (remaining > 0)
        {
          tmcTables.Matecps.Add(tmcTables.Matecps.Count);
          remaining--;
        }
      }

      // 各オブジェクトのmtcpを差し替え
      foreach (var obj in tmcTables.ObjData)
      {
        if (obj.Grp == -1) continue;

        if (obj.Matecp == cpCount)
        {
          obj.Matecp = cpTables.Customps.Count;
        }
        else
        {
          obj.Matecp = replaceIndices[(int)obj.Matecp];
        }
      }

      // プルダウン再設定 減少時
      if (cpTables.Customps.Count < cpCount)
      {
        int remaining = cpCount - cpTables.Customps.Count;
        while (remaining > 0)
        {
          tmcTables.Matecps.RemoveAt(tmcTables.Matecps.Count - 1);
          remaining--;
        }
      }

      modified = true;
    }

    private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
      ComboBox target = sender as ComboBox;

      if (!modified && (target.IsMouseCaptured || target.IsKeyboardFocused))
      {
        modified = true;
        textStatus.Path = tmcPath + "*";
        CommandManager.InvalidateRequerySuggested();
      }

      if (target.IsMouseCaptured || target.IsKeyboardFocused)
      {
        DataGrid dataGrid = Styles.FindVisualParent<DataGrid>(target);
        DataGridRow dataGridRow = Styles.FindVisualParent<DataGridRow>(target);
        dynamic curItem;
        dynamic data = tmcTables.ObjData;
        if (dataGrid.Name == "dgObject")
        {
          curItem = dataGridRow.Item as TmcTables.ObjectData;
        }
        else
        {
          data = tmcTables.VIData;
          curItem = dataGridRow.Item as TmcTables.VtxIdxGrpData;
        }

        bool selIndex = false;

        var b = BindingOperations.GetBinding(target, ComboBox.SelectedItemProperty) as Binding;
        if (b == null || b.Path == null)
        {
          b = BindingOperations.GetBinding(target, ComboBox.SelectedIndexProperty) as Binding;
          if (b == null || b.Path == null)
          {
            return;
          }
          selIndex = true;
        }
        var p = b.Path.Path;
        if (string.IsNullOrEmpty(p)) return;
        var i = p.LastIndexOf('.');

        if (i < 0)
        {
          if (dataGrid.SelectedItems.Count > 1)
          {
            Type type = target.DataContext.GetType();
            PropertyInfo pi = type.GetProperty(p);

            dynamic val = target.SelectedItem;
            if (selIndex)
            {
              val = target.SelectedIndex;
            }

            foreach (var selItem in dataGrid.SelectedItems)
            {
              dynamic item;
              if (dataGrid.Name == "dgObject")
              {
                item = selItem as TmcTables.ObjectData;
              }
              else
              {
                item = selItem as TmcTables.VtxIdxGrpData;
              }

              dynamic valDist = pi.GetValue(data[dataGrid.Items.IndexOf(item)]);
              if (valDist != null && !((p == "Toggle" || p == "Glasses" || p == "Transparent" || p == "CastShadow" || p == "ReceiveShadow") && valDist == -1))
              {
                if (p == "TexType")
                {
                  if (item.UVCount == curItem.UVCount)
                  {
                    pi.SetValue(data[dataGrid.Items.IndexOf(item)], val, null);
                    tmcTables.ChangeTexType(item, val, tmcData);
                  }
                }
                else if (p == "VtxGrp")
                {
                  if (item.VtxIdxGruops.IndexOf(val) != -1)
                  {
                    pi.SetValue(data[dataGrid.Items.IndexOf(item)], val, null);
                    tmcTables.RebuildTexData(tmcTables.VIData[item.VtxGrp], tmcData);
                    tmcTables.ReCalcGrpUseCount(item.Grp);
                    item.IsVgrpChanged = true;
                  }
                }
                else if (p == "UVCount" && item.IsEditable)
                {
                  string datasize = item.Datasize;
                  pi.SetValue(data[dataGrid.Items.IndexOf(item)], val, null);
                  tmcTables.RebuildTexData(item, tmcData);
                  tmcTables.ReCalcDataSize(item);
                  if (item.Datasize != datasize) item.IsChanged = true;
                }
                else if (dataGrid.Name != "dgVtxIdxGrp")
                {
                  pi.SetValue(data[dataGrid.Items.IndexOf(item)], val, null);
                }
              }
            }
          }
          else
          {
            if (p == "TexType")
            {
              tmcTables.ChangeTexType(curItem, (int)curItem.TexType, tmcData);
            }
            else if (p == "VtxGrp")
            {
              tmcTables.RebuildTexData(tmcTables.VIData[curItem.VtxGrp], tmcData);
              tmcTables.ReCalcGrpUseCount(curItem.Grp);
              curItem.IsVgrpChanged = true;
            }
            else if (p == "UVCount")
            {
              string datasize = curItem.Datasize;
              tmcTables.RebuildTexData(curItem, tmcData);
              tmcTables.ReCalcDataSize(curItem);
              if (curItem.Datasize != datasize) curItem.IsChanged = true;
            }
          }
        }
      }
    }

    private void CheckBox_Click(object sender, RoutedEventArgs e)
    {
      CheckBox target = sender as CheckBox;

      if (!modified)
      {
        modified = true;
        textStatus.Path = tmcPath + "*";
      }

      DataGrid dataGrid = Styles.FindVisualParent<DataGrid>(target);
      DataGridRow dataGridRow = Styles.FindVisualParent<DataGridRow>(target);
      dynamic data = tmcTables.ObjData;
      dynamic curItem = dataGridRow.Item as TmcTables.ObjectData;
      if (dataGrid.Name == "dgVtxIdxGrp")
      {
        data = tmcTables.VIData;
        curItem = dataGridRow.Item as TmcTables.VtxIdxGrpData;
      }
      var b = BindingOperations.GetBinding(target, CheckBox.IsCheckedProperty) as Binding;
      if (b == null || b.Path == null)
      {
        return;
      }
      var p = b.Path.Path;
      if (string.IsNullOrEmpty(p)) return;
      var i = p.LastIndexOf('.');
      if (i < 0)
      {
        if (dataGrid.SelectedItems.Count > 1)
        {
          Type type = target.DataContext.GetType();
          PropertyInfo pi = type.GetProperty(p);

          if (dataGrid.Name == "dgObject")
          {
            foreach (TmcTables.ObjectData item in dataGrid.SelectedItems)
            {
              dynamic valDist = pi.GetValue(data[dataGrid.Items.IndexOf(item)]);
              if (valDist != null && !(p == "EnvironmentMap" && !item.ChangeableEnvMap))
              {
                pi.SetValue(data[dataGrid.Items.IndexOf(item)], (bool)target.IsChecked, null);
              }
            }
          }
          else if (dataGrid.Name == "dgVtxIdxGrp")
          {
            foreach (TmcTables.VtxIdxGrpData item in dataGrid.SelectedItems)
            {
              dynamic valDist = pi.GetValue(data[dataGrid.Items.IndexOf(item)]);
              if (valDist != null && item.IsEditable)
              {
                pi.SetValue(data[dataGrid.Items.IndexOf(item)], (bool)target.IsChecked, null);
                SetVIDataChanged(p, item, (bool)target.IsChecked);
              }
            }
          }
        }
        else
        {
          if (dataGrid.Name == "dgVtxIdxGrp")
          {
            SetVIDataChanged(p, curItem, (bool)target.IsChecked);
          }
        }
      }
    }

    private void SetVIDataChanged(string p, TmcTables.VtxIdxGrpData item, bool isChecked)
    {
      if (p == "Normal" && isChecked)
      {
        item.NormalType = "Float3";
      }
      else if (p == "BWeights")
      {
        item.BIndices = item.BWeights;
        if (isChecked)
        {
          item.BWeightsType = "Dec4";
          item.BIndicesType = "Byte4";
        }
      }
      else if (p == "BIndices")
      {
        item.BWeights = item.BIndices;
        if (isChecked)
        {
          item.BWeightsType = "Dec4";
          item.BIndicesType = "Byte4";
        }
      }
      else if (p == "Tangent" && isChecked)
      {
        item.TangentType = "Float4";
      }
      tmcTables.ReCalcDataSize(item);
    }

    private void ComboBox_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
    {
      ComboBox target = sender as ComboBox;
      DataGrid dataGrid = Styles.FindVisualParent<DataGrid>(target);
      DataGridRow row = Styles.FindVisualParent<DataGridRow>(target);
      var item = row.Item;
      dataGrid.SelectedIndex = dataGrid.Items.IndexOf(item);
    }

    void dgObject_ContextMenuOpening(object sender, ContextMenuEventArgs e)
    {
      try
      {
        FrameworkElement fe = e.Source as FrameworkElement;

        DataGrid dataGrid = sender as DataGrid;
        if (dataGrid.SelectedItems.Count == 0)
        {
          e.Handled = true;
          return;
        }

        if (dataGrid.SelectedItems.Count == 1)
        {
          var item = dataGrid.SelectedItem as TmcTables.ObjectData;
          if (item.Grp == -1)
          {
            menuItemObjGrpAdd.Visibility = Visibility.Visible;
            menuItemObjAdd.Visibility = Visibility.Collapsed;
            menuItemObjDel.Visibility = Visibility.Collapsed;
            menuItemDataDel.Visibility = Visibility.Collapsed;
            menuItemDataDelCancel.Visibility = Visibility.Collapsed;
            menuItemGetMaterial.Visibility = Visibility.Collapsed;

            bool grpAddable = false;
            grpAddable = true;

            if (grpAddable)
              menuItemObjGrpAdd.IsEnabled = true;
            else
              menuItemObjGrpAdd.IsEnabled = false;

            return;
          }
        }

        bool selectObj = false;
        bool deletable = false;
        bool dataDeletable = false;
        bool dataDeleted = false;
        bool vgrpChanged = false;
        bool vtxIdxGrpChanged = false;
        foreach (TmcTables.ObjectData obj in dataGrid.SelectedItems)
        {
          if (obj.Grp != -1)
          {
            selectObj = true;

            if (tmcTables.ObjCounts[obj.Grp] > 1)
            {
              deletable = true;
            }

            if (tmcTables.VIData[(int)obj.VtxGrp].IsChanged)
            {
              vtxIdxGrpChanged = true;
            }
            else if (obj.IsVgrpChanged)
            {
              vgrpChanged = true;
            }
            else if (obj.IsDeleted)
            {
              dataDeleted = true;
            }
            else if (!obj.IsDeleted && !(obj.VtxCount == 0 && obj.IdxCount == 0 && !obj.IsDeleted))
            {
              dataDeletable = true;
            }
          }
        }

        if (!selectObj)
        {
          e.Handled = true;
          return;
        }

        menuItemObjGrpAdd.Visibility = Visibility.Collapsed;
        menuItemObjAdd.Visibility = Visibility.Visible;
        menuItemObjDel.Visibility = Visibility.Visible;
        menuItemDataDel.Visibility = Visibility.Visible;
        menuItemDataDelCancel.Visibility = Visibility.Visible;
        menuItemGetMaterial.Visibility = Visibility.Visible;

        //menuItemObjAdd.IsEnabled = false;
        menuItemObjDel.IsEnabled = false;
        menuItemDataDel.IsEnabled = false;
        menuItemDataDelCancel.IsEnabled = false;
        //menuItemGetMaterial.IsEnabled = false;

        if (deletable) menuItemObjDel.IsEnabled = true;

        if (vtxIdxGrpChanged) return;

        if (dataDeleted && !vgrpChanged) menuItemDataDelCancel.IsEnabled = true;

        if (dataDeletable) menuItemDataDel.IsEnabled = true;

      }
      catch (Exception ex)
      {
        MessageWindow.Show(this, ex.Message + "\r\n\r\n" + ex.StackTrace, txt["Error"]);
      }
    }

    private void menuItemObjGrpAdd_Click(object sender, RoutedEventArgs e)
    {
      try
      {
        if (dgObject.SelectedItems.Count > 1) return;

        TmcTables.ObjectData grp = dgObject.SelectedItem as TmcTables.ObjectData;
        if (grp.Grp != -1) return;
        int grpID = grp.ID;

        List<TmcTables.ObjectData> newObjects = new List<TmcTables.ObjectData>();


        // オブジェクトグループ名を入力して設定
        var newNodeName = "";
        newNodeName = inputObjNameWindow.Show(this, tmcData, tmcTables, grp.Name);
        if (newNodeName == null) return;

        // ノードを追加
        var originalNode = tmcData.Node[tmcTables.Nodes[grp.Node].OriginalIndex];
        var newNode = new TmcTables.NodeData(originalNode);
        newNode.Name = newNodeName;
        newNode.IsAdded = true;
        tmcTables.Nodes.Add(newNode);
        tmcTables.Nodes = tmcTables.Nodes.OrderBy(node => node.Name, StringComparer.OrdinalIgnoreCase).ToList();
        int parent = Array.FindIndex(tmcTables.Nodes.ToArray(), elem => elem.Name == tmcData.Node[tmcData.Hie[originalNode.Index].Parent].Name);
        tmcTables.Nodes[parent].Children.Add(newNodeName);

        // オブジェクトグループの挿入位置を割り出す
        var grpNames = tmcTables.ObjData.Where(obj => obj.Grp == -1).Select(obj => obj.Name).ToList();
        grpNames.Add(newNodeName);
        grpNames.Sort(StringComparer.OrdinalIgnoreCase);

        int destIdx = -1;
        int grpIdx = grpNames.IndexOf(newNodeName);
        if (grpIdx < grpNames.Count - 1)
        {
          destIdx = Array.FindIndex(tmcTables.ObjData.ToArray(), obj => obj.Name == grpNames[grpIdx + 1]);
        }
        else
        {
          destIdx = tmcTables.ObjData.Count;
        }

        // グループ内のオブジェクトをリスト化
        var originalObjects = tmcTables.ObjData.Where(obj => obj.Grp == grpID).ToList();

        // オブジェクトグループ作成と挿入
        var newGrp = tmcTables.AddObjectData(grp, destIdx);
        newGrp.Name = newNodeName;
        if (newGrp.Toggle == -1)
          newGrp.Toggle = 0;
        else
          newGrp.Toggle = grp.Toggle;

        // オブジェクト作成と挿入
        int count = 1;
        foreach (var obj in originalObjects)
        {
          var newObj = tmcTables.AddObjectData(obj, destIdx + count);
          newObj.Grp = newGrp.ID;
          newObjects.Add(newObj);
          count++;
        }

        // インデックスを再設定
        tmcTables.ResetObjectIndex();

        // ノードのObjIndexを再設定
        tmcTables.ResetNodesObjIndex();

        // 頂点グループを追加
        var originalVtxGrps = tmcTables.VIData.Where(vi => vi.ObjID == grpID).ToList();
        int destIdxVtxGrp = -1;
        if (grpIdx < grpNames.Count - 1)
        {
          destIdxVtxGrp = Array.FindIndex(tmcTables.VIData.ToArray(), elem => elem.ObjName == grpNames[grpIdx + 1]);
        }
        else
        {
          destIdxVtxGrp = tmcTables.VIData.Count;
        }

        var convertVIGrp = new Dictionary<int, int>();
        count = 0;
        foreach (var item in originalVtxGrps)
        {
          tmcTables.InsertVIData(item, destIdxVtxGrp + count, grpIdx);
          tmcTables.VIData[destIdxVtxGrp + count].ObjName = grpNames[grpIdx];
          count++;
        }

        count = 0;
        foreach (var item in originalVtxGrps)
        {
          convertVIGrp[item.Index] = destIdxVtxGrp + count;
          count++;
        }

        // 頂点グループのObjIDを再設定
        for (int i = 0; i < tmcTables.VIData.Count; i++)
        {
          tmcTables.VIData[i].ObjID = grpNames.IndexOf(tmcTables.VIData[i].ObjName);
        }

        // オブジェクトのVtxIdxGruopsを再設定
        tmcTables.ResetVtxIdxGruops();

        // 頂点グループ・Idxグループを再設定
        foreach (var obj in newObjects)
        {
          obj.VtxGrp = (byte)convertVIGrp[(int)obj.VtxGrp];
          tmcTables.VIData[(int)obj.VtxGrp].Use++;
        }

        // 頂点グループのIdxGrpを再設定
        tmcTables.ResetVIDataIdxGrp();

        // tmcTables.ObjCountsを再設定
        foreach (var obj in tmcTables.ObjData)
        {
          if (obj.Grp == -1)
          {
            tmcTables.ObjCounts[obj.ID] = 0;
          }
          else
          {
            tmcTables.ObjCounts[obj.Grp]++;
          }
        }

        if (!modified)
        {
          modified = true;
          textStatus.Path = tmcPath + "*";
        }
        objGrpRebuild = true;

        // 複製したオブジェクトグループを選択
        dgObject.SelectedIndex = -1;
        dgObject.SelectedItem = newGrp;
        dgObject.ScrollIntoView(dgObject.SelectedItem);
      }
      catch (Exception ex)
      {
        MessageWindow.Show(this, ex.Message + "\r\n\r\n" + ex.StackTrace, txt["Error"]);
      }
    }

    private void menuItemObjAdd_Click(object sender, RoutedEventArgs e)
    {
      try
      {
        List<TmcTables.ObjectData> newObjects = new List<TmcTables.ObjectData>();

        foreach (var item in dgObject.SelectedItems)
        {
          TmcTables.ObjectData obj = item as TmcTables.ObjectData;
          if (obj.Grp == -1) continue;

          var newObj = tmcTables.AddObjectData(obj);
          newObjects.Add(newObj);

          if (!modified)
          {
            modified = true;
            textStatus.Path = tmcPath + "*";
          }
        }

        dgObject.SelectedIndex = -1;
        foreach (var obj in newObjects)
        {
          dgObject.SelectedItems.Add(obj);
        }

        tmcTables.ResetObjectIndex();
      }
      catch (Exception ex)
      {
        MessageWindow.Show(this, ex.Message + "\r\n\r\n" + ex.StackTrace, txt["Error"]);
      }
    }

    private void menuItemObjDel_Click(object sender, RoutedEventArgs e)
    {
      try
      {
        var result = MessageWindow.Show(this, txt["ConfirmObjDelete"], txt["Confirm"], txt["OK"], txt["Cancel"]);
        if (result == "OK")
        {
          while (dgObject.SelectedItems.Count > 0)
          {
            TmcTables.ObjectData obj = dgObject.SelectedItem as TmcTables.ObjectData;
            if (obj.Grp == -1 || tmcTables.ObjCounts[obj.Grp] <= 1)
            {
              dgObject.SelectedItems.Remove(dgObject.SelectedItem);
              continue;
            }

            tmcTables.ObjCounts[obj.Grp]--;
            tmcTables.ObjData.Remove(obj);

            if (!modified)
            {
              modified = true;
              textStatus.Path = tmcPath + "*";
            }
          }

          tmcTables.ResetObjectIndex();
        }
      }
      catch (Exception ex)
      {
        MessageWindow.Show(this, ex.Message + "\r\n\r\n" + ex.StackTrace, txt["Error"]);
      }
    }

    private void menuItemDataDel_Click(object sender, RoutedEventArgs e)
    {
      var result = MessageWindow.Show(this, txt["ConfirmAllDelete"], txt["Confirm"], txt["OK"], txt["Cancel"]);
      if (result == "OK")
      {
        foreach (var item in dgObject.SelectedItems)
        {
          TmcTables.ObjectData obj = item as TmcTables.ObjectData;
          if (obj.Grp == -1 || (obj.VtxCount == 0 && obj.IdxCount == 0)) continue;

          obj.IsDeleted = true;
          obj.IsEditable = true;
          obj.VtxCount = 0;
          obj.IdxCount = 0;

          if (!modified)
          {
            modified = true;
            textStatus.Path = tmcPath + "*";
          }
        }
        tmcTables.ReCalcGrpVyxIdxCount();
      }
    }

    private void menuItemDataDelCancel_Click(object sender, RoutedEventArgs e)
    {
      foreach (var item in dgObject.SelectedItems)
      {
        TmcTables.ObjectData obj = item as TmcTables.ObjectData;
        if (obj.Grp == -1) continue;

        obj.IsDeleted = false;
        obj.IsEditable = false;
        obj.VtxCount = tmcData.ObjGrp[obj.Grp].Obj[obj.ID].VtxCount;
        obj.IdxCount = tmcData.ObjGrp[obj.Grp].Obj[obj.ID].IdxCount;
      }
      tmcTables.ReCalcGrpVyxIdxCount();
    }

    private void menuItemGetMaterial_Click(object sender, RoutedEventArgs e)
    {
      GettingMaterial gettingMat = getMaterial(null, -1);
      if (gettingMat == null) return;

      if (gettingMat.Col != null)
      {
        bool result = checkGettingCol(gettingMat);
        if (result)
        {
          setGettingCol(gettingMat);
          if (gettingMat.ColAdd)
          {
            foreach (var item in dgObject.SelectedItems)
            {
              TmcTables.ObjectData obj = item as TmcTables.ObjectData;
              if (obj.Grp == -1) continue;
              obj.MtrCol = gettingMat.Col.ID;
            }
          }
        }
      }
      if (gettingMat.MateCp != null)
      {
        bool result = checkGettingMateCp(gettingMat);
        if (result)
        {
          setGettingMateCp(gettingMat);
          if (gettingMat.MateCpAdd)
          {
            foreach (var item in dgObject.SelectedItems)
            {
              TmcTables.ObjectData obj = item as TmcTables.ObjectData;
              if (obj.Grp == -1) continue;
              obj.Matecp = gettingMat.MateCp.Index;
            }
          }
        }
      }
      if (gettingMat.Mat != null)
      {
        bool result = checkGettingMat(gettingMat);
        if (result)
        {
          setGettingMat(gettingMat);
          if (gettingMat.MatAdd)
          {
            foreach (var item in dgObject.SelectedItems)
            {
              TmcTables.ObjectData obj = item as TmcTables.ObjectData;
              if (obj.Grp == -1) continue;
              obj.Mcamtrl = gettingMat.Mat.ID;
            }
          }
        }
      }
      if (gettingMat.TexParams != null)
      {
        foreach (var item in dgObject.SelectedItems)
        {
          TmcTables.ObjectData obj = item as TmcTables.ObjectData;
          if (obj.Grp == -1) continue;
          ObjectPart objPart = tmcData.ObjGrp[obj.Grp].Obj[obj.ID];
          string checkTexType = "";
          string curTexType = "";
          if (obj.TexTypes.Count != 0) curTexType = obj.TexTypes[(int)obj.TexType].Replace(" ", "");
          if (curTexType == "Error") curTexType = "0123";
          for (byte i = 0; i < 4; i++)
          {
            objPart.TexParams[i] = gettingMat.TexParams[i];
          }
          for (byte i = 0; i < gettingMat.Tex.Count; i++)
          {
            if (objPart.Tex.Count <= i)
            {
              objPart.Tex.Add(gettingMat.Tex[i]);
            }
            else
            {
              objPart.Tex[i].Type = gettingMat.Tex[i].Type;
              for (int j = 0; j < 25; j++)
              {
                objPart.Tex[i].Data[j] = gettingMat.Tex[i].Data[j];
              }
            }
            obj.RecentTexTypes[i] = gettingMat.Tex[i].Type.ToString()[0];
            if (gettingMat.Tex.Count >= obj.TexCount) checkTexType += gettingMat.Tex[i].Type;
          }
          if (checkTexType.Length > obj.TexCount)
          {
            checkTexType = checkTexType.Substring(0, (int)obj.TexCount);
          }
          else
          {
            for (int i = checkTexType.Length; i < obj.TexCount; i++)
            {
              checkTexType += curTexType[i];
            }
          }

          obj.SetTexType(obj.UVCount, checkTexType);
        }
        modified = true;
      }
    }

    public GettingMaterial getMaterial(string type, int index)
    {
      List<TmcTables.ObjectData> objLists = new List<TmcTables.ObjectData>();

      foreach (var item in dgObject.SelectedItems)
      {
        TmcTables.ObjectData obj = item as TmcTables.ObjectData;
        if (obj.Grp == -1) continue;

        objLists.Add(obj);
      }

      var gettingMat = getMatWindow.Show(this, tmcTables, objLists, type, index);
      return gettingMat;
    }

    public bool checkGettingCol(GettingMaterial gettingMat)
    {
      // 既に同じものがあるかチェック（置換の場合は置換先は除く）
      List<int> equalCol = new List<int>();
      foreach (var col in tmcData.ColGrp)
      {
        if (!gettingMat.ColAdd && col.ID == gettingMat.ColTarget) continue;
        for (int i = 0; i < col.Color.Count; i++)
        {
          for (int j = 0; j < col.Color[i].Length; j++)
          {
            if (col.Color[i][j] != gettingMat.Col.Color[i][j])
            {
              goto SkipAdd;
            }
          }
        }
        equalCol.Add(col.ID);

      SkipAdd:;
      }

      // 同じものがあれば追加するか確認
      if (equalCol.Count > 0)
      {
        string str = txt["ConfirmReplace"];
        if (gettingMat.ColAdd) str = txt["ConfirmAdd"];

        string equalColText = "   MtrCol :";
        foreach (var col in equalCol)
        {
          equalColText += " " + col;
        }
        string result = MessageWindow.Show(this, txt["SameMtrCol"] + equalColText + "\r\n" + str, txt["Confirm"], txt["Yes"], txt["No"], txt["Auto"]);
        if (result == "Cancel")
        {
          return false;
        }
        else if (result == "Other")
        {
          foreach (var item in dgObject.SelectedItems)
          {
            TmcTables.ObjectData obj = item as TmcTables.ObjectData;
            if (obj.Grp == -1 || equalCol.Contains((int)obj.MtrCol)) continue;
            obj.MtrCol = equalCol[0];
          }
          modified = true;
          return false;
        }
      }
      return true;
    }

    public void setGettingCol(GettingMaterial gettingMat)
    {
      gettingMat.Col.Offset = 0;
      gettingMat.Col.Start = 0;
      if (gettingMat.ColAdd)
      {
        // tmcDataに追加
        gettingMat.Col.ID = tmcData.ColGrp.Count;
        tmcTables.MtlCols.Add(gettingMat.Col.ID);
        tmcData.ColGrp.Add(gettingMat.Col);
      }
      else
      {
        // tmcDataのデータを置換
        gettingMat.Col.ID = gettingMat.ColTarget;
        tmcData.ColGrp.RemoveAt(gettingMat.ColTarget);
        tmcData.ColGrp.Insert(gettingMat.ColTarget, gettingMat.Col);
      }
      modified = true;
    }

    public bool checkGettingMateCp(GettingMaterial gettingMat)
    {
      // 既に同じものがあるかチェック（置換の場合は置換先は除く）
      List<int> equalMateCp = new List<int>();
      foreach (var matecp in tmcData.MateCp)
      {
        if (!gettingMat.MateCpAdd && matecp.Index == gettingMat.MateCpTarget) continue;
        if (matecp.cp.Count != gettingMat.MateCp.cp.Count) continue;
        for (int i = 0; i < matecp.cp.Count; i++)
        {
          if (
            matecp.cp[i].Data1 != gettingMat.MateCp.cp[i].Data1 ||
            matecp.cp[i].Data2 != gettingMat.MateCp.cp[i].Data2 ||
            matecp.cp[i].Param != gettingMat.MateCp.cp[i].Param
            )
          {
            goto SkipAdd;
          }
        }
        equalMateCp.Add(matecp.Index);

      SkipAdd:;
      }

      // 同じものがあれば追加するか確認
      if (equalMateCp.Count > 0)
      {
        string str = txt["ConfirmReplace"];
        if (gettingMat.MateCpAdd) str = txt["ConfirmAdd"];

        string equalMateCpText = "   MateCp :";
        foreach (var mat in equalMateCp)
        {
          equalMateCpText += " " + mat;
        }
        string result = MessageWindow.Show(this, txt["SameMateCp"] + equalMateCpText + "\r\n" + str, txt["Confirm"], txt["Yes"], txt["No"], txt["Auto"]);
        if (result == "Cancel")
        {
          return false;
        }
        else if (result == "Other")
        {
          foreach (var item in dgObject.SelectedItems)
          {
            TmcTables.ObjectData obj = item as TmcTables.ObjectData;
            if (obj.Grp == -1 || equalMateCp.Contains((int)obj.Matecp)) continue;
            obj.Matecp = equalMateCp[0];
          }
          modified = true;
          return false;
        }
      }
      return true;
    }

    public void setGettingMateCp(GettingMaterial gettingMat)
    {
      gettingMat.MateCp.Offset = 0;
      gettingMat.MateCp.Start = 0;
      if (gettingMat.MateCpAdd)
      {
        foreach (var obj in tmcTables.ObjData)
        {
          if (obj.Matecp == tmcData.MateCp.Count)
          {
            obj.Matecp++;
          }
        }
        // tmcDataに追加
        gettingMat.MateCp.Index = tmcData.MateCp.Count;
        tmcTables.Matecps.Add(gettingMat.MateCp.Index + 1);
        tmcData.MateCp.Add(gettingMat.MateCp);
      }
      else
      {
        // tmcDataのデータを置換
        gettingMat.MateCp.Index = gettingMat.MateCpTarget;
        tmcData.MateCp.RemoveAt(gettingMat.MateCpTarget);
        tmcData.MateCp.Insert(gettingMat.MateCpTarget, gettingMat.MateCp);
      }
      modified = true;
    }

    public bool checkGettingMat(GettingMaterial gettingMat)
    {
      // 既に同じものがあるかチェック（置換の場合は置換先は除く）
      List<int> equalMat = new List<int>();
      foreach (var mat in tmcData.Mat)
      {
        if (!gettingMat.MatAdd && mat.ID == gettingMat.MatTarget) continue;

        if (mat.Param.Count != gettingMat.Mat.Param.Count) continue;

        for (int i = 0; i < mat.Param.Count; i++)
        {
          if (mat.Param[i] == null && gettingMat.Mat.Param[i] == null) continue;

          if (
            mat.Param[i] == null ||
            gettingMat.Mat.Param[i] == null ||
            mat.Param[i].ID != gettingMat.Mat.Param[i].ID ||
            mat.Param[i].Count != gettingMat.Mat.Param[i].Count ||
            mat.Param[i].Data1 != gettingMat.Mat.Param[i].Data1 ||
            mat.Param[i].Data2 != gettingMat.Mat.Param[i].Data2
          )
          {
            goto SkipAdd;
          }

          if (mat.Param[i].Data3 == null && gettingMat.Mat.Param[i].Data3 == null) continue;
          if (mat.Param[i].Data3 != null && gettingMat.Mat.Param[i].Data3 != null)
          {
            for (int j = 0; j < mat.Param[i].Data3.Length; j++)
            {
              if (mat.Param[i].Data3[j] != gettingMat.Mat.Param[i].Data3[j])
              {
                goto SkipAdd;
              }
            }
          }

          if (mat.Param[i].Params == null && gettingMat.Mat.Param[i].Params == null) continue;
          if (mat.Param[i].Params != null && gettingMat.Mat.Param[i].Params != null)
          {
            for (int j = 0; j < mat.Param[i].Params.Count; j++)
            {
              for (int k = 0; k < mat.Param[i].Params[j].Length; k++)
              {
                if (mat.Param[i].Params[j][k] != gettingMat.Mat.Param[i].Params[j][k])
                {
                  goto SkipAdd;
                }
              }
            }
          }
        }
        equalMat.Add(mat.ID);

      SkipAdd:;
      }

      // 同じものがあれば追加するか確認
      if (equalMat.Count > 0)
      {
        string str = txt["ConfirmReplace"];
        if (gettingMat.MatAdd) str = txt["ConfirmAdd"];

        string equalMatText = "   MCAMTRL :";
        foreach (var mat in equalMat)
        {
          equalMatText += " " + mat;
        }
        string result = MessageWindow.Show(this, txt["SameMCAMTRL"] + equalMatText + "\r\n" + str, txt["Confirm"], txt["Yes"], txt["No"], txt["Auto"]);
        if (result == "Cancel")
        {
          return false;
        }
        else if (result == "Other")
        {
          foreach (var item in dgObject.SelectedItems)
          {
            TmcTables.ObjectData obj = item as TmcTables.ObjectData;
            if (obj.Grp == -1 || equalMat.Contains((int)obj.Mcamtrl)) continue;
            obj.Mcamtrl = equalMat[0];
          }
          modified = true;
          return false;
        }
      }
      return true;
    }

    public void setGettingMat(GettingMaterial gettingMat)
    {
      gettingMat.Mat.Offset = 0;
      gettingMat.Mat.Start = 0;
      if (gettingMat.MatAdd)
      {
        // tmcDataに追加
        gettingMat.Mat.ID = tmcData.Mat.Count;
        tmcTables.Mcamtrls.Add(gettingMat.Mat.ID);
        tmcData.Mat.Add(gettingMat.Mat);
        Console.WriteLine("Add Mat");
      }
      else
      {
        // tmcDataのデータを置換
        gettingMat.Mat.ID = gettingMat.MatTarget;
        tmcData.Mat.RemoveAt(gettingMat.MatTarget);
        tmcData.Mat.Insert(gettingMat.MatTarget, gettingMat.Mat);
        Console.WriteLine("Replace Mat");
      }
      modified = true;
    }

    private void dgVtxIdxGrp_ContextMenuOpening(object sender, ContextMenuEventArgs e)
    {
      FrameworkElement fe = e.Source as FrameworkElement;

      DataGrid dataGrid = sender as DataGrid;
      if (dataGrid.SelectedItems.Count == 0)
      {
        e.Handled = true;
        return;
      }

      menuItemVtxIdxGrpAdd.IsEnabled = false;
      menuItemVtxIdxGrpDel.IsEnabled = false;
      menuItemVtxIdxGrpDataDel.IsEnabled = false;
      menuItemVtxIdxGrpChangeCancel.IsEnabled = false;


      bool deletable = false;
      bool deletableData = false;
      bool changed = false;
      foreach (TmcTables.VtxIdxGrpData item in dataGrid.SelectedItems)
      {
        if (item.Use == 0) deletable = true;

        foreach (var obj in tmcTables.ObjData)
        {
          if (obj.VtxGrp == item.Index && (obj.VtxCount != 0 || obj.IdxCount != 0))
          {
            deletableData = true;
            break;
          }
        }

        if (item.IsChanged) changed = true;
      }

      if (dataGrid.SelectedItems.Count == 1)
      {
        TmcTables.VtxIdxGrpData item = dataGrid.SelectedItem as TmcTables.VtxIdxGrpData;
        if (item.ObjID != -1) menuItemVtxIdxGrpAdd.IsEnabled = true;
      }

      if (deletable) menuItemVtxIdxGrpDel.IsEnabled = true;

      if (deletableData) menuItemVtxIdxGrpDataDel.IsEnabled = true;

      if (changed) menuItemVtxIdxGrpChangeCancel.IsEnabled = true;
    }

    private void menuItemVtxIdxGrpAdd_Click(object sender, RoutedEventArgs e)
    {
      try
      {
        var result = MessageWindow.Show(this, txt["ConfirmAddVtxGrp"], txt["Confirm"], txt["OK"], txt["Cancel"]);
        if (result == "OK")
        {
          TmcTables.VtxIdxGrpData item = dgVtxIdxGrp.SelectedItem as TmcTables.VtxIdxGrpData;

          int addIndex = tmcTables.InsertVIData(item);
          tmcTables.ResetVtxIdxGruops();
          tmcTables.ResetVIDataIdxGrp();
          dgVtxIdxGrp.SelectedIndex = addIndex;
          dgVtxIdxGrp.ScrollIntoView(dgVtxIdxGrp.Items[addIndex]);

          modified = true;
        }
      }
      catch (Exception ex)
      {
        MessageWindow.Show(this, ex.Message + "\r\n\r\n" + ex.StackTrace, txt["Error"]);
      }
    }

    private void menuItemVtxIdxGrpDel_Click(object sender, RoutedEventArgs e)
    {
      var result = MessageWindow.Show(this, txt["ConfirmDelVtxGrp"], txt["Confirm"], txt["OK"], txt["Cancel"]);
      if (result == "OK")
      {
        List<int> selectedIndices = new List<int>();
        foreach (var item in dgVtxIdxGrp.SelectedItems)
        {
          TmcTables.VtxIdxGrpData data = item as TmcTables.VtxIdxGrpData;
          if (data.Use == 0) selectedIndices.Add(data.Index);
        }

        byte newVtxGrp = 0;
        byte newIdxGrp = 0;
        for (int i = 0; i < tmcTables.VIData.Count; i++)
        {
          if (selectedIndices.Contains(i))
          {
            continue;
          }
          foreach (var obj in tmcTables.ObjData)
          {
            // ObjDataのVtxGrpを再設定
            if (obj.Grp != -1 && obj.VtxGrp == tmcTables.VIData[i].Index) obj.VtxGrp = newVtxGrp;

            // ObjDataのフラグ再設定
            if (obj.Grp != -1 && obj.VtxGrp == tmcTables.VIData[i].Index) obj.VtxGrp = newVtxGrp;
          }
          // VIDataのIndexを再設定
          tmcTables.VIData[i].Index = newVtxGrp;
          newVtxGrp++;

          if (tmcTables.VIData[i].ObjID != -1)
          {
            // VIDataのIdxGrpを再設定
            tmcTables.VIData[i].IdxGrp = newIdxGrp;
            newIdxGrp++;
          }
        }

        selectedIndices.Sort();
        selectedIndices.Reverse();
        foreach (var idx in selectedIndices)
        {
          tmcTables.VIData.RemoveAt(idx);
        }

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

    private void menuItemVtxIdxGrpDataDel_Click(object sender, RoutedEventArgs e)
    {
      var result = MessageWindow.Show(this, txt["ConfirmAllDataDelete"], txt["Confirm"], txt["OK"], txt["Cancel"]);
      if (result == "OK")
      {
        foreach (var item in dgVtxIdxGrp.SelectedItems)
        {
          TmcTables.VtxIdxGrpData data = item as TmcTables.VtxIdxGrpData;
          foreach (var obj in tmcTables.ObjData)
          {
            if (obj.VtxGrp == data.Index && (obj.VtxCount != 0 || obj.IdxCount != 0))
            {
              obj.IsDeleted = true;
              obj.IsEditable = true;
              obj.VtxCount = 0;
              obj.IdxCount = 0;
            }
          }
        }
        if (!modified)
        {
          modified = true;
          textStatus.Path = tmcPath + "*";
        }
        tmcTables.ReCalcGrpVyxIdxCount();
      }
    }

    private void menuItemVtxIdxGrpChangeCancel_Click(object sender, RoutedEventArgs e)
    {
      foreach (var item in dgVtxIdxGrp.SelectedItems)
      {
        TmcTables.VtxIdxGrpData data = item as TmcTables.VtxIdxGrpData;

        data.UVCount = (byte)tmcData.VtxGrp[data.OriginalIndex].UVCount;
        data.IsChanged = false;
      }
    }

    private void BuildContextMenu()
    {
      menuItemObjGrpAdd.Header = txt["AddObjGrp"];
      menuItemObjAdd.Header = txt["AddObj"];
      menuItemObjDel.Header = txt["DeleteObj"];
      menuItemDataDel.Header = txt["DeleteAllVtxIdx"];
      menuItemDataDelCancel.Header = txt["DeleteAllVtxIdxCancel"];
      menuItemGetMaterial.Header = txt["GetMaterial"];

      menuDataGridObject.Items.Add(menuItemObjGrpAdd);
      menuDataGridObject.Items.Add(menuItemObjAdd);
      menuDataGridObject.Items.Add(menuItemObjDel);
      menuDataGridObject.Items.Add(menuItemDataDel);
      menuDataGridObject.Items.Add(menuItemDataDelCancel);
      menuDataGridObject.Items.Add(menuItemGetMaterial);

      menuItemObjGrpAdd.Click += new RoutedEventHandler(menuItemObjGrpAdd_Click);
      menuItemObjAdd.Click += new RoutedEventHandler(menuItemObjAdd_Click);
      menuItemObjDel.Click += new RoutedEventHandler(menuItemObjDel_Click);
      menuItemDataDel.Click += new RoutedEventHandler(menuItemDataDel_Click);
      menuItemDataDelCancel.Click += new RoutedEventHandler(menuItemDataDelCancel_Click);
      menuItemGetMaterial.Click += new RoutedEventHandler(menuItemGetMaterial_Click);

      dgObject.ContextMenu = menuDataGridObject;

      menuItemVtxIdxGrpAdd.Header = txt["VtxIdxGrpAdd"];
      menuItemVtxIdxGrpDel.Header = txt["VtxIdxGrpDel"];
      menuItemVtxIdxGrpDataDel.Header = txt["DeleteAllVtxIdx"];
      menuItemVtxIdxGrpChangeCancel.Header = txt["VtxIdxGrpChangeCancel"];

      menuDataGridVtxIdxGrp.Items.Add(menuItemVtxIdxGrpAdd);
      menuDataGridVtxIdxGrp.Items.Add(menuItemVtxIdxGrpDel);
      menuDataGridVtxIdxGrp.Items.Add(menuItemVtxIdxGrpDataDel);
      menuDataGridVtxIdxGrp.Items.Add(menuItemVtxIdxGrpChangeCancel);

      menuItemVtxIdxGrpAdd.Click += new RoutedEventHandler(menuItemVtxIdxGrpAdd_Click);
      menuItemVtxIdxGrpDel.Click += new RoutedEventHandler(menuItemVtxIdxGrpDel_Click);
      menuItemVtxIdxGrpDataDel.Click += new RoutedEventHandler(menuItemVtxIdxGrpDataDel_Click);
      menuItemVtxIdxGrpChangeCancel.Click += new RoutedEventHandler(menuItemVtxIdxGrpChangeCancel_Click);

      dgVtxIdxGrp.ContextMenu = menuDataGridVtxIdxGrp;
    }



    public static TMCData ParseTmc(byte[] bin)
    {
      TMCData data = new TMCData(bin);

      if (data == null) return null;

      data.ParseObjBaseData(bin);
      data.ParseTextureData(bin);
      data.ParseMtrColor(bin);
      data.ParseMdlInfo(bin);
      data.ParseHieLayer(bin);
      data.ParseBoneOffsetMatrices(bin);
      //data.ParseVertexData(bin);
      if (data.H.Offsets[12] != 0) data.ParseMCAPack(bin);
      if (data.H.Offsets[16] != 0) data.ParseACSCLS(bin);

      return data;
    }

    private void OpenFile(string filePath)
    {
      try
      {
        panelEdit.IsEnabled = false;
        cbKeepData.IsEnabled = false;
        dgObject.IsEnabled = false;
        dgVtxIdxGrp.IsEnabled = false;
        modified = false;

        // null when Revert
        if (filePath != null || bn == null)
        {
          bn = System.IO.File.ReadAllBytes(filePath);
          char[] charsToTrim = { '\0' };
          string Name = Encoding.ASCII.GetString(bn, 0, 8).TrimEnd(charsToTrim);
          if (Name != "TMC")
          {
            MessageWindow.Show(this, txt["NotTMC"], txt["Error"]);
            return;
          }
          if (BitConverter.ToUInt32(bn, 8) != 16842752)
          {
            MessageWindow.Show(this, txt["UnsupportedTMC"], txt["Error"]);
            return;
          }
          tmcData = ParseTmc(bn);
        }

        if (tmcData != null)
        {
          if (filePath != null)
          {
            tmcData.Path = filePath;
            tmcData.WriteTime = System.IO.File.GetLastWriteTime(filePath);
          }

          // for broken TMC
          tmcData.H.Size = (uint)bn.Length;
          if (tmcData.H.Offsets[15] != 0 && BitConverter.ToUInt32(bn, (int)tmcData.H.Offsets[15] + 8) != 16842752)
          {
            tmcData.H.Offsets[15] = 0;
          }

          tmcTables = null;
          tmcTables = new TmcTables(tmcData);
          this.DataContext = tmcTables;
          //tmcCopyTables = tmcTables.Clone();

          panelEdit.IsEnabled = true;
          cbKeepData.IsEnabled = true;
          dgObject.IsEnabled = true;
          dgVtxIdxGrp.IsEnabled = true;
          textStatus.Path = filePath ?? tmcPath;
          tmcPath = textStatus.Path;

          this.Activate();

          getMatWindow.Init(tmcData);
        }
      }
      catch (Exception e)
      {
        MessageWindow.Show(this, e.Message + "\r\n\r\n" + e.StackTrace, txt["Error"]);
      }
    }

    private void ReOpenFile(string filePath)
    {
      try
      {
        bn = System.IO.File.ReadAllBytes(filePath);
        tmcData = ParseTmc(bn);

        if (tmcData == null)
        {
          throw new Exception(txt["FailedToReopen"]);
        }

        tmcData.Path = filePath;
        tmcData.WriteTime = System.IO.File.GetLastWriteTime(filePath);

        // 各種状態フラグをリセット
        int count = 0;
        int objIndex = 0;
        foreach (var grp in tmcData.ObjGrp)
        {
          tmcTables.ObjData[count].OriginalGrpIndex = grp.ID;
          tmcTables.ObjData[count].IsAdded = false;
          count++;
          objIndex = 0;

          foreach (var objPart in grp.Obj)
          {
            var obj = tmcTables.ObjData[count];
            obj.IsDeleted = false;
            obj.IsVgrpChanged = false;
            obj.IsAdded = false;
            obj.ID = (byte)objIndex;
            obj.Name = grp.Name + "_" + obj.ID.ToString("x");
            obj.OriginalObjIndex = objIndex;
            obj.OriginalGrpIndex = grp.ID;
            count++;
            objIndex++;
          }
        }
        foreach (var viData in tmcTables.VIData)
        {
          viData.IsChanged = false;
          viData.IsAdded = false;
          viData.OriginalIndex = viData.Index;
          viData.OriginalIdxGrp = viData.IdxGrp;
        }
        if (tmcTables.Nodes.Count > 0) tmcTables.Nodes.Clear();
        foreach (var node in tmcData.Node)
        {
          var newNode = new TmcTables.NodeData(node);
          foreach (var idx in tmcData.Hie[node.Index].Children)
          {
            newNode.Children.Add(tmcData.Node[idx].Name);
          }
          tmcTables.Nodes.Add(newNode);
        }
        modified = false;
        objGrpRebuild = false;
      }
      catch (Exception e)
      {
        MessageWindow.Show(this, e.Message + "\r\n\r\n" + e.StackTrace, txt["Error"]);
      }
    }

    private void SaveFile(string filePath)
    {
      try
      {
        // 各部バイナリ
        //  0 MdlGeo
        //  1 TTX
        //  2 VtxLay
        //  3 IdxLay
        //  4 MtrCol
        //  5 MdlInfo
        //  6 HieLay
        //  7 LHeader
        //  8 NodeLay
        //  9 GlblMtx
        // 10 BnOfsMtx
        // 11 cpf
        // 12 MCAPACK
        // 13 RENPACK
        // 14 GEOXTRAS
        // 15 
        // 16 ACSCLS
        Dictionary<int, List<byte>> partBn = new Dictionary<int, List<byte>>();

        uint lastOffset = tmcData.H.Size;
        for (int i = (int)tmcData.H.Count1 - 1; i >= 0; i--)
        {
          partBn[i] = new List<byte>();

          if (tmcData.H.Offsets[i] != 0)
          {
            partBn[i].AddRange(bn.Skip((int)tmcData.H.Offsets[i]).Take((int)(lastOffset - tmcData.H.Offsets[i])));
            lastOffset = tmcData.H.Offsets[i];
          }
        }

        // for broken TMC
        if (tmcData.H.Offsets[14] != 0 && BitConverter.ToUInt32(bn, (int)tmcData.H.Offsets[14] + 8) != 16842752 && partBn[14].Count > 0x40)
        {
          partBn[14] = new List<byte>(new byte[0x40]);
        }

        foreach (var grp in tmcData.ObjGrp)
        {
          if (grp.ChildCount == 0)
          {
            objGrpRebuild = true;
            break;
          }
        }

        if (objGrpRebuild)
        {
          // インデックスを再設定
          tmcTables.ResetObjectIndex();

          // 頂点グループのObjIDを再設定
          var grpNames = tmcTables.ObjData.Where(obj => obj.Grp == -1).Select(obj => obj.Name).ToList();
          foreach (var vidata in tmcTables.VIData)
          {
            vidata.ObjID = grpNames.IndexOf(vidata.ObjName);
          }

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

            int idx = Array.FindIndex(tmcTables.Nodes.ToArray(), elem => elem.Name == obj.Name);

            tmcTables.Nodes[idx].ObjIndex = obj.ID;
          }
        }

        partBn[0] = setObjGeoDataChanges(partBn[0]); // Tex、両面、透過1、透過2、MtrColを反映

        partBn[2] = setVtxLayDataChanges(partBn[2]); // 頂点削除を反映
        partBn[3] = setIdxLayDataChanges(partBn[3]); // Idx削除を反映
        partBn[4] = setMtrColDataChanges(); // MtrColを再構築
        partBn[5] = setMdlInfoDataChanges(partBn[5]); // MdlInfoを再構築

        if (tmcData.H.Offsets[11] != 0) partBn[11] = setCpfDataChanges(partBn[11]); // itable、切替を反映
        if (tmcData.H.Offsets[12] != 0) partBn[12] = setMcaPackDataChanges(); // MCAPACKを再構築

        if (objGrpRebuild)
        {
          var convertNodeIdx = new Dictionary<int, int>();
          foreach (var node in tmcData.Node)
          {
            int newNodeIdx = Array.FindIndex(tmcTables.Nodes.ToArray(), elem => elem.Name == node.Name);
            convertNodeIdx[node.Index] = newNodeIdx;
          }
          partBn[6] = setHieLayDataChanges(convertNodeIdx); // HieLayを再構築
          partBn[8] = setNodeLayDataChanges(); // NodeLayを再構築
          partBn[9] = setMtxDataChanges(partBn[9], "GlblMtx"); // GlblMtxを再構築
          partBn[10] = setMtxDataChanges(partBn[10], "BnOfsMtx"); // BnOfsMtxを再構築
          partBn[13] = setRenPackDataChanges(partBn[13]); // RenPackを再構築
          if (tmcData.H.Offsets[16] != 0) partBn[16] = setAcsclsDataChanges(partBn[16], convertNodeIdx); // MCAPACKを再構築
        }

        // 全体データの構築とHeaderのオフセットを変更
        List<byte> exBn = new List<byte>();
        exBn.AddRange(bn.Take((int)tmcData.H.Offsets[0]));

        for (int i = 0; i < tmcData.H.Count1; i++)
        {
            // オフセットを変更
          if (partBn[i].Count != 0)
          {
            replaceByteList(exBn, BitConverter.GetBytes(exBn.Count), 0x140 + (i * 4));
          }
          else
          {
            replaceByteList(exBn, BitConverter.GetBytes(0), 0x140 + (i * 4));
          }
          exBn.AddRange(partBn[i]);
        }

        // Headerの全体サイズを変更
        replaceByteList(exBn, BitConverter.GetBytes(exBn.Count), 0x10);

        updateChecking = false;

        // 保存
        System.IO.File.WriteAllBytes(filePath, exBn.ToArray());
        tmcPath = filePath;
        textStatus.Path = tmcPath;
        modified = false;

        if (cbKeepData.IsChecked == false)
        {
          // 再試行可能にしていない場合再読み込み
          ReOpenFile(filePath);
        }
        else
        {
          tmcData.WriteTime = System.IO.File.GetLastWriteTime(tmcData.Path);
        }

        MessageWindow.Show(this, txt["Saved"], txt["Info"]);

        updateChecking = true;

      }
      catch (Exception e)
      {
        MessageWindow.Show(this, e.Message + "\r\n\r\n" + e.StackTrace, txt["Error"]);
      }
    }

    private void CheckUpdated()
    {
      if (tmcData == null || !System.IO.File.Exists(tmcData.Path)) return;

      var lastWriteTime = System.IO.File.GetLastWriteTime(tmcData.Path);
      if (tmcData.WriteTime.CompareTo(lastWriteTime) != 0)
      {
        if (MessageWindow.Show(this, txt["ConfirmFileUpdated"] + "\r\n\r\n" + tmcData.Path, txt["Confirm"], txt["Yes"], txt["No"]) == "OK")
        {
          OpenFile(tmcData.Path);
        }
        else
        {
          tmcData.WriteTime = lastWriteTime;
        }
      }
    }

  }

}
