
using FastColoredTextBoxNS;
using MenuGenerator.Generators;
using MenuGenerator.Models;
using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Xml.Linq;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace MenuGenerator.Forms
{
    public partial class MainForm : Form
    {
        // Корневой элемент дерева меню
        private MenuNode _rootItem = new MenuNode { Name = "Root", Type = MenuItemType.FOLDER };
        // Флаг изменений
        private bool _isDirty = false;
        // Путь к текущему проекту
        private string _projectPath = string.Empty;

        public MainForm()
        {
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            // Заполняем ComboBox типов
            cBoxTypeLCD.Items.AddRange(new object[] { "ST7735", "ST7789", "ILI9341" });
            cBoxTypeCPU.Items.AddRange(new object[] { "STM32F103", "STM32F4xx", "AVR" });
            cBoxTypeControl.Items.AddRange(new object[]
                { "Энкодер", "5 кнопок", "4 кнопки", "3 кнопки" });

            cBoxTypeLCD.SelectedIndex = 0;
            cBoxTypeCPU.SelectedIndex = 0;
            cBoxTypeControl.SelectedIndex = 0;

            tBoxNameMenu.Text = "menu";
            tBoxCountPMenu.Text = "5";

            RefreshTree();
            UpdateProjectInfo();
        }

        // ----------------------------------------------------------------
        // Обновление дерева TreeView из модели
        // ----------------------------------------------------------------
        private void RefreshTree()
        {
            treeView1.BeginUpdate();
            treeView1.Nodes.Clear();
            var rootNode = BuildTreeNode(_rootItem);
            treeView1.Nodes.Add(rootNode);
            treeView1.ExpandAll();
            treeView1.EndUpdate();

            // Обновляем счётчик узлов
            int count = CountNodes(_rootItem) - 1; // не считаем root
            lbCountNode.Text = count.ToString();
            lbNameMenu.Text = tBoxNameMenu.Text;
        }

        private TreeNode BuildTreeNode(MenuNode item)
        {
            var node = new TreeNode(item.Name) { Tag = item };
            foreach (var child in item.Children)
                node.Nodes.Add(BuildTreeNode(child));
            return node;
        }

        private int CountNodes(MenuNode item)
        {
            int count = 1;
            foreach (var child in item.Children)
                count += CountNodes(child);
            return count;
        }

        // ----------------------------------------------------------------
        // Выбор узла в дереве — отображаем свойства
        // ----------------------------------------------------------------
        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (e.Node?.Tag is MenuNode item)
                ShowNodeProperties(item);
        }

        private void ShowNodeProperties(MenuNode item)
        {
            tBoxNameNode.Text = item.Name;
            tBoxTypeNode.Text = ((int)item.Type).ToString();
            tBoxVolNode.Text = item.Value.ToString();
            tBoxMinNode.Text = item.MinValue.ToString();
            tBoxMaxNode.Text = item.MaxValue.ToString();
            tBoxStepNode.Text = item.Step.ToString();

            // Вычисляем индексы для отображения (flatten)
            var flat = MenuCodeGenerator.Flatten(_rootItem);
            int idx = flat.IndexOf(item);
            textBox1.Text = idx >= 0 ? idx.ToString() : "-";
            tBoxParent.Text = idx >= 0 ? flat[idx].Parent.ToString() : "0";
            tBoxChFirst.Text = idx >= 0 ? flat[idx].FirstChild.ToString() : "0";
            tBoxChEnd.Text = idx >= 0 ? flat[idx].LastChild.ToString() : "0";
            tBoxCount.Text = item.Children.Count.ToString();

            // Tag — краткая таблица: idx:type:val:min:max:step:parent:first:last
            if (idx >= 0)
            {
                var fi = flat[idx];
                tBoxTagNode.Text = $"{idx}:{(int)fi.Type}:{fi.Value}:" +
                                   $"{fi.MinValue}:{fi.MaxValue}:{fi.Step}:" +
                                   $"{fi.Parent}:{fi.FirstChild}:{fi.LastChild}";
            }

            txtNewNodeName.Text = item.Name;
        }

        // ----------------------------------------------------------------
        // Контекстное меню — Добавить узел
        // ----------------------------------------------------------------
        private void cmnuAddNode_Click_1(object sender, EventArgs e)
        {
            var selected = treeView1.SelectedNode?.Tag as MenuNode;
            if (selected == null) selected = _rootItem;

            // Разрешаем добавлять только в FOLDER
            if (selected.Type != MenuItemType.FOLDER)
            {
                MessageBox.Show("Дочерние элементы можно добавлять только в папку (FOLDER).",
                    "Внимание", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            var newItem = new MenuNode
            {
                Name = "NewItem",
                Type = MenuItemType.BOOLEAN,
                Value = 0,
                MinValue = 0,
                MaxValue = 1,
                Step = 1
            };

            using (var dlg = new NodeEditForm(newItem))
            {
                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    selected.Children.Add(newItem);
                    _isDirty = true;
                    RefreshTree();
                }
            }
        }

        // ----------------------------------------------------------------
        // Контекстное меню — Удалить узел
        // ----------------------------------------------------------------
        private void cmnuRemoveNode_Click_1(object sender, EventArgs e)
        {
            var selected = treeView1.SelectedNode?.Tag as MenuNode;
            if (selected == null || selected == _rootItem)
            {
                MessageBox.Show("Невозможно удалить корневой элемент.",
                    "Внимание", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            if (MessageBox.Show($"Удалить '{selected.Name}' и все дочерние элементы?",
                "Подтверждение", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                RemoveNode(_rootItem, selected);
                _isDirty = true;
                RefreshTree();
            }
        }

        private bool RemoveNode(MenuNode parent, MenuNode target)
        {
            if (parent.Children.Remove(target)) return true;
            foreach (var child in parent.Children)
                if (RemoveNode(child, target)) return true;
            return false;
        }

        // ----------------------------------------------------------------
        // Контекстное меню — Редактировать узел
        // ----------------------------------------------------------------
        private void RenameMenu_Click_1(object sender, EventArgs e)
        {
            var selected = treeView1.SelectedNode?.Tag as MenuNode;
            if (selected == null) return;

            using (var dlg = new NodeEditForm(selected))
            {
                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    _isDirty = true;
                    RefreshTree();
                }
            }
        }

        // ----------------------------------------------------------------
        // ToolStrip — Новый проект
        // ----------------------------------------------------------------
        private void btn_prj_new_Click(object sender, EventArgs e)
        {
            if (_isDirty && MessageBox.Show("Сохранить текущий проект?",
                "Новый проект", MessageBoxButtons.YesNo) == DialogResult.Yes)
                SaveProject();

            _rootItem = new MenuNode { Name = "Root", Type = MenuItemType.FOLDER };
            _projectPath = string.Empty;
            _isDirty = false;
            tBoxNameMenu.Text = "menu";
            tBoxCountPMenu.Text = "5";
            RefreshTree();
            UpdateProjectInfo();
        }

        // ----------------------------------------------------------------
        // ToolStrip — Открыть проект
        // ----------------------------------------------------------------
        private void btn_prj_open_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = "Menu Project (*.mproj)|*.mproj|All files (*.*)|*.*";
            openFileDialog1.Title = "Открыть проект";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    LoadProject(openFileDialog1.FileName);
                    _projectPath = openFileDialog1.FileName;
                    _isDirty = false;
                    UpdateProjectInfo();
                    RefreshTree();
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"Ошибка открытия проекта:\n{ex.Message}",
                        "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        // ----------------------------------------------------------------
        // ToolStrip — Сохранить проект
        // ----------------------------------------------------------------
        private void btn_prj_save_Click(object sender, EventArgs e)
        {
            SaveProject();
        }

        private void SaveProject()
        {
            if (string.IsNullOrEmpty(_projectPath))
            {
                saveFileDialog1.Filter = "Menu Project (*.mproj)|*.mproj|All files (*.*)|*.*";
                saveFileDialog1.Title = "Сохранить проект";
                saveFileDialog1.FileName = tBoxNameMenu.Text;
                if (saveFileDialog1.ShowDialog() != DialogResult.OK) return;
                _projectPath = saveFileDialog1.FileName;
            }

            try
            {
                var xml = SerializeProject();
                File.WriteAllText(_projectPath, xml);
                _isDirty = false;
                UpdateProjectInfo();
                MessageBox.Show("Проект сохранён.", "Сохранение",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {

                MessageBox.Show($"Ошибка сохранения:\n{ex.Message}",
                                    "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        // ----------------------------------------------------------------
        // Сериализация проекта в XML
        // ----------------------------------------------------------------
        private string SerializeProject()
        {
            var root = new XElement("MenuProject",
                new XAttribute("MenuName", tBoxNameMenu.Text),
                new XAttribute("DisplayLines", tBoxCountPMenu.Text),
                new XAttribute("LCD", cBoxTypeLCD.Text),
                new XAttribute("CPU", cBoxTypeCPU.Text),
                new XAttribute("Control", cBoxTypeControl.Text),
                SerializeNode(_rootItem));
            return new XDocument(new XDeclaration("1.0", "utf-8", "yes"), root).ToString();
        }

        private XElement SerializeNode(MenuNode item)
        {
            var el = new XElement("Item",
                new XAttribute("Name", item.Name),
                new XAttribute("Type", (int)item.Type),
                new XAttribute("Value", item.Value),
                new XAttribute("MinValue", item.MinValue),
                new XAttribute("MaxValue", item.MaxValue),
                new XAttribute("Step", item.Step));
            foreach (var child in item.Children)
                el.Add(SerializeNode(child));
            return el;
        }

        // ----------------------------------------------------------------
        // Десериализация проекта из XML
        // ----------------------------------------------------------------
        private void LoadProject(string path)
        {
            var doc = XDocument.Load(path);
            var root = doc.Root;
            if (root == null) throw new Exception("Неверный формат файла проекта.");

            tBoxNameMenu.Text = root.Attribute("MenuName")?.Value ?? "menu";
            tBoxCountPMenu.Text = root.Attribute("DisplayLines")?.Value ?? "5";
            cBoxTypeLCD.Text = root.Attribute("LCD")?.Value ?? "ST7735";
            cBoxTypeCPU.Text = root.Attribute("CPU")?.Value ?? "STM32F103";
            cBoxTypeControl.Text = root.Attribute("Control")?.Value ?? "Энкодер";

            var itemEl = root.Element("Item");
            if (itemEl == null) throw new Exception("Не найден корневой элемент меню.");
            _rootItem = DeserializeNode(itemEl);
        }

        private MenuNode DeserializeNode(XElement el)
        {
            var item = new MenuNode
            {
                Name = el.Attribute("Name")?.Value ?? "Item",
                Type = (MenuItemType)int.Parse(el.Attribute("Type")?.Value ?? "0"),
                Value = int.Parse(el.Attribute("Value")?.Value ?? "0"),
                MinValue = int.Parse(el.Attribute("MinValue")?.Value ?? "0"),
                MaxValue = int.Parse(el.Attribute("MaxValue")?.Value ?? "100"),
                Step = int.Parse(el.Attribute("Step")?.Value ?? "1")
            };
            foreach (var child in el.Elements("Item"))
                item.Children.Add(DeserializeNode(child));
            return item;
        }

        // ----------------------------------------------------------------
        // Генерация Menu.h — кнопка toolStripButtonMenuH
        // ----------------------------------------------------------------
        private void toolStripButtonMenuH_Click(object sender, EventArgs e)
        {
            try
            {
                int displayLines = int.Parse(tBoxCountPMenu.Text);
                string code = MenuCodeGenerator.GenerateH(
                    _rootItem,
                    tBoxNameMenu.Text,
                    displayLines,
                    cBoxTypeCPU.Text,
                    cBoxTypeLCD.Text,
                    cBoxTypeControl.Text);

                OutCodeMenuH.Text = code;
                tabControl2.SelectedTab = tabPageMenuH;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Ошибка генерации Menu.h:\n{ex.Message}",
                    "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        // ----------------------------------------------------------------
        // Генерация Menu.c — кнопка toolStripButtonMenuC
        // ----------------------------------------------------------------
        private void toolStripButtonMenuC_Click(object sender, EventArgs e)
        {
            try
            {
                int displayLines = int.Parse(tBoxCountPMenu.Text);
                string code = MenuCodeGenerator.GenerateC(
                    _rootItem,
                    tBoxNameMenu.Text,
                    displayLines);

                OutCodeMenuC.Text = code;
                tabControl2.SelectedTab = tabPageMenuC;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Ошибка генерации Menu.c:\n{ex.Message}",
                    "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        // ----------------------------------------------------------------
        // Кнопка "Исходники в редактор" (button3)
        // Генерирует оба файла и переключает на вкладку Menu.h
        // ----------------------------------------------------------------
        private void button3_Click(object sender, EventArgs e)
        {
            toolStripButtonMenuH_Click(sender, e);
            toolStripButtonMenuC_Click(sender, e);
            tabControl2.SelectedTab = tabPageMenuH;
        }

        // ----------------------------------------------------------------
        // toolStrip1 ItemClicked — общий обработчик
        // ----------------------------------------------------------------
        private void toolStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            if (e.ClickedItem == toolStripButtonMenuH) toolStripButtonMenuH_Click(sender, e);
            else if (e.ClickedItem == toolStripButtonMenuC) toolStripButtonMenuC_Click(sender, e);
        }

        // ----------------------------------------------------------------
        // Обновление информации о проекте на вкладке PRJ
        // ----------------------------------------------------------------
        private void UpdateProjectInfo()
        {
            lbNamePRJ.Text = string.IsNullOrEmpty(_projectPath)
                ? "---"
                : Path.GetFileNameWithoutExtension(_projectPath);
            lbPatchPRJ.Text = string.IsNullOrEmpty(_projectPath)
                ? "---"
                : Path.GetDirectoryName(_projectPath);
            lbNameMenu.Text = tBoxNameMenu.Text;
        }

        // ----------------------------------------------------------------
        // Кнопка "Добавить последний проект" (button1)
        // ----------------------------------------------------------------
        private void button1_Click(object sender, EventArgs e)
        {
            string lastPath = Properties.Settings.Default.LastProjectPath;
            if (!string.IsNullOrEmpty(lastPath) && File.Exists(lastPath))
            {
                try
                {
                    LoadProject(lastPath);
                    _projectPath = lastPath;
                    _isDirty = false;
                    UpdateProjectInfo();
                    RefreshTree();
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"Ошибка загрузки последнего проекта:\n{ex.Message}",
                        "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            else
            {
                MessageBox.Show("Последний проект не найден.",
                    "Информация", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
        #region OutCodeMenuH TextChanged
        private void OutCodeMenuH_AutoIndentNeeded(object sender, FastColoredTextBoxNS.AutoIndentEventArgs e)
        {
            //block {}
            if (Regex.IsMatch(e.LineText, @"^[^""']*\{.*\}[^""']*$"))
                return;
            //start of block {}
            if (Regex.IsMatch(e.LineText, @"^[^""']*\{"))
            {
                e.ShiftNextLines = e.TabLength;
                return;
            }
            //end of block {}
            if (Regex.IsMatch(e.LineText, @"}[^""']*$"))
            {
                e.Shift = -e.TabLength;
                e.ShiftNextLines = -e.TabLength;
                return;
            }
            //label
            if (Regex.IsMatch(e.LineText, @"^\s*\w+\s*:\s*($|//)") &&
                !Regex.IsMatch(e.LineText, @"^\s*default\s*:"))
            {
                e.Shift = -e.TabLength;
                return;
            }
            //some statements: case, default
            if (Regex.IsMatch(e.LineText, @"^\s*(case|default)\b.*:\s*($|//)"))
            {
                e.Shift = -e.TabLength / 2;
                return;
            }
            //is unclosed operator in previous line ?
            if (Regex.IsMatch(e.PrevLineText, @"^\s*(if|for|foreach|while|[\}\s]*else)\b[^{]*$"))
                if (!Regex.IsMatch(e.PrevLineText, @"(;\s*$)|(;\s*//)"))//operator is unclosed
                {
                    e.Shift = e.TabLength;
                    return;
                }
        }

        private void OutCodeMenuH_CustomAction(object sender, FastColoredTextBoxNS.CustomActionEventArgs e)
        {
            MessageBox.Show(e.Action.ToString());
        }

        private void OutCodeMenuH_SelectionChangedDelayed(object sender, EventArgs e)
        {
            OutCodeMenuH.VisibleRange.ClearStyle(StylesL.SameWordsStyle);
            if (!OutCodeMenuH.Selection.IsEmpty)
                return;//user selected diapason

            //get fragment around caret
            var fragment = OutCodeMenuH.Selection.GetFragment(@"\w");
            string text = fragment.Text;
            if (text.Length == 0)
                return;
            //highlight same words
            var ranges = OutCodeMenuH.VisibleRange.GetRanges("\\b" + text + "\\b").ToArray();
            if (ranges.Length > 1)
                foreach (var r in ranges)
                    r.SetStyle(StylesL.SameWordsStyle);
        }

        private void OutCodeMenuH_TextChanged(object sender, TextChangedEventArgs e)
        {
            switch (StylesL.lang)
            {
                case "CSharp (custom highlighter)":
                    //For sample, we will highlight the syntax of C# manually, although could use built-in highlighter
                    CSharpSyntaxHighlight(e);//custom highlighting
                    break;
                default:
                    break;//for highlighting of other languages, we using built-in FastColoredTextBox highlighter
            }

            if (OutCodeMenuH.Text.Trim().StartsWith("<?xml"))
            {
                OutCodeMenuH.Language = Language.XML;

                OutCodeMenuH.ClearStylesBuffer();
                OutCodeMenuH.Range.ClearStyle(StyleIndex.All);
                InitStylesPriority();
                OutCodeMenuH.AutoIndentNeeded -= fctb_AutoIndentNeeded;

                OutCodeMenuH.OnSyntaxHighlight(new TextChangedEventArgs(OutCodeMenuH.Range));
            }
        }

        private void fctb_AutoIndentNeeded(object sender, AutoIndentEventArgs e)
        {
            //block {}
            if (Regex.IsMatch(e.LineText, @"^[^""']*\{.*\}[^""']*$"))
                return;
            //start of block {}
            if (Regex.IsMatch(e.LineText, @"^[^""']*\{"))
            {
                e.ShiftNextLines = e.TabLength;
                return;
            }
            //end of block {}
            if (Regex.IsMatch(e.LineText, @"}[^""']*$"))
            {
                e.Shift = -e.TabLength;
                e.ShiftNextLines = -e.TabLength;
                return;
            }
            //label
            if (Regex.IsMatch(e.LineText, @"^\s*\w+\s*:\s*($|//)") &&
                !Regex.IsMatch(e.LineText, @"^\s*default\s*:"))
            {
                e.Shift = -e.TabLength;
                return;
            }
            //some statements: case, default
            if (Regex.IsMatch(e.LineText, @"^\s*(case|default)\b.*:\s*($|//)"))
            {
                e.Shift = -e.TabLength / 2;
                return;
            }
            //is unclosed operator in previous line ?
            if (Regex.IsMatch(e.PrevLineText, @"^\s*(if|for|foreach|while|[\}\s]*else)\b[^{]*$"))
                if (!Regex.IsMatch(e.PrevLineText, @"(;\s*$)|(;\s*//)"))//operator is unclosed
                {
                    e.Shift = e.TabLength;
                    return;
                }
        }

        private void InitStylesPriority()
        {
            //add this style explicitly for drawing under other styles
            OutCodeMenuH.AddStyle(StylesL.SameWordsStyle);
        }

        private void CSharpSyntaxHighlight(TextChangedEventArgs e)
        {
            OutCodeMenuH.LeftBracket = '(';
            OutCodeMenuH.RightBracket = ')';
            OutCodeMenuH.LeftBracket2 = '\x0';
            OutCodeMenuH.RightBracket2 = '\x0';
            //clear style of changed range
            e.ChangedRange.ClearStyle(StylesL.BlueStyle, StylesL.BoldStyle, StylesL.GrayStyle, StylesL.MagentaStyle, StylesL.GreenStyle, StylesL.BrownStyle, StylesL.DefStyle);

            //string highlighting
            e.ChangedRange.SetStyle(StylesL.BrownStyle, @"""""|@""""|''|@"".*?""|(?<!@)(?<range>"".*?[^\\]"")|'.*?[^\\]'");
            //comment highlighting
            e.ChangedRange.SetStyle(StylesL.GreenStyle, @"//.*$", RegexOptions.Multiline);
            e.ChangedRange.SetStyle(StylesL.GreenStyle, @"(/\*.*?\*/)|(/\*.*)", RegexOptions.Singleline);
            e.ChangedRange.SetStyle(StylesL.GreenStyle, @"(/\*.*?\*/)|(.*\*/)", RegexOptions.Singleline | RegexOptions.RightToLeft);
            //number highlighting
            e.ChangedRange.SetStyle(StylesL.MagentaStyle, @"\b\d+[\.]?\d*([eE]\-?\d+)?[lLdDfF]?\b|\b0x[a-fA-F\d]+\b");
            //attribute highlighting
            e.ChangedRange.SetStyle(StylesL.GrayStyle, @"^\s*(?<range>\[.+?\])\s*$", RegexOptions.Multiline);
            //class name highlighting
            e.ChangedRange.SetStyle(StylesL.BoldStyle, @"\b(class|struct|enum|interface)\s+(?<range>\w+?)\b");
            //keyword highlighting
            e.ChangedRange.SetStyle(StylesL.BlueStyle, @"\b(abstract|as|base|bool|break|byte|case|catch|char|checked|class|const|continue|decimal|default|delegate|do|double|else|enum|event|explicit|extern|false|finally|fixed|float|for|foreach|goto|if|implicit|in|int|interface|internal|is|lock|long|namespace|new|null|object|operator|out|override|params|private|protected|public|readonly|ref|return|sbyte|sealed|short|sizeof|stackalloc|static|string|struct|switch|this|throw|true|try|typeof|uint|ulong|unchecked|unsafe|ushort|using|virtual|void|volatile|while|add|alias|ascending|descending|dynamic|from|get|global|group|into|join|let|orderby|partial|remove|select|set|value|var|where|yield)\b|#region\b|#endregion\b");
            //
            e.ChangedRange.SetStyle(StylesL.DefStyle, @"#ifndef|#define|#include|#endif.*$", RegexOptions.Multiline);//
            //clear folding markers
            e.ChangedRange.ClearFoldingMarkers();

            //set folding markers
            e.ChangedRange.SetFoldingMarkers("{", "}");//allow to collapse brackets block
            e.ChangedRange.SetFoldingMarkers(@"#region\b", @"#endregion\b");//allow to collapse #region blocks
            e.ChangedRange.SetFoldingMarkers(@"/\*", @"\*/");//allow to collapse comment block
        }
        #endregion

        #region OutCodeC
        private void OutCodeMenuC_AutoIndentNeeded(object sender, AutoIndentEventArgs e)
        {
            //block {}
            if (Regex.IsMatch(e.LineText, @"^[^""']*\{.*\}[^""']*$"))
                return;
            //start of block {}
            if (Regex.IsMatch(e.LineText, @"^[^""']*\{"))
            {
                e.ShiftNextLines = e.TabLength;
                return;
            }
            //end of block {}
            if (Regex.IsMatch(e.LineText, @"}[^""']*$"))
            {
                e.Shift = -e.TabLength;
                e.ShiftNextLines = -e.TabLength;
                return;
            }
            //label
            if (Regex.IsMatch(e.LineText, @"^\s*\w+\s*:\s*($|//)") &&
                !Regex.IsMatch(e.LineText, @"^\s*default\s*:"))
            {
                e.Shift = -e.TabLength;
                return;
            }
            //some statements: case, default
            if (Regex.IsMatch(e.LineText, @"^\s*(case|default)\b.*:\s*($|//)"))
            {
                e.Shift = -e.TabLength / 2;
                return;
            }
            //is unclosed operator in previous line ?
            if (Regex.IsMatch(e.PrevLineText, @"^\s*(if|for|foreach|while|[\}\s]*else)\b[^{]*$"))
                if (!Regex.IsMatch(e.PrevLineText, @"(;\s*$)|(;\s*//)"))//operator is unclosed
                {
                    e.Shift = e.TabLength;
                    return;
                }
        }

        private void OutCodeMenuC_CustomAction(object sender, CustomActionEventArgs e)
        {
            MessageBox.Show(e.Action.ToString());
        }

        private void OutCodeMenuC_TextChangedDelayed(object sender, TextChangedEventArgs e)
        {
            OutCodeMenuC.VisibleRange.ClearStyle(StylesL.SameWordsStyle);
            if (!OutCodeMenuC.Selection.IsEmpty)
                return;//user selected diapason

            //get fragment around caret
            var fragment = OutCodeMenuC.Selection.GetFragment(@"\w");
            string text = fragment.Text;
            if (text.Length == 0)
                return;
            //highlight same words
            var ranges = OutCodeMenuC.VisibleRange.GetRanges("\\b" + text + "\\b").ToArray();
            if (ranges.Length > 1)
                foreach (var r in ranges)
                    r.SetStyle(StylesL.SameWordsStyle);
        }

        private void OutCodeMenuC_TextChanged(object sender, TextChangedEventArgs e)
        {
            switch (StylesL.lang)
            {
                case "CSharp (custom highlighter)":
                    //For sample, we will highlight the syntax of C# manually, although could use built-in highlighter
                    CSharpSyntaxHighlight(e);//custom highlighting
                    break;
                default:
                    break;//for highlighting of other languages, we using built-in FastColoredTextBox highlighter
            }

            if (OutCodeMenuC.Text.Trim().StartsWith("<?xml"))
            {
                OutCodeMenuC.Language = Language.XML;

                OutCodeMenuC.ClearStylesBuffer();
                OutCodeMenuC.Range.ClearStyle(StyleIndex.All);
                InitStylesPriority();
                OutCodeMenuC.AutoIndentNeeded -= fctb_AutoIndentNeeded;

                OutCodeMenuC.OnSyntaxHighlight(new TextChangedEventArgs(OutCodeMenuC.Range));
            }
        }
        #endregion


    }
}