using System;
using System.Collections;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;
using umbraco.BasePages;
using umbraco.BusinessLogic;
using umbraco.cms.businesslogic.web;
using umbraco.editorControls.wysiwyg;
using umbraco.interfaces;
using umbraco.uicontrols;

namespace umbraco.editorControls.tinymce {
    public class TinyMCE : webcontrol.TinyMCE, IDataEditor, IMenuElement {
        private IData _data;
        private bool _enableContextMenu = false;
        private string _editorButtons = "";
        private string _advancedUsers = "";
        private bool _fullWidth = false;
        private int _width = 0;
        private int _height = 0;
        private bool _showLabel = false;
        private string _plugins = "";
        private ArrayList _stylesheets = new ArrayList();

        private ArrayList _menuIcons = new ArrayList();
        private SortedList _buttons = new SortedList();
        private SortedList _mceButtons = new SortedList();
        private bool _isInitialized = false;
        private string _activateButtons = "";
        private string _disableButtons = "help,visualaid,";
        private int m_maxImageWidth = 500;

        public virtual string Plugins {
            get { return _plugins; }
            set { _plugins = value; }
        }

        public TinyMCE(IData Data, string Configuration) {
            _data = Data;
            string[] configSettings = Configuration.Split("|".ToCharArray());

            if (configSettings.Length > 0) {
                _editorButtons = configSettings[0];

                if (configSettings.Length > 1)
                    if (configSettings[1] == "1")
                        _enableContextMenu = true;

                if (configSettings.Length > 2)
                    _advancedUsers = configSettings[2];

                if (configSettings.Length > 3) {
                    if (configSettings[3] == "1")
                        _fullWidth = true;
                    else if (configSettings[4].Split(',').Length > 1) {
                        _width = int.Parse(configSettings[4].Split(',')[0]);
                        _height = int.Parse(configSettings[4].Split(',')[1]);
                    }
                }

                // default width/height
                if (_width < 1)
                    _width = 500;
                if (_height < 1)
                    _height = 400;

                // add stylesheets
                if (configSettings.Length > 4) {
                    foreach (string s in configSettings[5].Split(",".ToCharArray()))
                        _stylesheets.Add(s);
                }

                if (configSettings.Length > 6 && configSettings[6] != "")
                    _showLabel = bool.Parse(configSettings[6]);

                if (configSettings.Length > 7 && configSettings[7] != "")
                    m_maxImageWidth = int.Parse(configSettings[7].ToString());

                // sizing
                if (!_fullWidth) {
                    config.Add("width", _width.ToString());
                    config.Add("height", _height.ToString());
                }

                if (_enableContextMenu)
                    _plugins += ",contextmenu";

                //                config.Add("theme_advanced_statusbar_location", "none");

                // If the editor is used in umbraco, use umbraco's own toolbar
                bool onFront = false;
                if (HttpContext.Current.Request.Path.ToLower().IndexOf(GlobalSettings.Path.ToLower()) > -1)
                    config.Add("theme_advanced_toolbar_location", "external");
                else {
                    onFront = true;
                    config.Add("theme_advanced_toolbar_location", "top");
                }

                // load plugins
                IDictionaryEnumerator pluginEnum = tinyMCEConfiguration.Plugins.GetEnumerator();
                while (pluginEnum.MoveNext()) {
                    tinyMCEPlugin plugin = (tinyMCEPlugin)pluginEnum.Value;
                    if (plugin.UseOnFrontend || (!onFront && !plugin.UseOnFrontend))
                        _plugins += "," + plugin.Name;
                }
                if (_plugins.StartsWith(","))
                    _plugins = _plugins.Substring(1, _plugins.Length - 1);
                if (_plugins.EndsWith(","))
                    _plugins = _plugins.Substring(0, _plugins.Length - 1);

                config.Add("plugins", _plugins);

                // Check advanced settings
                if (("," + _advancedUsers + ",").IndexOf("," + UmbracoEnsuredPage.CurrentUser.UserType.Id + ",") > -1)
                    config.Add("umbracoAdvancedMode", "true");
                else
                    config.Add("umbracoAdvancedMode", "false");

                // Check maximum image width
                config.Add("umbracoMaximumDefaultImageWidth", m_maxImageWidth.ToString());

                // Styles
                string cssFiles = String.Empty;
                string styles = string.Empty;
                foreach (string styleSheetId in _stylesheets) {
                    if (styleSheetId.Trim() != "")
                        try {
                            StyleSheet s = StyleSheet.GetStyleSheet(int.Parse(styleSheetId), false, false);
                            cssFiles += GlobalSettings.Path + "/../css/" + s.Text + ".css";

                            foreach (StylesheetProperty p in s.Properties) {
                                if (styles != string.Empty) {
                                    styles += ";";
                                }
                                if (p.Alias.StartsWith("."))
                                    styles += p.Text + "=" + p.Alias;
                                else
                                    styles += p.Text + "=" + p.Alias;
                            }

                            cssFiles += ",";
                        } catch (Exception ee) {
                            Log.Add(LogTypes.Error, -1,
                                    string.Format(
                                        string.Format("Error adding stylesheet to tinymce (id: {{0}}). {0}", ee),
                                        styleSheetId));
                        }
                }

                config.Add("content_css", cssFiles);
                config.Add("theme_advanced_styles", styles);

                // Add buttons
                IDictionaryEnumerator ide = tinyMCEConfiguration.Commands.GetEnumerator();
                while (ide.MoveNext()) {
                    tinyMCECommand cmd = (tinyMCECommand)ide.Value;
                    if (_editorButtons.IndexOf("," + cmd.Alias + ",") > -1)
                        _activateButtons += cmd.Alias + ",";
                    else
                        _disableButtons += cmd.Alias + ",";
                }

                if (_activateButtons.Length > 0)
                    _activateButtons = _activateButtons.Substring(0, _activateButtons.Length - 1);
                if (_disableButtons.Length > 0)
                    _disableButtons = _disableButtons.Substring(0, _disableButtons.Length - 1);

                // Add buttons
                initButtons();
                _activateButtons = "";

                int separatorPriority = 0;
                ide = _mceButtons.GetEnumerator();
                while (ide.MoveNext()) {
                    string mceCommand = ide.Value.ToString();
                    int curPriority = (int)ide.Key;

                    // Check priority
                    if (separatorPriority > 0 &&
                        Math.Floor(decimal.Parse(curPriority.ToString()) / 10) >
                        Math.Floor(decimal.Parse(separatorPriority.ToString()) / 10))
                        _activateButtons += "separator,";

                    _activateButtons += mceCommand + ",";

                    separatorPriority = curPriority;
                }


                config.Add("theme_advanced_buttons1", _activateButtons);
                config.Add("theme_advanced_buttons2", "");
                config.Add("theme_advanced_buttons3", "");
                config.Add("theme_advanced_toolbar_align", "left");
                config.Add("theme_advanced_disable", _disableButtons);

                config.Add("theme_advanced_path ", "true");
                config.Add("extended_valid_elements", "div[*]");
                config.Add("document_base_url", "/");
                config.Add("event_elements", "div");


                if (HttpContext.Current.Request.Path.IndexOf(GlobalSettings.Path) > -1)
                    config.Add("language", User.GetUser(BasePage.GetUserId(BasePage.umbracoUserContextID)).Language);
                else
                    config.Add("language", System.Threading.Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName);

                if (_fullWidth) {
                    config.Add("auto_resize", "true");
                    base.Cols = 30;
                    base.Rows = 30;
                } else {
                    base.Cols = 0;
                    base.Rows = 0;
                }
            }
        }

        #region IDataEditor Members

        #region TreatAsRichTextEditor

        public virtual bool TreatAsRichTextEditor {
            get { return false; }
        }

        #endregion

        #region ShowLabel

        public virtual bool ShowLabel {
            get { return _showLabel; }
        }

        #endregion

        #region Editor

        public Control Editor {
            get { return this; }
        }

        #endregion

        #region Save()

        public virtual void Save() {
            string parsedString = base.Text.Trim();
            if (parsedString != string.Empty) {
                parsedString = replaceMacroTags(parsedString).Trim();

                // clean macros and add paragraph element for tidy
                bool removeParagraphs = false;
                if (parsedString.Length > 16 && parsedString.ToLower().Substring(0, 17) == "|||?umbraco_macro") {
                    removeParagraphs = true;
                    parsedString = "<p>" + parsedString + "</p>";
                }

                // tidy html

                if (UmbracoSettings.TidyEditorContent) {
                    string tidyTxt = library.Tidy(parsedString, false);
                    if (tidyTxt != "[error]") {
                        // compensate for breaking macro tags by tidy
                        parsedString = tidyTxt.Replace("/?>", "/>");
                        if (removeParagraphs) {
                            if (parsedString.Length - parsedString.Replace("<", "").Length == 2)
                                parsedString = parsedString.Replace("<p>", "").Replace("</p>", "");
                        }
                    } else {
                        // TODO
                        // How to log errors? _data.NodeId does not exist?
                        //BusinessLogic.Log.Add(BusinessLogic.LogTypes.Error, BusinessLogic.User.GetUser(0), _data.NodeId, "Error tidying txt from property: " + _data.PropertyId.ToString());
                    }
                }

                // rescue umbraco tags
                parsedString = parsedString.Replace("|||?", "<?").Replace("/|||", "/>").Replace("|*|", "\"");

                // fix images
                parsedString = tinyMCEImageHelper.cleanImages(parsedString);

                // parse current domain and instances of slash before anchor (to fix anchor bug)
                // NH 31-08-2007
                if (HttpContext.Current.Request.ServerVariables != null) {
                    parsedString = parsedString.Replace(helper.GetBaseUrl(HttpContext.Current) + "/#", "#");
                    parsedString = parsedString.Replace(helper.GetBaseUrl(HttpContext.Current), "");
                }

                // if text is an empty parargraph, make it all empty
                if (parsedString.ToLower() == "<p></p>")
                    parsedString = "";
            }
            _data.Value = parsedString;

            // update internal webcontrol value with parsed result
            base.Text = parsedString;
        }

        #endregion

        protected override void OnLoad(EventArgs e) {
            base.OnLoad(e);
            try {
                base.NodeId = ((cms.businesslogic.datatype.DefaultData)_data).NodeId;
                base.VersionId = ((cms.businesslogic.datatype.DefaultData)_data).Version;
                config.Add("umbraco_toolbar_id",
                           ElementIdPreFix + ((cms.businesslogic.datatype.DefaultData)_data).PropertyId.ToString());
            } catch {
                // Empty catch as this is caused by the document doesn't exists yet,
                // like when using this on an autoform
            }
            Page.ClientScript.RegisterClientScriptBlock(GetType(), "tinymce",
                                                        "<script language=\"javascript\" type=\"text/javascript\" src=\"" +
                                                        JavascriptLocation + CoreFile + "\"></script>");
            Page.ClientScript.RegisterClientScriptBlock(GetType(), "tinymceUmbPasteCheck",
                                                        "<script language=\"javascript\" type=\"text/javascript\" src=\"" +
                                                        JavascriptLocation +
                                                        "/plugins/umbracoAdditions/umbPasteCheck.js\"></script>");
        }

        protected override void Render(HtmlTextWriter output) {
            base.JavascriptLocation = GlobalSettings.ClientPath + "/tinymce/tiny_mce.js";
            base.Render(output);
        }

        protected override void OnInit(EventArgs e) {
            base.OnInit(e);
            if (!Page.IsPostBack) {
                if (_data != null && _data.Value != null)
                    base.Text = _data.Value.ToString();
            }
        }

        private string replaceMacroTags(string text) {
            while (findStartTag(text) > -1) {
                string result = text.Substring(findStartTag(text), findEndTag(text) - findStartTag(text));
                text = text.Replace(result, generateMacroTag(result));
            }
            return text;
        }

        private string generateMacroTag(string macroContent) {
            string macroAttr = macroContent.Substring(5, macroContent.IndexOf(">") - 5);
            string macroTag = "|||?UMBRACO_MACRO ";
            Hashtable attributes = ReturnAttributes(macroAttr);
            IDictionaryEnumerator ide = attributes.GetEnumerator();
            while (ide.MoveNext()) {
                if (ide.Key.ToString().IndexOf("umb_") != -1) {
                    // Hack to compensate for Firefox adding all attributes as lowercase
                    string orgKey = ide.Key.ToString();
                    if (orgKey == "umb_macroalias")
                        orgKey = "umb_macroAlias";

                    macroTag += orgKey.Substring(4, orgKey.ToString().Length - 4) + "=|*|" +
                                ide.Value.ToString() + "|*| ";
                }
            }
            macroTag += "/|||";

            return macroTag;
        }

        public static Hashtable ReturnAttributes(String tag) {
            Hashtable ht = new Hashtable();
            MatchCollection m =
                Regex.Matches(tag, "(?<attributeName>\\S*)=\"(?<attributeValue>[^\"]*)\"",
                              RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
            foreach (Match attributeSet in m)
                ht.Add(attributeSet.Groups["attributeName"].Value.ToString(),
                       attributeSet.Groups["attributeValue"].Value.ToString());

            return ht;
        }

        private int findStartTag(string text) {
            string temp = "";
            text = text.ToLower();
            if (text.IndexOf("ismacro=\"true\"") > -1) {
                temp = text.Substring(0, text.IndexOf("ismacro=\"true\""));
                return temp.LastIndexOf("<");
            }
            return -1;
        }

        private int findEndTag(string text) {
            string find = "<!-- endumbmacro -->";

            int endMacroIndex = text.ToLower().IndexOf(find);
            string tempText = text.ToLower().Substring(endMacroIndex + find.Length, text.Length - endMacroIndex - find.Length);
            int finalEndPos = 0;
            while (tempText.Length > 5)
                if (tempText.Substring(finalEndPos, 6) == "</div>")
                    break;
                else
                    finalEndPos++;

            return endMacroIndex + find.Length + finalEndPos + 6;
        }

        #endregion

        #region IDataFieldWithButtons Members

        public object[] MenuIcons {
            get {
                initButtons();

                object[] tempIcons = new object[_menuIcons.Count];
                for (int i = 0; i < _menuIcons.Count; i++)
                    tempIcons[i] = _menuIcons[i];
                return tempIcons;
            }
        }

        private void initButtons() {
            if (!_isInitialized) {
                _isInitialized = true;

                // Add icons for the editor control:
                // Html
                // Preview
                // Style picker, showstyles
                // Bold, italic, Text Gen
                // Align: left, center, right
                // Lists: Bullet, Ordered, indent, undo indent
                // Link, Anchor
                // Insert: Image, macro, table, formular

                foreach (string button in _activateButtons.Split(',')) {
                    if (button.Trim() != "") {
                        try {
                            tinyMCECommand cmd = (tinyMCECommand)tinyMCEConfiguration.Commands[button];

                            string appendValue = "";
                            if (cmd.Value != "")
                                appendValue = ", '" + cmd.Value + "'";
                            _mceButtons.Add(cmd.Priority, cmd.Command);
                            _buttons.Add(cmd.Priority,
                                         new editorButton(cmd.Alias, ui.Text("buttons", cmd.Alias, null), cmd.Icon,
                                                          "tinyMCE.execInstanceCommand('" + ClientID + "', '" +
                                                          cmd.Command + "', " + cmd.UserInterface + appendValue + ")"));
                        } catch (Exception ee) {
                            Log.Add(LogTypes.Error, User.GetUser(0), -1,
                                    string.Format("TinyMCE: Error initializing button '{0}': {1}", button, ee.ToString()));
                        }
                    }
                }

                // add save icon
                int separatorPriority = 0;
                IDictionaryEnumerator ide = _buttons.GetEnumerator();
                while (ide.MoveNext()) {
                    object buttonObj = ide.Value;
                    int curPriority = (int)ide.Key;

                    // Check priority
                    if (separatorPriority > 0 &&
                        Math.Floor(decimal.Parse(curPriority.ToString()) / 10) >
                        Math.Floor(decimal.Parse(separatorPriority.ToString()) / 10))
                        _menuIcons.Add("|");

                    try {
                        editorButton e = (editorButton)buttonObj;

                        MenuIconI menuItem = new MenuIconClass();

                        menuItem.OnClickCommand = e.onClickCommand;
                        menuItem.ImageURL = e.imageUrl;
                        menuItem.AltText = e.alttag;
                        menuItem.ID = e.id;
                        _menuIcons.Add(menuItem);
                    } catch {
                    }

                    separatorPriority = curPriority;
                }
            }
        }

        #endregion

        #region IMenuElement Members

        public string ElementName {
            get { return "div"; }
        }

        public string ElementIdPreFix {
            get { return "umbTinymceMenu"; }
        }

        public string ElementClass {
            get { return "tinymceMenuBar"; }
        }

        public int ExtraMenuWidth {
            get {
                initButtons();
                return _buttons.Count * 40 + 300;
            }
        }

        #endregion
    }
}