using System;
using System.IO;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace S04_Projet
{
    public class MyImage
    {
        private int fileInfoHeaderSize;
        private int width;
        private int height;
        private int colorPlanesNb;
        private int bitsPerPixel;
        private int compressionMethod;
        private int imgSize;
        private int horizontalRes;
        private int VerticalRes;
        private int nbColor;
        private int nbImportantColor;
        private string format;
        private int fileSize;
        private int offset;
        private Pixel[,] Pixels;

        public MyImage(string path)
        {
            byte[] file = new byte[0];
            try
            {
                file = File.ReadAllBytes(path);
            }
            catch (Exception e)
            {
                Console.WriteLine("Une erreur est survenue : " + e.Message);
            }
            finally
            {
                #region Header
                format = (char)file[0] + "" + (char)file[1];
                fileSize = EndianToInt(file, 2, 6);
                offset = EndianToInt(file, 10, 14);
                Console.WriteLine("Format : {0}\nTaille : {1} octets", format, fileSize);
                Console.WriteLine("Offset : " + offset);
                #endregion

                #region File info
                fileInfoHeaderSize = EndianToInt(file, 14, 18);
                width = EndianToInt(file, 18, 22);
                height = EndianToInt(file, 22, 26);
                colorPlanesNb = EndianToInt(file, 26, 28);
                bitsPerPixel = EndianToInt(file, 28, 30);
                compressionMethod = EndianToInt(file, 30, 34);
                imgSize = EndianToInt(file, 34, 38);
                horizontalRes = EndianToInt(file, 38, 42);
                VerticalRes = EndianToInt(file, 42, 46);
                nbColor = EndianToInt(file, 46, 50);
                nbImportantColor = EndianToInt(file, 50, 54);

                Console.WriteLine("File info header size : {0} octets", fileInfoHeaderSize);
                Console.WriteLine("Size : {0}x{1}px", width, height);
                Console.WriteLine("Color planes number : " + colorPlanesNb);
                Console.WriteLine("Bits per pixel : " + bitsPerPixel);
                Console.WriteLine("Compression method : " + compressionMethod);
                Console.WriteLine("Image size : {0} octets", imgSize);
                Console.WriteLine("Horizontal res : {0}\nVertical res : {1}", horizontalRes, VerticalRes);
                Console.WriteLine("Number of colors : " + nbColor);
                Console.WriteLine("Number of important colors : " + nbImportantColor);
                #endregion

                #region Pixel array
                int pixelNumber = width * height;
                Pixels = new Pixel[width, height];

                Stopwatch s = new Stopwatch();

                #region Mono
                /*s.Start();
                for (int i = 0; i < pixelNumber; i++)
                {
                    int x = i % width;
                    int y = i / width;
                    byte r = file[i * 3 + offset];
                    byte g = file[i * 3 + offset + 1];
                    byte b = file[i * 3 + offset + 2];
                    Pixels[x, y] = new Pixel(r, g, b);
                }
                s.Stop();
                Console.WriteLine(s.ElapsedMilliseconds);*/
                #endregion

                #region Parallel 
                s.Start();
                Parallel.For(0, pixelNumber, i =>
                {
                    Pixels[i % width, i / width] = new Pixel(file[i * 3 + offset], file[i * 3 + offset + 1], file[i * 3 + offset + 2]);
                });
                s.Stop();
                Console.WriteLine(s.ElapsedMilliseconds);

                #endregion

                #endregion
            }
        }

        public void FromImageToFile(string output)
        {
            int nbPixel = (height * width);
            int bytesNumber = nbPixel * 3 + offset; // Multiply by 3 because 3 bytes / pixel
            byte[] file = new byte[bytesNumber];

            #region HEADER
            // FORMAT
            ASCIIEncoding encoding = new ASCIIEncoding();
            mixArrays(file, encoding.GetBytes(format), 0);
            // FILE SIZE
            mixArrays(file, IntToEndian(fileSize), 2);
            // OFFSET
            mixArrays(file, IntToEndian(offset), 10);
            #endregion

            #region File Info Header
            mixArrays(file, IntToEndian(fileInfoHeaderSize), 14);
            mixArrays(file, IntToEndian(width), 18);
            mixArrays(file, IntToEndian(height), 22);
            mixArrays(file, IntToEndian(1), 26); // Number of colors planes
            mixArrays(file, IntToEndian(bitsPerPixel), 28);
            mixArrays(file, IntToEndian(compressionMethod), 30);
            mixArrays(file, IntToEndian(fileSize - offset), 34); // Image size TO CHANGE IF NO MORE BI_RGB
            mixArrays(file, IntToEndian(horizontalRes), 38);
            mixArrays(file, IntToEndian(VerticalRes), 42);
            mixArrays(file, IntToEndian(nbColor), 46);
            mixArrays(file, IntToEndian(0), 50); // Number of important colors
            #endregion

            #region Pixel array
            for (int i = 0; i < nbPixel; i++)
            {
                mixArrays(file, Pixels[i % width, i / width].getRGB(), 54 + (i * 3));
            }

            File.WriteAllBytes(output, file);
            #endregion
        }

        public static int EndianToInt(byte[] arr, int from, int to)
        {
            int somme = 0;
            for (int i = from; i < to; i++) somme += (arr[i] << ((i - from) * 8));
            return somme;
        }

        public static byte[] IntToEndian(int val)
        {
            byte[] endian = new byte[4];
            for (int i = 0; i < 4; i++)
                endian[i] = (byte)(val >> (i * 8));
            return endian;
        }

        public void mixArrays(byte[] arr, byte[] toAdd, int start)
        {
            for (int i = start; i < start + toAdd.Length; i++)
            {
                arr[i] = toAdd[i - start];
            }
        }
    }
}
