using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using nft.core.game;
using System.IO;
using System.Diagnostics;
using System.Drawing.Imaging;
using nft.util;
using System.Drawing.Drawing2D;
using System.Collections;
using System.Runtime.InteropServices;
using nft.framework.drawing;

using Generator = nft.contributions.game.CtbImageImportTerrainGenerator;
namespace nft.ui.core {
    public partial class TerrainFromBitmapSetup : UserControl, IMapGeneSetupPanel {
        private ImageRef imgSelected = null;
        private Image imgThumb = null;
        private IEnumerator previewJob = null;
        private ImageColorFormat imgFormat = null;

        public TerrainFromBitmapSetup() {
            InitializeComponent();
            Disposed += new EventHandler(OnDisposed);
        }

        public string ImagePath {
            get { return tbImgPath.Text; }
        }

        public ColorChannel ColorChannel {
            get { return (ColorChannel)cbColChannel.SelectedIndex; }
        }

        void OnDisposed(object sender, EventArgs e) {
            ClearSelectedImage();
        }

        protected override void OnLoad(EventArgs e) {
            base.OnLoad(e);
            sizeRadio_ChackedChanged(this, e);
        }

        protected override void OnEnter(EventArgs e) {
            base.OnEnter(e);
            Application.Idle += OnIdle;
        }

        protected override void OnLeave(EventArgs e) {
            base.OnLeave(e);
            Application.Idle -= OnIdle;
        }

        void OnIdle(object sender, EventArgs e) {
            if (previewJob != null) {
                if (!previewJob.MoveNext()) {
                    previewJob = null; // job finished
                }
                picPreview.Refresh();
            }
        }

        IEnumerable CreatePreviewJob() {
            // NOTE: imgThum.PixelFormat must be Format32bppArgb.
            Bitmap work = new Bitmap(imgThumb);
            picPreview.Image = work;
            PixelFormat pf = work.PixelFormat;
            int h = imgThumb.Height;
            int w = imgThumb.Width;
            int water = (int)numSeaLevel.Value * 255 / 1000;
            int shift = 8*cbColChannel.SelectedIndex;
            for (int y = 0; y < h; y++) {
                Rectangle r = new Rectangle(0, y, w, 1);
                BitmapData data = work.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
                IntPtr ptr = data.Scan0;
                int w2 = w << 2;
                for (int x = 0; x < w2; x+=4) {
                    Int32 c = Marshal.ReadInt32(ptr,x);
                    c >>= shift;
                    c &= 0xff;
                    if (c <= water) {
                        Int32 c2 = 0xff + c - water;
                        c2 += (c2 << 6) & 0x3f00;
                        c = (Int32)(0xff000000 + c2);
                        //c = (Int32)(0xff0000ff + (c-water));
                    } else {
                        c -= water;
                        if (c < 128) {
                            c = (Int32)(0xff008000 + (c << 8));
                        } else {
                            c = (Int32)(0xfe00ff00 + (c << 17));
                        }
                    }
                    Marshal.WriteInt32(ptr, x, c);                    
                }
                work.UnlockBits(data);
                //yield return null;
            }
            yield break;
        }

        protected void OnSelectedImageChanged(string path){
            if (File.Exists(path)) {
                ClearSelectedImage();
                ImageRef img = null;
                try {
                    img = ImageRef.FromFile(path);
                } catch (Exception) {
                    MessageBox.Show(
                        this, "ファイルを画像として開けません\n" + path, "読み込み画像", MessageBoxButtons.OK
                        );
                }
                if (img != null) {
                    SetSelectedImage(img);
                }
            } else {
                MessageBox.Show(
                    this, "ファイルが見つかりません\n" + path, "読み込み画像", MessageBoxButtons.OK
                    );
            }
        }

        protected virtual void ClearSelectedImage() {
            if (imgSelected != null) {
                imgSelected.ReleaseRef();
                imgSelected = null;
                imgFormat = null;
            }
            if (imgThumb != null) {
                imgThumb.Dispose();
                imgThumb = null;
            }
            tbImgSize.Text = "";
            tbImgColormap.Text = "";
            picPreview.Image = null;
        }

        protected virtual void SetSelectedImage(ImageRef imgref) {
            imgSelected = imgref;
            Image img = imgref.AddRef();
            imgFormat = ImageUtil.GetColorFormat(img);
            tbImgSize.Text = string.Format("{0}x{1}", img.Size.Width, img.Size.Height);
            tbImgColormap.Text = imgFormat.Name;
            imgThumb = ImageUtil.CreateThumbnail
                (img, picPreview.ClientSize, PixelFormat.Format32bppArgb, InterpolationMode.Bicubic);
            picPreview.Image = imgThumb;
            cbColChannel.Enabled = !imgFormat.GrayScaled;
            if (imgFormat.AlphaBits > 1) {
                if (cbColChannel.Items.Count == 3)
                    cbColChannel.Items.Add("[A]ｱﾙﾌｧ");
            } else {
                if (cbColChannel.Items.Count == 4)
                    cbColChannel.Items.RemoveAt(3);
            }
            if (cbColChannel.SelectedIndex < 0)
                cbColChannel.SelectedIndex = 0;
            numSizeX.Maximum = img.Width * 2;
            numSizeX.Value = img.Width * numSizeRatio.Value / 100;
            numSizeY.Maximum = img.Height * 2;
            numSizeY.Value = img.Height * numSizeRatio.Value / 100;
            previewJob = CreatePreviewJob().GetEnumerator();
        }

        private void tbImgPath_KeyUp(object sender, KeyEventArgs e) {
            if (e.KeyCode == Keys.Return) {
                e.Handled = true;
                string path = tbImgPath.Text;
                OnSelectedImageChanged(path);
            }
        }

        private void btnBrowse_Click(object sender, EventArgs e) {
            string path = tbImgPath.Text;
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.CheckFileExists = true;
            dlg.Title = "画像の選択";
            dlg.Filter = "Image Files(*.bmp;*.jpg;*.gif,*.png)|*.BMP;*.JPG;*.GIF;*.PNG;*.JPEG|All files (*.*)|*.*";
            dlg.FilterIndex = 0;
            if (path.Trim().Length > 0) {
                dlg.InitialDirectory = path;
                dlg.FileName = path;
            } else {
                dlg.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
                dlg.FileName = "";
            }
            if (dlg.ShowDialog(this) == DialogResult.OK) {
                path = dlg.FileName;
                tbImgPath.Text = path;
                OnSelectedImageChanged(path);
            }            
        }

        private void sizeRadio_ChackedChanged(object sender, EventArgs e) {
            bool b = rbSizeDirect.Checked;
            numSizeRatio.Enabled = !b;
            numSizeX.Enabled = b;
            numSizeY.Enabled = b;
        }

        private void numSizeRatio_ValueChanged(object sender, EventArgs e) {
            if (numSizeRatio.Enabled) {
                Image img = imgSelected.AddRef();
                numSizeX.Value = img.Width * numSizeRatio.Value / 100;
                numSizeY.Value = img.Height * numSizeRatio.Value / 100;
                imgSelected.ReleaseRef();
            }
        }

        private void numSizeXY_ValueChanged(object sender, EventArgs e) {
            if (!numSizeRatio.Enabled) {
                Image img = imgSelected.AddRef();
                if (sender == numSizeX) {
                    numSizeRatio.Value = numSizeX.Value * 100 / img.Width;
                } else {
                    numSizeRatio.Value = numSizeY.Value * 100 / img.Height;
                }
                imgSelected.ReleaseRef();
            }
        }

        private void numSeaLevel_ValueChanged(object sender, EventArgs e) {
            previewJob = CreatePreviewJob().GetEnumerator();
        }

        private void cbColChannel_SelectedIndexChanged(object sender, EventArgs e) {
            previewJob = CreatePreviewJob().GetEnumerator();
        }


        #region IMapGeneSetupPanel メンバ

        public void UpdateView(ParamSet param) {
            throw new NotImplementedException();
        }

        public void RequestCurrentParams(ref ParamSet param) {
            double hrate = (double)numSizeRatio.Value / 100.0;
            double vrate = (double)numHeightRatio.Value / 100.0;
            param.Set(Generator.KEY_SRCIMG_PATH, ImagePath);
            param.Set(Generator.KEY_COLOR_CHANNEL, Enum.GetName(typeof(ColorChannel),this.ColorChannel));
            param.Set(Generator.KEY_HORZ_SCALING, hrate);
            param.Set(Generator.KEY_VERT_SCALING, vrate);
            param.Set(Generator.KEY_SEA_LEVEL_HEIGHT, numSeaLevel.Value );
        }

        #endregion
    }
}
