
using System;
using System.IO;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using nft.framework;
using nft.util;
using nft.ui;
using nft.framework.drawing;
using System.Text;
using nft.core.geometry;
using System.Diagnostics;

namespace nft.debug
{
    public enum Dir4SN : int { NE = 0, NW, SE, SW };
    /// <summary>
    /// TestTerrainForm の概要の説明です。
	/// </summary>
    public class TestTerrainForm : System.Windows.Forms.Form {

        private ListView listView1;
        private NumericUpDown numericUpDown1;
        private Label label1;
        private Label label2;
        private Label status;
        private ColumnHeader ID;
        private ColumnHeader NE;
        private ColumnHeader NW;
        private ColumnHeader SE;
        private ColumnHeader SW;
        private ColumnHeader Housen;
        private ColumnHeader Bright;
        private ColumnHeader direction;
        private ColumnHeader Angle;
        private Button button1;
        private SaveFileDialog saveFileDlg;
        /// <summary>
		/// 必要なデザイナ変数です。
		/// </summary>
		private System.ComponentModel.Container components = null;
        private Hashtable table = new Hashtable();
        Vect3D light { get { return GeometricConstants.SunLight; } }
        Vect3D eye { get { return GeometricConstants.SunLight; } }
        PopupWnd popup;

        public TestTerrainForm()
		{
            popup = new PopupWnd();
            //
			// Windows フォーム デザイナ サポートに必要です。
			//
			InitializeComponent();
            createTable();
		}

		/// <summary>
		/// 使用されているリソースに後処理を実行します。
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

        void createTable() {
            table.Clear();
            listView1.Items.Clear();
            int m = (int)numericUpDown1.Value +1;
            for (int p1 = 0; p1 < m; p1++) {
                for (int p2 = 0; p2 < m; p2++) {
                    for (int p3 = 0; p3 < m; p3++) {
                        if (p1 * p2 * p3 > 0) continue;
                        addItem(new TerrainData(p1, p2, p3, -1));
                        addItem(new TerrainData(p1, p2, -1, p3));
                        addItem(new TerrainData(p1, -1, p2, p3));
                        addItem(new TerrainData(-1, p1, p2, p3));
                    }
                }
            }
            status.Text = makeStatusText();
        }

        string makeStatusText() {
            int total = listView1.Items.Count;
            int visible = 0;
            Hashtable angH = new Hashtable();
            Hashtable angV = new Hashtable();
            foreach (TerrainData dt in table.Values) {
                 Vect3D nml = dt.NormalVector;
                double vis = nml.InnerProduct(eye);
                if (vis < 0.0) {
                    visible++;
                    string ah = nml.Azimuth.ToString("#.0");
                    string av = nml.Elevation.ToString("#.0");
                    if(!angH.ContainsKey(ah))
                        angH.Add(ah,ah);
                    if (!angV.ContainsKey(av))
                        angV.Add(av,av);
                }
            }
            return string.Format("Total={0}, Visible={1}, H={2}, V={3}", total, visible, angH.Count, angV.Count);
        }

        #region Windows フォーム デザイナで生成されたコード
        /// <summary>
        /// デザイナ サポートに必要なメソッドです。このメソッドの内容を
        /// コード エディタで変更しないでください。
        /// </summary>
        private void InitializeComponent()
		{
            this.listView1 = new System.Windows.Forms.ListView();
            this.ID = new System.Windows.Forms.ColumnHeader();
            this.NE = new System.Windows.Forms.ColumnHeader();
            this.NW = new System.Windows.Forms.ColumnHeader();
            this.SE = new System.Windows.Forms.ColumnHeader();
            this.SW = new System.Windows.Forms.ColumnHeader();
            this.Housen = new System.Windows.Forms.ColumnHeader();
            this.Bright = new System.Windows.Forms.ColumnHeader();
            this.direction = new System.Windows.Forms.ColumnHeader();
            this.Angle = new System.Windows.Forms.ColumnHeader();
            this.numericUpDown1 = new System.Windows.Forms.NumericUpDown();
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.status = new System.Windows.Forms.Label();
            this.button1 = new System.Windows.Forms.Button();
            this.saveFileDlg = new System.Windows.Forms.SaveFileDialog();
            ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit();
            this.SuspendLayout();
            // 
            // listView1
            // 
            this.listView1.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.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
            this.ID,
            this.NE,
            this.NW,
            this.SE,
            this.SW,
            this.Housen,
            this.Bright,
            this.direction,
            this.Angle});
            this.listView1.FullRowSelect = true;
            this.listView1.GridLines = true;
            this.listView1.Location = new System.Drawing.Point(5, 35);
            this.listView1.Name = "listView1";
            this.listView1.Size = new System.Drawing.Size(428, 388);
            this.listView1.TabIndex = 0;
            this.listView1.UseCompatibleStateImageBehavior = false;
            this.listView1.View = System.Windows.Forms.View.Details;
            this.listView1.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.listView1_MouseDoubleClick);
            this.listView1.SelectedIndexChanged += new System.EventHandler(this.listView1_SelectedIndexChanged);
            this.listView1.Leave += new System.EventHandler(this.listView1_Leave);
            // 
            // ID
            // 
            this.ID.Text = "#";
            this.ID.Width = 40;
            // 
            // NE
            // 
            this.NE.Text = "ne";
            this.NE.Width = 30;
            // 
            // NW
            // 
            this.NW.Text = "nw";
            this.NW.Width = 30;
            // 
            // SE
            // 
            this.SE.Text = "se";
            this.SE.Width = 30;
            // 
            // SW
            // 
            this.SW.Text = "sw";
            this.SW.Width = 30;
            // 
            // Housen
            // 
            this.Housen.Text = "法線";
            this.Housen.Width = 100;
            // 
            // Bright
            // 
            this.Bright.Text = "輝度";
            this.Bright.Width = 45;
            // 
            // direction
            // 
            this.direction.Text = "方角";
            this.direction.Width = 45;
            // 
            // Angle
            // 
            this.Angle.Text = "仰角";
            this.Angle.Width = 40;
            // 
            // numericUpDown1
            // 
            this.numericUpDown1.Location = new System.Drawing.Point(61, 10);
            this.numericUpDown1.Maximum = new decimal(new int[] {
            8,
            0,
            0,
            0});
            this.numericUpDown1.Minimum = new decimal(new int[] {
            1,
            0,
            0,
            0});
            this.numericUpDown1.Name = "numericUpDown1";
            this.numericUpDown1.Size = new System.Drawing.Size(33, 19);
            this.numericUpDown1.TabIndex = 2;
            this.numericUpDown1.Value = new decimal(new int[] {
            4,
            0,
            0,
            0});
            this.numericUpDown1.ValueChanged += new System.EventHandler(this.numericUpDown1_ValueChanged);
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(25, 12);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(30, 12);
            this.label1.TabIndex = 3;
            this.label1.Text = "Step:";
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(103, 12);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(50, 12);
            this.label2.TabIndex = 3;
            this.label2.Text = "Patterns:";
            // 
            // status
            // 
            this.status.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
            this.status.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
            this.status.Location = new System.Drawing.Point(159, 10);
            this.status.Name = "status";
            this.status.Padding = new System.Windows.Forms.Padding(1);
            this.status.Size = new System.Drawing.Size(191, 19);
            this.status.TabIndex = 3;
            this.status.Text = "N/A";
            // 
            // button1
            // 
            this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.button1.Location = new System.Drawing.Point(365, 7);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(60, 23);
            this.button1.TabIndex = 4;
            this.button1.Text = "Save";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // saveFileDlg
            // 
            this.saveFileDlg.DefaultExt = "csv";
            this.saveFileDlg.Filter = "CSVﾌｧｲﾙ|*.csv|全てのファイル|*.*";
            // 
            // TestTerrainForm
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 12);
            this.ClientSize = new System.Drawing.Size(437, 427);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.status);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.numericUpDown1);
            this.Controls.Add(this.listView1);
            this.Name = "TestTerrainForm";
            this.Text = "TestTerrainForm";
            ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

		}
		#endregion

        void addItem(TerrainData data) {
            if (!table.ContainsKey(data)) {
                table.Add(data, data);
                string v = data.GetHashCode().ToString("X4");
                ListViewItem item = listView1.Items.Add(v,v);
                Array d4 = Enum.GetValues(typeof(Dir4SN));
                foreach (Dir4SN dir in d4) {
                    item.SubItems.Add(data[dir].ToString());
                }
                item.SubItems.Add(data.getHousenText());
                Vect3D nml = data.NormalVector;
                double vis = nml.InnerProduct(eye);
                double br = double.NaN;
                if (vis < 0.0) {
                    br = -nml.InnerProduct(light);
                    if (br < 0) {
                        br = 0;
                    }
                }
                item.SubItems.Add(br.ToString("#.00"));
                item.SubItems.Add(nml.Azimuth.ToString("#.0"));
                item.SubItems.Add(nml.Elevation.ToString("#.0"));
            }
        }

        private void numericUpDown1_ValueChanged(object sender, EventArgs e) {
            createTable();
        }

        private void button1_Click(object sender, EventArgs e) {
            saveFileDlg.ShowDialog();
            string fname = saveFileDlg.FileName;
            if (fname != null && fname.Length > 0) {                
                StreamWriter writer = new StreamWriter(fname,false,Encoding.GetEncoding("shift-jis"));
                writer.WriteLine("#,ne,nw,se,sw,nx,ny,nz,br,ah,av");
                foreach(ListViewItem itm in listView1.Items){
                    //writer.Write(itm.Text);
                    foreach(ListViewItem.ListViewSubItem sitm  in itm.SubItems){
                        writer.Write(sitm.Text);
                        writer.Write(',');
                    }
                    writer.WriteLine();
                }
                writer.Flush();
                writer.Close();
            }
        }

        private void listView1_MouseDoubleClick(object sender, MouseEventArgs e) {
            ListViewHitTestInfo info = listView1.HitTest(e.Location);
            if (info.Item != null) {
                popup.Show(this);
            }
        }

        private void listView1_Leave(object sender, EventArgs e) {
            if (popup.Visible) {
                popup.Hide();
            }
        }

        private void listView1_SelectedIndexChanged(object sender, EventArgs e) {
            if (popup.Visible) {
                popup.Hide();
            }
        }

	}

    class PopupWnd : Form {
        private System.ComponentModel.Container components = null;
        private PictureBox picBox;
        public PopupWnd() {
            InitializeComponent();
        }
        private void InitializeComponent() {
            this.SuspendLayout();
            // 
            // picBox
            // 
            this.picBox = new PictureBox();
            this.picBox.Location = new System.Drawing.Point(351, 4);
            this.picBox.Name = "picBox";
            //this.picBox.Size = new System.Drawing.Size(32, 96);
            this.picBox.Dock = DockStyle.Fill;
            this.picBox.TabIndex = 5;
            this.picBox.TabStop = false;

            this.Size = new System.Drawing.Size(32, 96);
            this.FormBorderStyle = FormBorderStyle.None;
            this.Controls.Add(this.picBox);
            this.Name = "TerrainPopup";
            this.Text = "TerrainPopup";
            this.ShowInTaskbar = false;
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        public void setImage(Image img) {
            picBox.Image = img;
            if (img != null) {
                this.Size = img.Size;
            }
        }
        protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}
    }

    class TerrainData {
        private int id;
        private int[] vortex;        
        private Vect3D housen;

        public TerrainData(int ne, int nw, int se, int sw){
            vortex = new int[]{ne, nw, se, sw};
            id = (vortex[0] & 0x000f) + ((vortex[1] & 0x000f) << 4) + ((vortex[2] & 0x000f) << 8) + ((vortex[3] & 0x000f )<< 12);
            calcHousen();
        }

        private void calcHousen() {
            Vect3D[] pos = new Vect3D[3];
            int j = 0;
            for (int i = 0; i < 4; i++) {
                if (vortex[i] >= 0 && j<4) {
                    pos[j].Z = vortex[i];
                    pos[j].X = (i < 2) ? -1 : 1;
                    pos[j].Y = (i & 1) == 1 ? -1 : 1;
                    j++;
                }
            }
            housen = new Vect3D();
            housen.X = (pos[2].Y - pos[1].Y) * (pos[0].Z - pos[2].Z) - (pos[0].Y - pos[2].Y) * (pos[2].Z - pos[1].Z);
            housen.Y = (pos[2].Z - pos[1].Z) * (pos[0].X - pos[2].X) - (pos[0].Z - pos[2].Z) * (pos[2].X - pos[1].X);
            housen.Z = (pos[2].X - pos[1].X) * (pos[0].Y - pos[2].Y) - (pos[0].X - pos[2].X) * (pos[2].Y - pos[1].Y);
            housen.Normalize();
            if (housen.Z < 0.0)
                housen.Multiple(-1.0);
        }

        public int this[Dir4SN dir] {
            get{
                return vortex[(int)dir];
            }
            set{
                vortex[(int)dir] = value;
                calcHousen();
            }
        }

        public Vect3D NormalVector {
            get {
                return housen;
            }
        }

        public String getHousenText() {
            return String.Format("{0:0.##}, {1:0.##}, {2:0.##}", housen.X, housen.Y, housen.Z);
        }

        public override int GetHashCode() {
            return id;
        }

        public override bool Equals(object obj) {
            TerrainData other = obj as TerrainData;
            if (other != null) {
                return other.id==this.id;
            } else {
                return false;
            }
        }
    }
}
