using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
//using System.
using Nlgp1.StageEditor.Notifies;

namespace Nlgp1.StageEditor.Controls {
	public partial class MapControl : UserControl {
		private StageNotify stage;
		private EditorNotify editor;

		public MapControl() {
			InitializeComponent();

			this.Screen.MouseDown += MouseDownOrMoveGrid;
			this.Screen.MouseMove += MouseDownOrMoveGrid;
			this.KeyDown += KeyDownGrid;
			this.KeyUp += KeyUpGrid;





			this.Screen.Paint += OnPaint;
		}

		public void Clear() {
			//	commonMap.l
		}

		#region PropertyChanged
		public void MapData_PropertyChanged( object sender , PropertyChangedEventArgs e ) {
			var stage = (StageNotify)sender;
			this.stage = stage;
			if( e.PropertyName == "Create" )
				UpdateSize();
			if( e.PropertyName == "AddLayer" )
				Refresh();
			if( e.PropertyName == "RemoveLayer" )
				Refresh();
			if( e.PropertyName == "MoveUpLayer" )
				Refresh();
			if( e.PropertyName == "MoveDownLayer" || e.PropertyName == "Refresh" )
				Refresh();
			if( e.PropertyName == "UpdateSize" )
				UpdateSize();
		}

		public void ChipData_PropertyChanged( object sender , PropertyChangedEventArgs e ) {
			if( e.PropertyName == "SetProperty" )
				Refresh();
		}

		public void EditorData_PropertyChanged( object sender , PropertyChangedEventArgs e ) {
			var editor = (EditorNotify)sender;
			this.editor = editor;
			if( e.PropertyName == "Create" )
				Refresh();
			if( e.PropertyName == "CurrentLayer" )
				Refresh();
			if( e.PropertyName == "SetGridProperty" )
				Refresh();
			if( e.PropertyName == "SetScale" )
				UpdateSize();
		}

		public void ChipGridData_PropertyChanged( object sender , PropertyChangedEventArgs e ) {
			var layer = (LayerNotify)sender;
			if( e.PropertyName == "LoadLayerImage" )
				Refresh();
			if( e.PropertyName == "SetChipIndex" )
				Refresh();
			if( e.PropertyName == "SetGridData" )
				Refresh();
			if( e.PropertyName == "Shift" )
				Refresh();
			if( e.PropertyName == "RemoveChipData" )
				Refresh();
		}

		public void ViewSettingData_PropertyChanged( object sender , PropertyChangedEventArgs e ) {
			Refresh();
		}

		private void UpdateSize() {
			if( stage == null )
				return;
			if( editor == null )
				return;

			this.Anchor = AnchorStyles.Left | AnchorStyles.Top;

			var scale = editor.Scale;
			vScroll.Maximum = (int)Math.Round( stage.ColumnNum * scale );
			hScroll.Maximum = (int)Math.Round( stage.RowNum * scale );
			Refresh();
		}
		#endregion // PropertyChanged

		private void KeyDownGrid(object sender, KeyEventArgs e)
		{
			//MessageBox.Show("KeyDown");
			isCopying = true;

		}
		private void KeyUpGrid(object sender, KeyEventArgs e)
		{
			//
			//MessageBox.Show("KeyUp");
			isCopying = false;
		}

		private void MouseDownOrMoveGrid( object sender , MouseEventArgs e ) {
			if( e.Button != MouseButtons.Left )
				return;

			var scale = editor.Scale;
			var width = Screen.Width;
			var height = Screen.Height;
			if( e.Location.X < 0 || e.Location.X >= width )
				return;
			if( e.Location.Y < 0 || e.Location.Y >= height )
				return;

			var chipSize = stage.ChipSize * scale;
			var row = (int)Math.Floor( e.Location.Y / chipSize );
			var column = (int)Math.Floor( e.Location.X / chipSize );

			OnClickGrid( row + vScroll.Value , column + hScroll.Value , isCopying);
		}

		#region ClickGrid
		public event ClickGridEventHandler ClickGrid;
		private void OnClickGrid( int row , int column ,bool iscopying) {
			if( ClickGrid == null )
				return;

			var e = new ClickGridEventArgs( row , column , iscopying);
			ClickGrid( this , e );
		}

		public delegate void ClickGridEventHandler( object sender , ClickGridEventArgs e );
		public class ClickGridEventArgs : EventArgs {
			public int Row {
				get;
				private set;
			}
			public int Column {
				get;
				private set;
			}
			public bool IsCopying
			{
				get;
				private set;
			}
			public ClickGridEventArgs( int row , int column, bool iscopying ) {
				this.Row = row;
				this.Column = column;
				this.IsCopying = iscopying;
			}
		}
		#endregion




		public event DragGridEventHandler DragGrid;
		private void OnDragGrid(int row, int column, int rownum, int colnum, bool iscopying)
		{
			if (DragGrid == null)
				return;

			var e = new DragGridEventArgs(row, column,rownum,colnum,iscopying);
			DragGrid(this, e);
		}

		public delegate void DragGridEventHandler(object sender, DragGridEventArgs e);
		public class DragGridEventArgs : EventArgs
		{
			public int Row
			{
				get;
				private set;
			}
			public int Column
			{
				get;
				private set;
			}
			public int RowNum
			{
				get;
				private set;
			}
			public int ColumnNum
			{
				get;
				private set;
			}
			public bool IsCopying
			{
				get;
				private set;
			}
			public DragGridEventArgs(int row, int column,int rownum, int colnum,bool copying)
			{
				this.Row = row;
				this.Column = column;
				this.RowNum = rownum;
				this.ColumnNum = colnum;
				this.IsCopying = copying;
			}
		}
		#region ドラッグスクロール
		private bool isDragging = false;
		private Point prevMousePoint = new Point();
		private Point afterMousePoint = new Point();
		private Point currentMousePoint = new Point();
		private Rectangle dragRect = new Rectangle();
		private bool isCopying = false;
		private void Screen_MouseDown( object sender , MouseEventArgs e ) {
			/*if( e.Button != MouseButtons.Right )
				return;
			this.isDragging = true;
			this.prevMousePoint = Screen.PointToScreen( e.Location );*/
			if (e.Button == MouseButtons.Right)
			{
				this.isDragging = true;
				var scale = editor.Scale;
				var width = Screen.Width;
				var height = Screen.Height;
				var chipSize = stage.ChipSize * scale;
				var row = (int)Math.Floor(e.Location.Y / chipSize);
				var column = (int)Math.Floor(e.Location.X / chipSize);

				this.prevMousePoint = new Point(row + vScroll.Value, column + hScroll.Value);
			}


		}

		private void Screen_MouseMove( object sender , MouseEventArgs e ) {
			/*if( isDragging == false )
				return;
			var chipSize = commonMap.ChipSize * editor.Scale;
			var location = Screen.PointToScreen( e.Location );
			var moveX = ( location.X - prevMaousePoint.X );
			var moveY = ( location.Y - prevMaousePoint.Y );

			moveX = (int)moveX + hScroll.Value;
			moveY = (int)moveY + vScroll.Value;
			if( moveX >= 0 && hScroll.Maximum > moveX )
				hScroll.Value = moveX;

			if( moveY >= 0 && vScroll.Maximum > moveY )
				vScroll.Value = moveY;

			this.prevMaousePoint = location;*/

			//とりあえず、マウスを端にもってくとスクロールバーを移動させる
			var scale = editor.Scale;
			var width = Screen.Width;
			var height = Screen.Height;
			var chipSize = stage.ChipSize * scale;
			var row = (int)Math.Floor(e.Location.Y / chipSize);
			var column = (int)Math.Floor(e.Location.X / chipSize);




			this.currentMousePoint.Y = row + vScroll.Value;
			this.currentMousePoint.X = column + hScroll.Value;
			if (e.Button == MouseButtons.Right || e.Button == MouseButtons.Left)
			{

				if (e.X > Screen.Width - 16)
				{
					if (hScroll.Value <= hScroll.Maximum - 1)
						hScroll.Value += 1;

				}
				if (e.X < 16)
				{
					if (hScroll.Value >= hScroll.Minimum + 1)
						hScroll.Value -= 1;

				}
				if (e.Y > Screen.Height - 16)
				{
					if (vScroll.Value <= vScroll.Maximum - 1)
						vScroll.Value += 1;

				}
				if (e.Y < 16)
				{
					if (vScroll.Value >= vScroll.Minimum + 1)
						vScroll.Value -= 1;

				}
			}
   		Refresh();
		}

		private void Screen_MouseUp( object sender , MouseEventArgs e ) {
			/*if( e.Button != MouseButtons.Right )
				return;
			this.isDragging = false;*/
			if (e.Button == MouseButtons.Right)
			{
				var scale = editor.Scale;
				var width = Screen.Width;
				var height = Screen.Height;
				var chipSize = stage.ChipSize * scale;
				var row = (int)Math.Floor(e.Location.Y / chipSize);
				var column = (int)Math.Floor(e.Location.X / chipSize);

				afterMousePoint = new Point(row + vScroll.Value, column + hScroll.Value);
				//MessageBox.Show(this.prevMousePoint.X + " " + this.prevMousePoint.Y + " " + this.afterMousePoint.X + " " + this.afterMousePoint.Y + " ");
				OnDragGrid(this.prevMousePoint.X, this.prevMousePoint.Y, this.afterMousePoint.X, this.afterMousePoint.Y,this.isCopying);
				isDragging = false;
			}

		}
		#endregion

		private void hScroll_ValueChanged( object sender , EventArgs e ) {
			Refresh();
		}

		private void Screen_Click(object sender, EventArgs e)
		{

		}


		private void vScroll_Scroll(object sender, ScrollEventArgs e)
		{

		}

		private void hScroll_Scroll(object sender, ScrollEventArgs e)
		{

		}
		#region 描画

		public void OnPaint(object sender, PaintEventArgs e)
		{
			// デザイナで表示するためにnullチェックをする
			// TODO MapDataとEditorDataにもnullオブジェクトを導入する
			if (stage == null)
				return;
			if (editor == null)
				return;

			ClearBackground(e.Graphics);

			var orderedLayerList = GetOrderedLayerList();
			orderedLayerList.ForEach(layer => DrawChipImage(e.Graphics, layer));

			var colorFrameLayerList = GetColorFrameLayerList();
			colorFrameLayerList.ForEach(layer => DrawChipFrame(e.Graphics, layer));

			if (editor.VisibleGrid)
			{
				DrawChipLine(e.Graphics);
			}
			if (isDragging == true)
			{
				var  x = this.prevMousePoint.X - vScroll.Value;
				if (x < 0) x = 0;
				var y = this.prevMousePoint.Y - hScroll.Value;
				if (y < 0) y = 0;
				var width = this.currentMousePoint.X - hScroll.Value - y;
				if (width < 0) width= 0;
				var height = this.currentMousePoint.Y - vScroll.Value-x;
				if (height < 0) height = 0;
				e.Graphics.DrawRectangle(Pens.Red, y * 32, x * 32, width* 32, height * 32);
			}


		}

		private void ClearBackground(Graphics g)
		{
			var brush = new SolidBrush(BackColor);

			var rect = g.VisibleClipBounds;
			g.FillRectangle(brush, rect);
			brush.Dispose();
		}

		private void DrawChipImage(Graphics graphics, LayerNotify layer)
		{
			var chipsetImage = layer.ChipsetImage;

			var scale = editor.Scale;
			var chipSize = stage.ChipSize * scale;

			for (int i = vScroll.Value; i < layer.Map.GetLength( 0 ) && vScroll.Value + Screen.Height / chipSize > i; i++)
			{
				for (int j = hScroll.Value; j < layer.Map.GetLength( 1 ) && hScroll.Value + Screen.Width / chipSize > j; j++)
				{
					var chip = layer.Map[ i , j ];
					if (chip.Index > 0)
					{
						var x = (j - hScroll.Value) * chipSize;
						var y = (i - vScroll.Value) * chipSize;
						var image = chipsetImage[chip.Index];
						graphics.DrawImage(image, x, y, chipSize, chipSize);
					}
				}
			}
		}

		private void DrawChipFrame(Graphics graphics, LayerNotify layer)
		{
			var color = editor.GetLayerSetting(layer).FrameColor;
			var penmWidth = 3;
			var pen = new Pen(color, penmWidth);

			var scale = editor.Scale;
			var chipSize = stage.ChipSize * scale;

			for (int i = 0; i < layer.Map.GetLength( 0 ) ; i++)
			{
				for (int j = 0; j < layer.Map.GetLength( 1 ); j++)
				{
					var chip = layer.Map[ i , j ];
					if (chip.Index > 0) {
						var x = j * chipSize;
						var y = i * chipSize;
						graphics.DrawRectangle(pen, x + 1, y + 1, chipSize - 2, chipSize - 2);
					}
				}
			}
			pen.Dispose();
		}

		private void DrawChipLine(Graphics g)
		{
			var color = editor.GridColor;
			var pen = new Pen(color);

			var scale = editor.Scale;
			var gridSize = editor.GridSize * scale;

			var width = Screen.Width;
			var height = Screen.Height;

			for (float y = 0; y < height; y += gridSize)
			{
				g.DrawLine(pen, 0, y, width, y);
			}
			for (float x = 0; x < width; x += gridSize)
			{
				g.DrawLine(pen, x, 0, x, height);
			}
			pen.Dispose();
		}

		public List<LayerNotify> GetOrderedLayerList()
		{
			var layerList = stage.LayerList.Where(
				layer => editor.GetLayerSetting(layer).Visible).ToList();

			var currentLayer = editor.CurrentLayer;
			var currentViewerSetting = editor.GetCurrentLayerSetting();
			if (currentViewerSetting.Visible &&
				currentViewerSetting.Foreground)
			{
				layerList.Remove(currentLayer);
				layerList.Add(currentLayer);
			}
			if (currentViewerSetting.OtherHide)
			{
				layerList.Clear();
				if (currentViewerSetting.Visible)
				{
					layerList.Add(currentLayer);
				}
			}

			return layerList;
		}

		public List<LayerNotify> GetColorFrameLayerList()
		{
			var layerList = GetOrderedLayerList();
			var colorFrameList = layerList.Where(
				layer => editor.GetLayerSetting(layer).FrameColor != Color.Empty).ToList();

			return colorFrameList;
		}
		#endregion // 描画

		private void Screen_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
		{
			//MessageBox.Show("");
		}

		private void Screen_MouseEnter(object sender, EventArgs e)
		{

		}

		private void Screen_MouseLeave(object sender, EventArgs e)
		{

		}

		private void vScroll_KeyDown(object sender, KeyEventArgs e)
		{
			//MessageBox.Show("");

		}




	}
}