using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace Cylinder
{
    class clCylinder
    {
        private byte[,] brgPixelsB;
        private byte[,] brgPixelsG;
        private byte[,] brgPixelsR;
        private byte[,] brgPixelsL;
        private int viI;
        private int viJ;
        private double radB;
        private double radA;
        private int radB1;
        private Form1 myForm;
        private bool fBlock = false;
        private Bitmap bmpTemp { get; set; }
        private int viWidth = 0;
        private int viHeight = 0;
        public Graphics graphFore = null;
        public Graphics graphBack = null;
        public Graphics graph = null;
        private byte[] bmpBytesSource;
        private byte[] bmpBytesFore;
        private byte[] bmpBytesBack;
        //Исходная в половину 
        private static Bitmap bmpStart = null;
        private Bitmap bitmapWorkFore { get; set; }
        private Bitmap bitmapWorkBack { get; set; }
        private Bitmap bitmapWork { get; set; }
        private Point[] ptEllipceFore;
        private Point[] ptEllipceBack;        
        public clCylinder(Form1 form1)
        {           
            myForm = form1;
        }
        internal void vNewSize()
        {
            if (fBlock) return;
            fBlock = true;
            //Берём дорогу картинки 
            string s = myForm.srgFiles[myForm.viImage];
            //Картинка из памяти помещаем в bmpTemp
            bmpTemp = new Bitmap(s);
            viWidth = bmpTemp.Width;
            viHeight = bmpTemp.Height;
            while (viWidth % 2 != 0)
            {
                viWidth--;
            }

            if (viWidth < 20 || viHeight < 20)
            {
                bmpTemp.Dispose();
                return;
            }
            bmpStart = new Bitmap(viWidth, viHeight);
            graphFore = Graphics.FromImage(bmpStart);
            graphFore.DrawImage(bmpTemp, new Rectangle(0, 0, viWidth, viHeight), 0, 0, bmpTemp.Width, bmpTemp.Height, GraphicsUnit.Pixel);
            //Освобождаем картинку в памяти 
            bmpTemp.Dispose();
            graphFore.Dispose();
            //Готовим массив ссылок на байты ARJB картинки
            bmpBytesSource = new Byte[viWidth * viHeight * 4];
            BitmapData bmpTempDataStart = bmpStart.LockBits(new Rectangle(0, 0, viWidth, viHeight), ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);
            unsafe
            {
                try
                {
                    //Забираем все ссылки на байты картинки 
                    Marshal.Copy(bmpTempDataStart.Scan0, bmpBytesSource, 0, viWidth * viHeight * 4);
                }
                catch (Exception)
                {
                }
                finally
                {
                    bmpStart.UnlockBits(bmpTempDataStart);
                    bmpTempDataStart = null;

                }
            }
            //Распределяем ссылки в массивы пикселей по цветам 
            brgPixelsB = new byte[viWidth, viHeight];
            brgPixelsG = new byte[viWidth, viHeight];
            brgPixelsR = new byte[viWidth, viHeight];
            brgPixelsL = new byte[viWidth, viHeight];
            //Забираем в массивы все биты источника в массивы по цветам 
            for (int i = 0; i < viWidth; i++)
            {
                for (int j = 0; j < viHeight; j++)
                {
                    brgPixelsB[i, j] = bmpBytesSource[i * 4 + (j * viWidth * 4)];
                    brgPixelsG[i, j] = bmpBytesSource[i * 4 + (j * viWidth * 4) + 1];
                    brgPixelsR[i, j] = bmpBytesSource[i * 4 + (j * viWidth * 4) + 2];
                    brgPixelsL[i, j] = bmpBytesSource[i * 4 + (j * viWidth * 4) + 3];

                }
            }
            //Задаём радиусы элипсов для отрисовки верхней и нижней части вращающегося цилиндра
            //Крышка и донышко 
            radB = bmpStart.Width / 2 / 20;            
            radA = Math.Sqrt(((double)bmpStart.Width * (double)bmpStart.Width / (2 * (Math.PI * Math.PI)) - radB * radB));            
            radB1 = (int)radB;
            //Прямоугольник залит синим цветом, внутри нарисован элипс краснам цветом
            //По красному будем определять границы в которых нужно брать пиксели 
            Bitmap bmp = new Bitmap(2 * (int)radA, 2 * radB1);            
            graphFore = Graphics.FromImage(bmp);
            graphFore.Clear(Color.Blue);
            graphFore.DrawEllipse(new Pen(Color.FromArgb(255, 0, 0), 1), 0, 0, 2 * (int)radA - 1, 2 * radB1 - 1);            
            Color cl;
            List<int> listIb = new List<int>();
            List<int> listJb = new List<int>();
            List<int> listIf = new List<int>();
            List<int> listJf = new List<int>();
            //Выбираем красные точки и их координаты помещаем 
            //в компоненты List соответственно для передней и задней частей элипса
            for (int i = 0; i < bmp.Width; i++)
            {
                for (int j = 0; j < bmp.Height; j++)
                {
                    cl = bmp.GetPixel(i, j);
                    if (cl.R == 255)
                    {
                        if (j < radB1)
                        {
                            //Координаты задней части
                            listIb.Add(i);
                            listJb.Add(j);

                        }
                        else
                        {
                            //Координаты передней части
                            listIf.Add(i);
                            listJf.Add(j);
                        }

                    }

                }
            }
            //Точки искривления для крышки и донышка передней и задней части
            //Идея, насколько надо сдвинуть каждую точку передние вниз,
            //задние поднять - тоесть изогнуть картинку по элипсу 
            ptEllipceFore = new Point[listIf.Count];
            for (int i = 0; i < listIf.Count; i++)
            {
                ptEllipceFore[i].X = listIf[i];
                ptEllipceFore[i].Y = -(listJf[i] - (int)radB);
            }
            ptEllipceBack = new Point[listIb.Count];
            for (int i = listIb.Count - 1; i >= 0; i--)
            {
                try
                {
                    ptEllipceBack[i].X = listIb[i];
                    ptEllipceBack[i].Y = (int)radB - listJb[i];
                }
                catch (Exception)
                {

                }
            }
            //Создаём классы для рисования на передней  bitmapWorkFore - graphFore, 
            //bitmapWorkBack - graphBack и на итоговом рисунке bitmapWork - graph            
            bitmapWorkFore = new Bitmap(2 * (int)radA, viHeight + 2 * radB1);
            bitmapWorkBack = new Bitmap(2 * (int)radA, viHeight + 2 * radB1);
            bitmapWork = new Bitmap(2 * (int)radA, viHeight + 2 * radB1);
            graphFore = Graphics.FromImage(bitmapWorkFore);
            graphBack = Graphics.FromImage(bitmapWorkBack);
            graph = Graphics.FromImage(bitmapWork);
            
            //Параметры приложения 
            myForm.WindowState = System.Windows.Forms.FormWindowState.Normal;
            myForm.Width = bmpStart.Width;
            myForm.Height = bmpStart.Height+200;
            myForm.Left = (myForm.screen.Width - myForm.Width) / 2;
            myForm.Top = (myForm.screen.Height - myForm.Height) / 2;
            myForm.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage;           
            myForm.pictureBox1.BackColor = myForm.backColor;
            viI = 0;
            viJ = 0;
            fBlock = false;
        }

        internal void LineTick()
        {
            if (fBlock) return;
            fBlock = true;
            graphFore.Clear(myForm.backColor);          
            int W = 2 * (int)radA;
            //Массивы пикселей для передней и задней части рисунка
            bmpBytesFore = new byte[W * 4 * (viHeight + 2 * radB1)];
            bmpBytesBack = new byte[W * 4 * (viHeight + 2 * radB1)];
            int viFrom1 = 0;
            int viTo1 = 0;
            //Забираем пиксели в массивы bmpBytesBack для части рисунка на заднем плане
            //Вращение определяет viJ, которое в конце функции  увеличивается (viJ++) 
            for (int i = W - 1; i >= 0; i--)
            {
                if (viWidth - 1 - i - viJ < 0)
                {
                    viJ = 0;
                }                
                for (int j = viHeight - 1 + radB1; j >= -radB1; j--)
                {
                    try
                    {
                        viTo1 = (W-i) * 4 + ((j + radB1) * W) * 4;
                        //Учёт кривизны
                        viFrom1 = j + ptEllipceBack[i].Y;
                        if (viFrom1 < 0)
                        {
                            continue;
                        }
                        if (viFrom1 >= bitmapWorkBack.Height - 2 * radB1)
                        {
                            continue;
                        }

                        bmpBytesBack[viTo1] = brgPixelsB[viWidth - 1 - i - viJ, viFrom1];
                        bmpBytesBack[viTo1 + 1] = brgPixelsG[viWidth - 1 - i - viJ, viFrom1];
                        bmpBytesBack[viTo1 + 2] = brgPixelsR[viWidth - 1 - i - viJ, viFrom1];
                        bmpBytesBack[viTo1 + 3] = brgPixelsL[viWidth - 1 - i - viJ, viFrom1];
                    }
                    catch (Exception)
                    {                       
                    }                 
                }
            }
            //Забираем пиксели в массивы bmpBytesBack для части рисунка на заднем плане
            //Вращение определяет viI, которое в конце функции  увеличивается (viJ++) 
            for (int i = 0; i < W; i++)
            {
                for (int j = 0; j < viHeight + radB1; j++)
                {
                    try
                    {
                        viTo1 = i * 4 + ((j + radB1) * W) * 4;
                        //Учёт кривизны
                        viFrom1 = j + ptEllipceFore[i].Y;
                        if (viFrom1 < 0)
                        {
                            continue;
                        }
                        if (viFrom1 >= bitmapWorkFore.Height - 2 * radB1)
                        {
                            continue;
                        }
                        bmpBytesFore[viTo1] = brgPixelsB[(i + viI) % viWidth, viFrom1];
                        bmpBytesFore[viTo1 + 1] = brgPixelsG[(i + viI) % viWidth, viFrom1];
                        bmpBytesFore[viTo1 + 2] = brgPixelsR[(i + viI) % viWidth, viFrom1];
                        bmpBytesFore[viTo1 + 3] = brgPixelsL[(i + viI) % viWidth, viFrom1];
                    }
                    catch (Exception)
                    {
                        
                    }
                }
            }

            //Перенос необходимых байтов в bitmapWorkBack 
            BitmapData bitmapData = bitmapWorkBack.LockBits(new Rectangle(0, 0, bitmapWorkBack.Width, bitmapWorkBack.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb);
            unsafe
            {
                Marshal.Copy(bmpBytesBack, 0, bitmapData.Scan0, bmpBytesFore.Length);

            }
            bitmapWorkBack.UnlockBits(bitmapData);
            //Перенос необходимых байтов в bitmapWorkFore 
            bitmapData = bitmapWorkFore.LockBits(new Rectangle(0, 0, bitmapWorkFore.Width, bitmapWorkFore.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb);
            unsafe
            {
                Marshal.Copy(bmpBytesFore, 0, bitmapData.Scan0, bmpBytesFore.Length);

            }
            bitmapWorkFore.UnlockBits(bitmapData);
            //Перед отрисовкой определяем пиксели прозрачности 
            Color transparentcolor = bitmapWorkFore.GetPixel(0, 0);
            ImageAttributes imageattributes = new ImageAttributes();
            imageattributes.SetColorKey(transparentcolor, transparentcolor);
            //Отрисовка задней части рисунка bitmapWorkBack на bitmapWork
            graph.DrawImage(bitmapWorkBack, new Rectangle(0, 0, bitmapWorkBack.Width, bitmapWorkBack.Height),
                    0, 0, bitmapWorkBack.Width, bitmapWorkBack.Height, GraphicsUnit.Pixel, imageattributes);
            //Отрисовка передней части bitmapWorkFore поверх задней части на  bitmapWork 
            graph.DrawImage(bitmapWorkFore, new Rectangle(0, 0, bitmapWorkBack.Width, bitmapWorkFore.Height),
                   0, 0, bitmapWorkFore.Width, bitmapWorkFore.Height, GraphicsUnit.Pixel, imageattributes);
            myForm.pictureBox1.BackColor = bitmapWorkFore.GetPixel(0, 0);            
            //Публикация рисунка 
            myForm.pictureBox1.Image = bitmapWork;            
            viI++;
            viJ++;
            if (viI >= 1000 * viWidth) viI = 0;
            fBlock = false;
        }
    }
}
