using System;
using System.Text.RegularExpressions;

namespace NcProgramManager
{
    internal class FanucProgram
    {

        private static Regex titleRegex = new Regex(@"(\b[O]+\d{4}\b)|(\b\d{4}\b)"); //cerca per O#### o #### dove # è un carattere numerico
        private static Regex oldTitleRegex = new Regex(@"\<((\b[O]+\d{4}\b)|(\b\d{4}\b))\>");//cerca per <O####> o #### dove # è un carattere numerico
        private static Regex titleAndCommentRegex = new Regex(@"((\b[O]+\d{4}\b)|(\b\d{4}\b))+(\n|(\s*\((.*?)\)))");
        private static Regex commentRegex = new Regex(@"\((.*?)\)");

        private const string programStringName = "program:";
        private const string toolOffsetDataName = "tooloffsetdata:";
        private const string workZeroOffsetDataName = "workzerooffset:";

        private static Regex programRegex = new Regex(@"program:\{\n((?:.*?|\n)*?)\}");
        private static Regex toolOffsetDataRegex = new Regex(@"tooloffsetdata:\{\n((?:.*?|\n)*?)\}");
        private static Regex workZeroOffsetDataRegex = new Regex(@"workzerooffset:\{\n((?:.*?|\n)*?)\}");


        public String Program;
        public String ToolOffsetData;
        public String WorkZeroOffsetData;

        public void setValue(String value, FILE_TYPE type)
        {
            switch (type)
            {
                case FILE_TYPE.CNC_PROGRAM: Program = value; break;
                case FILE_TYPE.TOOL_OFFEST_DATA: ToolOffsetData = value; break;
                case FILE_TYPE.WORK_ZERO_OFFSET: WorkZeroOffsetData = value; break;
            }
            Standardize();
        }
        public enum FILE_TYPE : short
        {
            CNC_PROGRAM = 0,
            TOOL_OFFEST_DATA = 1,
            WORK_ZERO_OFFSET = 5
        }

        public FanucProgram(string _program)
        {
            var programMatch = programRegex.Match(_program);
            if (programMatch.Success)
            {
                Program = programMatch.Value.Substring(9);
                Program = Program.Trim('}');
            }
            else Program = null;

            var toolOffsetDataMatch = toolOffsetDataRegex.Match(_program);
            if (toolOffsetDataMatch.Success)
            {
                ToolOffsetData = toolOffsetDataMatch.Value.Substring(16);
                ToolOffsetData = ToolOffsetData.Trim('}');
            }
            else ToolOffsetData = null;

            var workZeroOffsetDataMatch = workZeroOffsetDataRegex.Match(_program);
            if (workZeroOffsetDataMatch.Success)
            {
                WorkZeroOffsetData = workZeroOffsetDataMatch.Value.Substring(16);
                WorkZeroOffsetData = WorkZeroOffsetData.Trim('}');
            }
            else WorkZeroOffsetData = null;

            //Old program format with ONLY the program outside of brachets
            if (!programMatch.Success && !toolOffsetDataMatch.Success && !workZeroOffsetDataMatch.Success)
            {
                Program = _program;
            }

            Standardize();
        }

        public string GetProgramTitle()
        {
            var match = titleRegex.Match(Program);
            if (match.Success)
            {
                return match.Value;
            }
            throw new InvalidOperationException("Regex could not match any program title in the file, check program formatting for a O#### patern");
        }

        public static string GetProgramTitle(string program)
        {
            var match = titleRegex.Match(program);
            if (match.Success)
            {
                return match.Value;
            }
            throw new InvalidOperationException("Regex could not match any program title in the file, check program formatting for a O#### patern");
        }

        public string GetProgramComment()
        {
            var groupMatch = titleAndCommentRegex.Match(Program);
            if (groupMatch.Success)
            {
                Match match = commentRegex.Match(groupMatch.Value);
                if (match.Success)
                {
                    return match.Value.Trim(new Char[] { '(', ')' });
                }
            }
            return "";
        }

        private void Standardize()
        {
            //removes % from start of file
            int percent_index = Program.IndexOf('%');
            if (percent_index == 0)
            {
                Program = Program.Remove(percent_index, 1);
            }
            //removes % from start of file
            if (ToolOffsetData != null)
            {
                percent_index = ToolOffsetData.IndexOf('%');
                if (percent_index == 0)
                {
                    ToolOffsetData = ToolOffsetData.Remove(percent_index, 1);
                }
            }
            //removes % from start of file
            if (WorkZeroOffsetData != null)
            {
                percent_index = WorkZeroOffsetData.IndexOf('%');
                if (percent_index == 0)
                {
                    WorkZeroOffsetData = WorkZeroOffsetData.Remove(percent_index, 1);
                }
            }

            //replace <O####> with O####
            var oldTitleMatch = oldTitleRegex.Match(Program);
            if (oldTitleMatch.Success)
            {
                Program = Program.Replace(oldTitleMatch.Value, GetProgramTitle());
            }

            //adds \n to file beginning if missing
            int new_line_index = Program.IndexOf('\n');
            if (new_line_index != 0)
            {
                Program = Program.Substring(Program.IndexOf(GetProgramTitle()));
                Program = Program.Insert(0, "\n");
            }
        }

        //fornendo un programma CNC e un commento in INPUT restituisce un output nel formato corretto per il caricamento con la funzione download
        public void ChangeComment(string comment)
        {
            string previousComment = GetProgramComment();
            if (previousComment != "")
                Program = Program.Replace(previousComment, "(" + comment + ")");
            else
            {
                Program = Program.Replace(GetProgramTitle(), GetProgramTitle() + "(" + comment + ")");
            }

        }

        public void ChangeProgName(string progName)
        {
            var match = titleRegex.Match(progName);
            if (!match.Success)
            {
                throw new InvalidOperationException("New Program Name does Not respect the proper formattings O#### patern");
            }
            Program.Replace(GetProgramTitle(), progName);
        }

        public void ChangeNameAndComment(string progName, string comment)
        {
            ChangeProgName(progName);
            ChangeComment(comment);
        }

        public override string ToString()
        {
            return programStringName + "{\n" + Program + "\n}\n" +
                toolOffsetDataName + "{\n" + ToolOffsetData + "\n}\n" +
                workZeroOffsetDataName + "{\n" + WorkZeroOffsetData + "\n}\n";
        }
    }
}
