using Pgm;
using System;
using System.Collections.Generic;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;

namespace task_78
{
    public class Program
    {
        static void Main(string[] args) {
            Font inFont = readFont(Directory.GetCurrentDirectory() + "\\font.pgm");
            if (inFont == null)
                return;

            writeFont(inFont, "font_print.bmp");
            Bitmap testBmp = new Bitmap(Image.FromFile("test.png"));
            printToImage(testBmp, "test_print.bmp", inFont.chars.ToArray(), 128, 64);
            Console.ReadLine();
        }

        static Font readFont(string path) {
           return new Font(PgmCreator.ReadPgmFile(path));
        }

        /// <summary>
        /// Saves all Font.Char-Objects listed within the Font class into a new bmp file.
        /// </summary>
        /// <param name="font"></param>
        /// <param name="path"></param>
        static void writeFont(Font font, string path) {
            // create a new bmp with with equal dimensions to the font's data array.
            Bitmap bmp = new Bitmap(font.data.GetLength(0), font.data.GetLength(1));
            // incrementor to not draw every character into the same spot.
            int shift = 0;
            // Iterate through all characters
            foreach (Font.Char c in font.chars) {
                for (int x = 0; x < c.w; x++) {
                    for (int y = 0; y < c.h; y++) {
                        // Print every single pixel of each character
                        try {
                            bmp.SetPixel(shift + x, y, Color.FromArgb(255, c.data[x, y], c.data[x, y], c.data[x, y]));
                        } catch(Exception e) {
                            Console.WriteLine(e.Message);
                        }
                    }
                }
                // Shift Char.Width pixels to the right + 1 Pixel for Seperation.
                shift += c.w + 1;
            }
            bmp.Save(path);
            Console.WriteLine("Bitmap saved!");
        }

        /// <summary>
        /// prints a single character on a bmp at a given destination
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="c"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private static Bitmap printToImage(Bitmap bmp, Font.Char c, int x, int y) {
            for (int xx = 0; xx < c.w; xx++) {
                for (int yy = 0; yy < c.h; yy++) {
                    // Print every single pixel of given character
                    try {
                        // respect the offset here, x = target location of character, xx = current iteration of character pixel.
                        int code = c.data[xx, yy];
                        if (code != 6)
                            bmp.SetPixel(x + xx, y + yy, Color.FromArgb(255, code, code, code));
                    } catch (Exception e) {
                        Console.WriteLine(e.Message);
                    }
                }
            }
            return bmp;
        }

        /// <summary>
        /// Prints a given array of chars into a bitmap, then writes it to given destination file.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="destination"></param>
        /// <param name="chars"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public static void printToImage(Bitmap source, string destination, Font.Char[] chars, int x, int y) {
            int shift = 0;
            foreach(var c in chars) {
                source = printToImage(source, c, x + shift, y);
                shift += c.w + 1;
            }
            source.Save(destination);
            Console.WriteLine("printed a total of " + chars.Length + " characters to bmp, saved as " + destination);
        }
    }

    public class Font {
        public byte[,] data { get; private set; }
        public List<Char> chars { get; private set; }

        public Font(byte[,] data) {
            this.chars = new List<Char>();
            this.data = data;
            this.getCharacters();
        }

        /// <summary>
        /// attempts to read all characters stored in the data[,] information by splitting all characters using the grayscale value of '150' as given in the task.
        /// all readable characters will be stored in the chars-List<Char>
        /// </summary>
        private void getCharacters() {
            int i = 0, count = 0;
            // a new list, containing byte[]s to store the vertical row of pixels for each character
            List<byte[]> strip = new List<byte[]>();
            // clear the character list, just in case this font was already written before.
            chars.Clear();
            do {
                // Do we have a seperating greyscale code?
                if (data[i, 0] == 150 && i > 0) {
                    // create a new 2 dimensional byte where we can temporarily write the character into
                    byte[,] c = new byte[strip.Count, strip[0].Length];
                    for (int j = 0; j < strip.Count; j++) {
                        for (int k = 0; k < strip[0].Length; k++) {
                            c[j, k] = strip[j][k];
                        }
                    }
                    // increment the char counter
                    count++;
                    // add a new char struct to the list
                    chars.Add(new Font.Char { key = "c_" + count, data = c, w = c.GetLength(0), h = c.GetLength(1) });
                    // clear the strip storage for the next character
                    strip.Clear();
                    Console.WriteLine("Found Character " + count);
                } else {
                    // if no seperating color code was detected, we add another byte[] to the strip list
                    byte[] s = new byte[data.GetLength(1)];
                    for(int j = 0; j < s.Length; j++) {
                        s[j] = data[i, j];
                    }
                    strip.Add(s);
                }

            } while (i++ < data.GetLength(0) - 1);
        }

        public struct Char
        {
            public string key;
            public byte[,] data;
            public int w, h;
        }
    }
}
