using lib;
using QRCoder;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace lib
{
    class VarObject
    {

        const string _VAR_PATTERN = @"([\w\d\[\]/\(\)\-_]+)($|:(([\w\d\[\]/]+)($|\(([\w\d\[\]/\-:\.]+)\))))";

        public string name { get; set; }
        public string type { get; set; }
        public string format { get; set; }
        public string value { get; set; }

        public VarObject(string name, string type, string format)
        {
            this.name = name;
            this.type = type;
            if (format != null)
                if (!format.Equals(String.Empty))
                {
                    format = Utils.regexPattern(format, @"\(([\w\(\)\d\[\]/]+)\)")[0];
                }
            this.format = format;
        }

        public VarObject(string input)
        {
            List<string> var = Utils.regexPattern(input, _VAR_PATTERN);
            this.name = var[0];
            this.type = var.Count > 1 ? var[1] : null;
            if (type != null)
                if (!type.Equals(String.Empty))
                {
                    type = Utils.regexPattern(type, @"([\w\d\[\]/]+)")[0];
                }
            this.format = var.Count > 2 ? var[2] : "word";
            if (format != null)
                if (!format.Equals(String.Empty))
                {
                    format = Utils.regexPattern(format, @"\(([\w\(\)\d\[\]/\-:\.]+)\)")[0];
                }
            this.value = null;
        }

        override
        public string ToString()
        {
            return string.Format("{0}, {1}, {2}, {3}", name, type, format, value);
        }
    }
    class TextProcessing
    {

        Dictionary<string, VarObject> Vars { get; set; }
        private string str { get; set; }

        public TextProcessing(string str, char delimiter)
        {
            Console.WriteLine(str);
            Vars = new Dictionary<string, VarObject>();

            string[] tokens = str.Split(delimiter);
            foreach(string token in tokens) {
                

                try
                {
                    VarObject varObject = new VarObject(token);
                    Vars.Add(varObject.name, varObject);
                    Console.WriteLine(varObject.ToString());
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message + e.StackTrace);
                }
            }
        }

        public Dictionary<string, VarObject> ParseInput(string input, char delimiter)
        {
            int i = 0;
            string[] tokens = input.Split(delimiter);
            foreach (string key in Vars.Keys)
            {
                VarObject var = null;
                Vars.TryGetValue(key, out var);
                if (var!= null)
                {
                    var.value = tokens[i++].Trim();
                }

                // check index
                if (i >= tokens.Length)
                    break;
            }

            return Vars;
        }

        public static string ReplaceVariables(TextProcessing tp, string input, IniFile ini = null)
        {
            string encap = @"\$\{([\w:\-\(\)\d\[\]/\$\{\}]+)\}";
            Regex ItemRegex = new Regex(encap, RegexOptions.Compiled);
            foreach (Match ItemMatch in ItemRegex.Matches(input))
            {
                Console.WriteLine(ItemMatch);
                //Remove bracket ${ ... }
                string content = Utils.regexPattern(ItemMatch.ToString(), encap)[0];
                Console.WriteLine(content);
                
                /*
                Match m = Regex.Match(content, encap);
                if (m.Success)
                {
                    content = TextProcessing.ReplaceVariables(tp, content, ini);
                }
                */

                VarObject var = new VarObject(content);
                Console.WriteLine(var.ToString());

                VarObject varValue;
                if (!tp.Vars.TryGetValue(var.name, out varValue) && 
                        (
                            // Not allow for null value
                            var.type.Equals(String.Empty)
                            || var.type.Equals("word")
                            || var.type.Equals("decimal")
                            || var.type.Equals("datetime")
                        )
                    )
                {
                    throw new Exception(String.Format("Variable [{0}] not found error!", var.name));
                }
                switch (var.type)
                {
                    case "":
                    case "word":
                    case "decimal":
                        input = input.Replace(ItemMatch.ToString(), varValue.value);
                        break;
                    case "datetime":
                        if (varValue.value == "-")
                            input = input.Replace(ItemMatch.ToString(), varValue.value);
                        else
                        {
                            input = input.Replace(ItemMatch.ToString(),
                                DateTime.ParseExact(varValue.value, varValue.format, null).ToString(var.format));
                            //if (input.IndexOf(ItemMatch.ToString()) >= 0)
                            //{
                            //    input = input.Replace(ItemMatch.ToString(),
                            //        DateTime.ParseExact(varValue.value, varValue.format, null).ToString(var.format));
                            //}
                        }
                        break;
                    case "ini":
                        string section = Utils.regexPattern(var.name, @"\[([\w\d\-\(\)_]+)\]")[0];
                        string key = var.format;
                        string ini_value = ini == null ? "" : ini.Read(key, section);

                        input = input.Replace(ItemMatch.ToString(), TextProcessing.ReplaceVariables(tp, ini_value, ini));
                        break;
                    case "qrcodePNGbase64":

                        Match m = Regex.Match(content, encap);
                        if (m.Success)
                        {
                            content = TextProcessing.ReplaceVariables(tp, content, ini);
                            //Remove :qrcode
                            content = Utils.regexPattern(content, @"([\w\d\|\-\s\(\)_\-:,/\.#]+):qrcodePNGbase64")[0];

                            QRCodeGenerator qrGenerator = new QRCodeGenerator();
                            QRCodeData qrCodeData = qrGenerator.CreateQrCode(content, QRCodeGenerator.ECCLevel.Q);
                            QRCode qrCode = new QRCode(qrCodeData);

                            Bitmap qrCodeImage = qrCode.GetGraphic(20);

                            MemoryStream memoryStream = new MemoryStream();
                            qrCodeImage.Save(memoryStream, ImageFormat.Png);

                            // converting to base64
                            memoryStream.Position = 0;
                            byte[] byteBuffer = memoryStream.ToArray();

                            memoryStream.Close();

                            string base64String = Convert.ToBase64String(byteBuffer);
                            byteBuffer = null;

                            input = input.Replace(ItemMatch.ToString(), base64String);
                        }
                        break;
                }
            }

            return input;
        }
    }
}
