using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TDCGExplorer;
using System.Data;
using System.Drawing;
using System.IO;
using System.Diagnostics;
using Microsoft.DirectX;
using System.Drawing.Imaging;

namespace System.Windows.Forms
{
    class PoseFilePage : ZipFilePageControl
    {
        private ContextMenuStrip contextMenuStripPoseSaveData;
        private System.ComponentModel.IContainer components;
        private string filename;
        private ToolStripMenuItem toolStripMenuItemThumbs;
        private ToolStripMenuItem toolStripMenuItemClose;
        private TreeView PoseTreeView;
        private ToolStripMenuItem toolStripMenuItemSaveTmo;
        private ToolStripMenuItem toolStripMenuItemMakeTahFile;
        private PNGPOSEStream posestream;
        private PNGPoseData posedata;
        private ToolStripMenuItem toolStripMenuItemShowPose;

        private MemoryStream streamdata = null;

        private bool fDisplayed = false;

        // zipファイルの中から
        public PoseFilePage(GenericTahInfo tahInfo) : base(tahInfo)
        {
            filename = Path.GetFileName(tahInfo.path);
            Cursor.Current = Cursors.WaitCursor;
            try
            {
                InitializeComponent();
                ExtractFile();
                TDCGExplorer.TDCGExplorer.SetToolTips(Text);
            }
            catch (System.InvalidCastException ex)
            {
                Debug.WriteLine(ex.Message);
            }
            Cursor.Current = Cursors.Default;
        }
        // ファイルから直接読み出す.
        public PoseFilePage(string path)
        {
            Cursor.Current = Cursors.WaitCursor;
            try
            {
                InitializeComponent();
                Text = Path.GetFileName(path);
                filename = Path.GetFileName(path);
                TDCGExplorer.TDCGExplorer.LastAccessFile = path;
                using (FileStream fs = File.OpenRead(path))
                {
                    using (MemoryStream ms = new MemoryStream())
                    {
                        ZipFileUtil.CopyStream(fs, ms);
                        BindingStream(ms);
                        ms.Close();
                    }
                    fs.Close();
                }
                TDCGExplorer.TDCGExplorer.SetToolTips(Text);
            }
            catch (System.InvalidCastException ex)
            {
                Debug.WriteLine(ex.Message);
            }
            Cursor.Current = Cursors.Default;
        }

        protected override void Dispose(bool disposing)
        {
            if (streamdata != null) streamdata.Dispose();
            streamdata = null;
            if (posedata != null) posedata.Dispose();
            posedata = null;
            if (posestream != null) posestream.Dispose();
            posestream = null;

            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.contextMenuStripPoseSaveData = new System.Windows.Forms.ContextMenuStrip(this.components);
            this.toolStripMenuItemThumbs = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripMenuItemSaveTmo = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripMenuItemMakeTahFile = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripMenuItemShowPose = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripMenuItemClose = new System.Windows.Forms.ToolStripMenuItem();
            this.PoseTreeView = new System.Windows.Forms.TreeView();
            this.contextMenuStripPoseSaveData.SuspendLayout();
            this.SuspendLayout();
            // 
            // contextMenuStripPoseSaveData
            // 
            this.contextMenuStripPoseSaveData.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.toolStripMenuItemThumbs,
            this.toolStripMenuItemSaveTmo,
            this.toolStripMenuItemMakeTahFile,
            this.toolStripMenuItemShowPose,
            this.toolStripMenuItemClose});
            this.contextMenuStripPoseSaveData.Name = "contextMenuStripSaveData";
            this.contextMenuStripPoseSaveData.Size = new System.Drawing.Size(265, 114);
            // 
            // toolStripMenuItemThumbs
            // 
            this.toolStripMenuItemThumbs.Name = "toolStripMenuItemThumbs";
            this.toolStripMenuItemThumbs.Size = new System.Drawing.Size(264, 22);
            this.toolStripMenuItemThumbs.Text = TextResource.MakeThumbnail;
            this.toolStripMenuItemThumbs.Click += new System.EventHandler(this.toolStripMenuItemThumbs_Click);
            // 
            // toolStripMenuItemSaveTmo
            // 
            this.toolStripMenuItemSaveTmo.Name = "toolStripMenuItemSaveTmo";
            this.toolStripMenuItemSaveTmo.Size = new System.Drawing.Size(264, 22);
            this.toolStripMenuItemSaveTmo.Text = TextResource.SaveTMOFile;
            this.toolStripMenuItemSaveTmo.Click += new System.EventHandler(this.toolStripMenuItemSaveTmo_Click);
            // 
            // toolStripMenuItemMakeTahFile
            // 
            this.toolStripMenuItemMakeTahFile.Name = "toolStripMenuItemMakeTahFile";
            this.toolStripMenuItemMakeTahFile.Size = new System.Drawing.Size(264, 22);
            this.toolStripMenuItemMakeTahFile.Text = TextResource.SaveTAHFile;
            this.toolStripMenuItemMakeTahFile.Click += new System.EventHandler(this.toolStripMenuItemMakeTahFile_Click);
            // 
            // toolStripMenuItemShowPose
            // 
            this.toolStripMenuItemShowPose.Name = "toolStripMenuItemShowPose";
            this.toolStripMenuItemShowPose.Size = new System.Drawing.Size(264, 22);
            this.toolStripMenuItemShowPose.Text = TextResource.DisplayPoseTSOView;
            this.toolStripMenuItemShowPose.Click += new System.EventHandler(this.toolStripMenuItemShowPose_Click);
            // 
            // toolStripMenuItemClose
            // 
            this.toolStripMenuItemClose.Name = "toolStripMenuItemClose";
            this.toolStripMenuItemClose.Size = new System.Drawing.Size(264, 22);
            this.toolStripMenuItemClose.Text = TextResource.Close;
            this.toolStripMenuItemClose.Click += new System.EventHandler(this.toolStripMenuItemClose_Click);
            // 
            // PoseTreeView
            // 
            this.PoseTreeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
            this.PoseTreeView.ContextMenuStrip = this.contextMenuStripPoseSaveData;
            this.PoseTreeView.LineColor = System.Drawing.Color.Empty;
            this.PoseTreeView.Location = new System.Drawing.Point(0, 0);
            this.PoseTreeView.Name = "PoseTreeView";
            this.PoseTreeView.Size = new System.Drawing.Size(121, 97);
            this.PoseTreeView.TabIndex = 0;
            this.PoseTreeView.DoubleClick += new System.EventHandler(this.PoseTreeView_DoubleClick);
            // 
            // PoseFilePage
            // 
            this.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
            this.Controls.Add(this.PoseTreeView);
            this.Resize += new System.EventHandler(this.PoseTreeeView_Resize);
            this.Enter += new System.EventHandler(this.PoseFilePage_Enter);
            this.contextMenuStripPoseSaveData.ResumeLayout(false);
            this.ResumeLayout(false);

        }

        public override void BindingStream(MemoryStream ms)
        {
            // データの複製を作る.
            streamdata = new MemoryStream();
            ZipFileUtil.CopyStream(ms, streamdata);

            ms.Seek(0, SeekOrigin.Begin);
            Bitmap savefilebitmap = new Bitmap(ms);

            posestream = new PNGPOSEStream();
            ms.Seek(0, SeekOrigin.Begin);
            posedata = posestream.LoadStream(ms);

#if false
            TDCGExplorer.TDCGExplorer.MainFormWindow.PictureBox.Image = savefilebitmap;
            TDCGExplorer.TDCGExplorer.MainFormWindow.PictureBox.Width = savefilebitmap.Width;
            TDCGExplorer.TDCGExplorer.MainFormWindow.PictureBox.Height = savefilebitmap.Height;
#else
            TDCGExplorer.TDCGExplorer.MainFormWindow.SetBitmap(savefilebitmap);
#endif
#if false
            TDCG.Viewer viewer = TDCGExplorer.TDCGExplorer.MainFormWindow.Viewer;
            if (posedata.scene == false)
            {
                // キャラが無い時はデフォ子
                if (TDCGExplorer.TDCGExplorer.FigureLoad == false)
                {
                    viewer.AddFigureFromPNGFile("default.tdcgsav.png", false);
                    TDCGExplorer.TDCGExplorer.FigureLoad = true;
                }
                using (MemoryStream tmo = new MemoryStream(posedata.figures[0].tmo.data))
                {
                    viewer.LoadTMOFile(tmo);
                }
            }
            else
            {
                // 全部ロードする.
                ms.Seek(0, SeekOrigin.Begin);
                viewer.AddFigureFromPNGStream(ms, false);
                TDCGExplorer.TDCGExplorer.FigureLoad = false;
            }
            List<float> camera = posedata.GetCamera();
            Vector3 eye = new Vector3(camera[0], camera[1], camera[2]);
            Vector3 ypr = new Vector3(camera[5], camera[4], camera[6]);
            Matrix m = Matrix.RotationYawPitchRoll(ypr.Y, ypr.X, ypr.Z) * Matrix.Translation(eye.X, eye.Y, eye.Z);
            viewer.Camera.Reset();
            viewer.Camera.Translation = new Vector3(-m.M41, -m.M42, m.M43);
            viewer.Camera.Angle = ypr;

            TDCGExplorer.TDCGExplorer.MainFormWindow.setNeedCameraReset();
#endif
            // データ階層ツリーを構築する.

            MakeTreeView();
        }

        public void DisplayPose()
        {
            if (TDCGExplorer.TDCGExplorer.MainFormWindow.Viewer == null) TDCGExplorer.TDCGExplorer.MainFormWindow.makeTSOViwer();

            fDisplayed = true;

            TDCG.Viewer viewer = TDCGExplorer.TDCGExplorer.MainFormWindow.Viewer;
            if (posedata.scene == false)
            {
                if (TDCGExplorer.TDCGExplorer.SystemDB.forcereloadsavedata == true)
                {
                    viewer.FigureList.Clear();
                    viewer.AddFigureFromPNGFile("default.tdcgsav.png", false);
                }
                else
                {
                    if (viewer.FigureList.Count == 1 && viewer.FigureList[0].TSOList.Count > 1)
                    {
                        // なにもしない.
                    }else{
                        viewer.FigureList.Clear();
                        viewer.AddFigureFromPNGFile("default.tdcgsav.png", false);
                    }
                }

                //TDCGExplorer.TDCGExplorer.FigureLoad = true;
                using (MemoryStream tmo = new MemoryStream(posedata.figures[0].tmo.data))
                {
                    // あらかじめFigureは全部消去する.
                    viewer.LoadTMOFile(tmo);
                    viewer.BackColor = Color.LightPink;
                }
            }
            else
            {
                // 全部ロードする.
                streamdata.Seek(0, SeekOrigin.Begin);
                viewer.AddFigureFromPNGFile(streamdata, false);
                //TDCGExplorer.TDCGExplorer.FigureLoad = false;
                viewer.BackColor = Color.Yellow;
            }
            List<float> camera = posedata.GetCamera();
            Vector3 eye = new Vector3(camera[0], camera[1], camera[2]);
            Vector3 ypr = new Vector3(-camera[5], -camera[4], -camera[6]);
            Matrix m = Matrix.RotationYawPitchRoll(ypr.Y, ypr.X, ypr.Z) * Matrix.Translation(eye.X, eye.Y, eye.Z);
            viewer.Camera.Reset();
            viewer.Camera.Translation = new Vector3(-m.M41, -m.M42, -m.M43);
            viewer.Camera.Angle = ypr;

            TDCGExplorer.TDCGExplorer.MainFormWindow.setNeedCameraReset();
        }

        private void PoseTreeeView_Resize(object sender, EventArgs e)
        {
            PoseTreeView.Size = Size;
        }

        private void toolStripMenuItemThumbs_Click(object sender, EventArgs e)
        {
            if (TDCGExplorer.TDCGExplorer.BusyTest() == true) return;
            ChangeThumb();
            //TDCGExplorer.TDCGExplorer.MainFormWindow.UpdateSaveFileTree();
        }

        public void ChangeThumb()
        {
            if (fDisplayed == false)
            {
                DisplayPose();
                TDCGExplorer.TDCGExplorer.MainFormWindow.Viewer.FrameMove();
                TDCGExplorer.TDCGExplorer.MainFormWindow.Viewer.Render();
            }
            // サーフェースからbitmapを作る.
            Bitmap orgbitmap = TDCGExplorer.TDCGExplorer.MainFormWindow.Viewer.GetBitmap();
            // アイコン用bitmapを作る.
            Bitmap savefilebitmap = new Bitmap(128, 128, PixelFormat.Format24bppRgb);

            // 縮小する.
            int x1, y1, x2, y2;
            if (orgbitmap.Width > orgbitmap.Height)
            {// 横に長い
                int div = orgbitmap.Width - orgbitmap.Height;
                x1 = div / 2;
                x2 = orgbitmap.Width - div / 2;
                y1 = 0;
                y2 = orgbitmap.Height;
            }
            else
            {// 縦に長い
                int div = orgbitmap.Height - orgbitmap.Width;
                x1 = 0;
                x2 = orgbitmap.Width;
                y1 = div / 2;
                y2 = orgbitmap.Height - div / 2;
            }
            Graphics srcG = Graphics.FromImage(orgbitmap);
            Graphics dstG = Graphics.FromImage(savefilebitmap);
            Rectangle dstRect = new Rectangle(0, 0, 128, 128);
            dstG.DrawImage(orgbitmap, dstRect, x1, y1, x2 - x1, y2 - y1, GraphicsUnit.Pixel);

            try
            {
                // ヘビーセーブ形式で保存する.
                if (savefilebitmap == null) return;
                // まずPNG形式のデータを作る.
                using (MemoryStream basepng = new MemoryStream())
                {
                    savefilebitmap.Save(basepng, System.Drawing.Imaging.ImageFormat.Png);
                    // PNGFileクラスにデータを取り込む.
                    using (PNGPOSEStream pngstream = new PNGPOSEStream())
                    {
                        basepng.Seek(0, SeekOrigin.Begin);
                        PNGFile png = pngstream.GetPNG(basepng);
                        //POSEデータを設定する.
                        pngstream.PoseData = posedata;
                        // 保存先を決める.
                        string savefile_dir = TDCGExplorer.TDCGExplorer.SystemDB.posefile_savedirectory;
                        string savefile_name = Path.GetFileNameWithoutExtension(filename) + ".png";

                        SaveFileDialog dialog = new SaveFileDialog();
                        dialog.FileName = filename;
                        dialog.InitialDirectory = TDCGExplorer.TDCGExplorer.SystemDB.posefile_savedirectory;
                        dialog.Filter = TextResource.PNGPoseFileDescription;
                        dialog.FilterIndex = 0;
                        dialog.Title = TextResource.SelectSaveFileName;
                        dialog.RestoreDirectory = true;
                        dialog.OverwritePrompt = true;
                        dialog.CheckPathExists = true;

                        if (dialog.ShowDialog() == DialogResult.OK)
                        {
                            string destpath = Path.Combine(savefile_dir, dialog.FileName);
                            // 保存先をオープン.
                            //File.Delete(destpath);
                            TDCGExplorer.TDCGExplorer.FileDelete(destpath);
                            using (Stream output = File.Create(destpath))
                            {
                                // PNGを出力する.
                                pngstream.SavePNGFile(png, output);
                            }

                            // 以前表示していたbitmapを捨てる.
#if false
                        TDCGExplorer.TDCGExplorer.MainFormWindow.PictureBox.Image = savefilebitmap;
                        TDCGExplorer.TDCGExplorer.MainFormWindow.PictureBox.Width = savefilebitmap.Width;
                        TDCGExplorer.TDCGExplorer.MainFormWindow.PictureBox.Height = savefilebitmap.Height;
#else
                            TDCGExplorer.TDCGExplorer.MainFormWindow.SetBitmap(savefilebitmap);
#endif
                            // ファイルを追加する.
                            TDCGExplorer.TDCGExplorer.AddFileTree(destpath);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                TDCGExplorer.TDCGExplorer.SetToolTips(TextResource.Error + ":" + ex.Message);
            }
            srcG.Dispose();
            dstG.Dispose();
            orgbitmap.Dispose();

            //savefilebitmapは表示に使うので捨ててはいけない.
            //savefilebitmap.Dispose();
        }

        private void PoseFilePage_Enter(object sender, EventArgs e)
        {
            Control obj = (Control)sender;
            obj.Focus();
        }

        private void MakeTreeView()
        {
            PoseDataNode posenode = new PoseDataNode(filename);
            PoseTreeView.Nodes.Add(posenode);
            // tahを展開する.
            if (posedata.scene)
            {
                PoseDataCameraNode camera = new PoseDataCameraNode(TextResource.Camera, posedata.camera);
                posenode.Nodes.Add(camera);
                foreach (TDCGExplorer.PNGPoseFigureData data in posedata.figures)
                {
                    PoseDataFigureNode figure = new PoseDataFigureNode(TextResource.Figure, data);
                    posenode.Nodes.Add(figure);
                    PoseDataLightNode light = new PoseDataLightNode(TextResource.Light, data.light);
                    figure.Nodes.Add(light);
                    PoseDataTMONode tmo = new PoseDataTMONode(TextResource.TMO, data.tmo);
                    figure.Nodes.Add(tmo);
                    PoseDataTSONode tso = new PoseDataTSONode(TextResource.TSO, data.tsos);
                    figure.Nodes.Add(tso);
                }
            }
            else
            {
                PoseDataCameraNode camera = new PoseDataCameraNode(TextResource.Camera, posedata.camera);
                PoseDataLightNode light = new PoseDataLightNode(TextResource.Light, posedata.figures[0].light);
                PoseDataTMONode tmo = new PoseDataTMONode(TextResource.TMO, posedata.figures[0].tmo);
                posenode.Nodes.Add(camera);
                posenode.Nodes.Add(light);
                posenode.Nodes.Add(tmo);
            }
            posenode.ExpandAll();
        }

        private void toolStripMenuItemSaveTmo_Click(object sender, EventArgs e)
        {
            if (TDCGExplorer.TDCGExplorer.BusyTest() == true) return;

            PoseDataTMONode node = PoseTreeView.SelectedNode as PoseDataTMONode;
            if (node == null)
            {
                MessageBox.Show(TextResource.OnlyTMOFile, TextResource.Error, MessageBoxButtons.OK);
                return;
            }

            try
            {
                SaveFileDialog dialog = new SaveFileDialog();
                dialog.FileName = filename + ".tmo";
                dialog.InitialDirectory = TDCGExplorer.TDCGExplorer.SystemDB.tahpath;
                dialog.Filter = TextResource.TMOFileDescription;
                dialog.FilterIndex = 0;
                dialog.Title = TextResource.SelectSaveFileName;
                dialog.RestoreDirectory = true;
                dialog.OverwritePrompt = true;
                dialog.CheckPathExists = true;

                if (dialog.ShowDialog() == DialogResult.OK)
                {
                    string tmofilename = dialog.FileName;
                    //File.Delete(tmofilename);
                    TDCGExplorer.TDCGExplorer.FileDelete(tmofilename);

                    using(MemoryStream ms = new MemoryStream(node.tmo.data))
                    using (Stream fileStream = File.Create(tmofilename))
                    {
                        BufferedStream bufferedDataStream = new BufferedStream(ms);
                        BufferedStream bufferedFileStream = new BufferedStream(fileStream);
                        CopyStream(bufferedDataStream, bufferedFileStream);

                        bufferedFileStream.Flush();
                        bufferedFileStream.Close();
                        bufferedDataStream.Close();
                    }
                }
            }
            catch (Exception ex)
            {
                TDCGExplorer.TDCGExplorer.SetToolTips(TextResource.Error + ":" + ex.Message);
            }

        }

        private static void CopyStream(Stream input, Stream output)
        {
            byte[] buf = new byte[1024];
            int len;
            while ((len = input.Read(buf, 0, buf.Length)) > 0)
            {
                output.Write(buf, 0, len);
            }
        }

        private void toolStripMenuItemMakeTahFile_Click(object sender, EventArgs e)
        {
            if (TDCGExplorer.TDCGExplorer.BusyTest() == true) return;

            PoseDataTSONode node = PoseTreeView.SelectedNode as PoseDataTSONode;
            if (node == null)
            {
                MessageBox.Show(TextResource.OnlyTSOFile, TextResource.Error, MessageBoxButtons.OK);
                return;
            }

            try
            {
                SimpleTextDialog dialog = new SimpleTextDialog();
                dialog.Owner = TDCGExplorer.TDCGExplorer.MainFormWindow;
                dialog.dialogtext = TextResource.SaveTAHFile;
                dialog.labeltext = TextResource.Filename;
                dialog.textfield = filename;

                if (dialog.ShowDialog() == DialogResult.OK)
                {
#if false
                    // 新規TAHを作成する.
                    string dbfilename = LBFileTahUtl.GetTahDbPath(dialog.textfield);
                    string tahfilename = Path.GetFileNameWithoutExtension(dialog.textfield);
                    if (File.Exists(dbfilename))
                    {
                        MessageBox.Show("既にデータベースファイルがあります。\n" + dbfilename + "\n削除してから操作してください。", "エラー", MessageBoxButtons.OK);
                        return;
                    }
#endif
                    // 常に新規タブで.
                    TAHEditor editor = null;
                    try
                    {
                        editor = new TAHEditor( null);
                        editor.SetInformation(Path.GetFileNameWithoutExtension(dialog.textfield) + ".tah", 1);
                        editor.makeTAHFile(dialog.textfield, node.tso);
                        TDCGExplorer.TDCGExplorer.MainFormWindow.AssignTagPageControl(editor);
                        editor.SelectAll();
                    }
                    catch (Exception)
                    {
                        if (editor != null) editor.Dispose();
                    }
                }
            }
            catch (Exception ex)
            {
                TDCGExplorer.TDCGExplorer.SetToolTips(TextResource.Error + ":" + ex.Message);
            }

        }

        private void toolStripMenuItemClose_Click(object sender, EventArgs e)
        {
            if (TDCGExplorer.TDCGExplorer.BusyTest()) return;
            Parent.Dispose();
        }

        private void toolStripMenuItemShowPose_Click(object sender, EventArgs e)
        {
            if (streamdata != null)
            {
                DisplayPose();
            }
        }

        private void PoseTreeView_DoubleClick(object sender, EventArgs e)
        {
            if (streamdata != null)
            {
                DisplayPose();
            }
        }

    }

    // topノード
    class PoseDataNode : TreeNode
    {
        public PoseDataNode(string text)
        {
            Text = text;
        }

        // 共通ユーティリティ関数
        public List<float> ByteToFloat(Byte[] data)
        {
            List<float> retval = new List<float>();
            for (int offset = 0; offset < data.Length; offset += sizeof(float))
            {
                float flo = BitConverter.ToSingle(data, offset);
                retval.Add(flo);
            }
            return retval;
        }
    }

    class PoseDataCameraNode : PoseDataNode
    {
        public byte[] camera;
        public PoseDataCameraNode(string text, Byte[] data)
            : base(text)
        {
            camera = data;
        }
    }

    class PoseDataFigureNode : PoseDataNode
    {
        public PNGPoseFigureData figure;
        public PoseDataFigureNode(string text, PNGPoseFigureData data)
            : base(text)
        {
            figure = data;
        }
    }

    class PoseDataLightNode : PoseDataNode
    {
        public PNGPoseLight light;
        public PoseDataLightNode(string text, PNGPoseLight data)
            : base(text)
        {
            light = data;
        }
    }

    class PoseDataTMONode : PoseDataNode
    {
        public PNGPoseTmoData tmo;
        public PoseDataTMONode(string text, PNGPoseTmoData data)
            : base(text)
        {
            tmo = data;
        }
    }

    class PoseDataTSONode : PoseDataNode
    {
        public List<PNGTsoData> tso;
        public PoseDataTSONode(string text, List<PNGTsoData> data)
            : base(text)
        {
            tso = data;
        }
    }
}
