// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2021 Torsten Schache, Hiperscan GmbH Dresden, Germany
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;

namespace Hiperscan.Extensions.ApoIdent
{
    public class BitimapLoader
    {
        private class DoubleColour
        {
            public double A { get; set; }
            public double Gray { get; set; }
        }

        public void LoadPicture(string resource, bool big_picture = false)
        {
            try
            {
                Stream stream = null;
                if (File.Exists(resource))
                    stream = File.Open(resource, FileMode.Open);
                else
                    stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource);
               

                System.Drawing.Bitmap bitmap = new Bitmap(stream);

                if (big_picture)
                    this.LoadBigPicture(resource, bitmap);
                else
                    this.LoadSmallPicture(resource, bitmap);

                stream.Close();

            }
            catch(Exception ex)
            {
                Console.WriteLine("Cannot read bmp resource {0}: {1}", resource, ex);
                this.LoadSuccess = false;
            }
        }

        private void LoadBigPicture(string resource, Bitmap bitmap)
        {
            DoubleColour[,] pixels = new DoubleColour[bitmap.Width, bitmap.Height];

            Parallel.For(0, bitmap.Width, (x) =>
            {
                for (int y = 0; y < bitmap.Height; y++)
                {

                    System.Drawing.Color c = bitmap.GetPixel(x, y);
                    pixels[x, y] = new DoubleColour();
                    pixels[x, y].A = c.A;
                    pixels[x, y].Gray = (c.R + c.G + c.B) / 3;
                }
            });

            lock (this.PictureSync)
            {
                this.PixelPictures.Add(resource, pixels);
            }

        }

        private void LoadSmallPicture(string resource, Bitmap bitmap)
        {
            DoubleColour[,] pixels = new DoubleColour[bitmap.Width, bitmap.Height];
            for (int x = 0; x < bitmap.Width; x++)
            {
                for (int y = 0; y < bitmap.Height; y++)
                {

                    System.Drawing.Color c = bitmap.GetPixel(x, y);
                    pixels[x, y] = new DoubleColour();
                    pixels[x, y].A = c.A;
                    pixels[x, y].Gray = (c.R + c.G + c.B) / 3;
                }
            }

            lock (this.PictureSync)
            {
                this.PixelPictures.Add(resource, pixels); 
            }
        }
        
        public Gdk.Pixbuf SetIconColor(string resource, Gdk.Color new_color,   Gdk.Size size, double brightness = 0)
        {
            DoubleColour[,] values = null;
            lock (this.PictureSync)
                values = this.PixelPictures[resource];

            Bitmap bitmap = new Bitmap(values.GetLength(0), values.GetLength(1));

            using (MemoryStream ms = new MemoryStream())
            {
                for (int x = 0; x < values.GetLength(0); x++)
                {
                    for (int y = 0; y < values.GetLength(1); y++)
                    {
                        double gray = values[x, y].Gray;

                        int red = (int)(gray + (255 - gray) / 255 * (new_color.Red / 256));
                        int green = (int)(gray + (255 - gray) / 255 * (new_color.Green / 256));
                        int blue = (int)(gray + (255 - gray) / 255 * (new_color.Blue / 256));

                        System.Drawing.Color c = System.Drawing.Color.FromArgb((int)values[x, y].A, red, green, blue);
                        bitmap.SetPixel(x, y, c);
                    }
                }

                bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
                ms.Position = 0;

                return new Gdk.Pixbuf(ms, size.Width, size.Height);
            }
        }

        public Cairo.ImageSurface SetPictureColor(string resource, Gdk.Color new_color)
        {
            DoubleColour[,] values = null;
            lock (this.PictureSync)
                values = this.PixelPictures[resource];

            Bitmap bitmap = new Bitmap(values.GetLength(0), values.GetLength(1));

            Parallel.For(0, bitmap.Width, (x) =>
            {
                for (int y = 0; y < bitmap.Height; y++)
                {
                    double gray = values[x, y].Gray;

                    int red = (int)(gray + (255 - gray) / 255 * (new_color.Red / 256));
                    int green = (int)(gray + (255 - gray) / 255 * (new_color.Green / 256));
                    int blue = (int)(gray + (255 - gray) / 255 * (new_color.Blue / 256));

                    System.Drawing.Color c = System.Drawing.Color.FromArgb((int)values[x, y].A, red, green, blue);
                    bitmap.SetPixel(x, y, c);
                }
            });

            string tmp_file = Path.GetTempFileName();
            using (FileStream stream = File.Open(tmp_file, FileMode.Open))
            {
                bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
            }


            Cairo.ImageSurface surface = new Cairo.ImageSurface(tmp_file);

            try { File.Delete(tmp_file); } catch (Exception){};

            return surface;
        }

        private Dictionary<string, DoubleColour[,]> PixelPictures { get; set; } = new Dictionary<string, DoubleColour[,]>();
        public bool LoadSuccess { get; private set; } = true;
        private  object PictureSync { get; set; } = new object();
    }
}
