context: using System;
  using System.Collections.Generic;
  using System.Text;
  using System.Web.UI;
  using SenseNet.ApplicationModel;
  using SenseNet.ContentRepository.Fields;
  using SenseNet.ContentRepository.Security;
  using SenseNet.ContentRepository.Storage;
  using System.Web.UI.WebControls;
  using System.Reflection;
  using System.Text.RegularExpressions;
  using System.Collections;
  using SenseNet.ContentRepository.Storage.Schema;
  using SenseNet.ContentRepository.Storage.Security;
  using SenseNet.Portal.Virtualization;
  using SenseNet.Portal.UI.Controls;
  using SNC = SenseNet.ContentRepository;
  using SNP = SenseNet.Portal;
  using SenseNet.ContentRepository.i18n;
  using SenseNet.ContentRepository.Schema;
  using SenseNet.ContentRepository;
  using System.Linq;
  using SenseNet.Diagnostics;
  using System.Globalization;
  
  namespace SenseNet.Portal.UI
  {
      /// <summary>
      /// <c>ContentView</c> is a class responsible for enabling various visualizations of the <see cref="SenseNet.ContentRepository.Content ">Content</see> class that it owns.
      /// </summary>
      /// <remarks>
      /// The two most important properties of <c>ContentView</c> is the collection of <see cref="SenseNet.Portal.UI.Controls.FieldControl">FieldControl</see>s and the <see cref="SenseNet.ContentRepository.Content ">Content</see> it references.
      /// These two parts are closely related as every <see cref="SenseNet.Portal.UI.Controls.FieldControl">FieldControl</see> wraps a #Field# defined within its <see cref="SenseNet.ContentRepository.Content ">Content</see>.
      /// 
      /// Basically the <c>ContentView</c> is a visualizer of the <see cref="SenseNet.ContentRepository.Content ">Content</see> assigned to it when created. The <see cref="SenseNet.Portal.UI.Controls.FieldControl">FieldControl</see>s of this ContentView
      /// each visualize a #Field# defined within this <see cref="SenseNet.ContentRepository.Content ">Content</see> (but not necessary all of them). 
      /// 
      /// Another important role of the <c>ContentView</c> is automatically utilizing the pre-defined <see cref="SenseNet.Portal.UI.ViewMode">ViewMode</see>s 
      /// It renders itself and all of its <see cref="SenseNet.Portal.UI.Controls.FieldControl">FieldControl</see>s according to the <see cref="SenseNet.Portal.UI.ViewMode">ViewMode</see> assigned to it when created.
      /// </remarks>
      /// <example>
      /// 
      /// The following code shows a method that loads a ContentView using a Browse view defined in a file located elsewhere than the default location
      /// <code>
      /// public ContentView LoadCustomBrowseView(string nodePath, string customBrowseViewPath)
      /// {
      ///     Node node = Content.Load(nodePath);
      ///     if(node != null)
      ///     {
      ///         ContentView contentView = ContentView.Create(node,this.Page,ViewMode.Browse,customBrowseViewPath);
      ///         return(contentView);
      ///     }
      ///     else
      ///     {
      ///         // TODO: throw exception indicating that nodePath either does not define a node or permission to view it is denied
      ///         throw new NullReferenceException();
      ///     }
      /// </code>
      /// </example>
      public abstract class ContentView : UserControl, INamingContainer
      {
          /// <summary>
          /// EventHandler for the UserAction. Use it in the following cases:
          ///  - defaultbuttons is contained in contentview (obsolete!)
          ///  - a button with OnClick="Click" property is contained in a SingleContentView
          /// Use CommandButtonsAction when using the CommandButtons control in the contentview!
          /// </summary>
          public event EventHandler<UserActionEventArgs> UserAction;
  
          /// <summary>
          /// EventHandler for CommandButtons control. When a button in the CommandButtons control is clicked, this event is fired.
          /// </summary>
          public event EventHandler<CommandButtonsEventArgs> CommandButtonsAction;
  
  
          private List<ErrorControl> _errorControls;
  
          /// <summary>
          /// Gets the owned Content object
          /// </summary>
          public SNC.Content Content { get; private set; }
  
          /// <summary>
          /// Gets the <see cref="SenseNet.Portal.UI.ViewMode">ViewMode</see> in which the content should be viewed. This value determines the DefaultControlRenderMode property.
          /// </summary>
          public ViewMode ViewMode { get; private set; }
  
          /// <summary>
          /// Gets the <see cref="SenseNet.Portal.UI.Controls.FieldControlRenderMode">FieldControlRenderMode</see> which will be the default render mode for <see cref="SenseNet.Portal.UI.Controls.FieldControl">FieldControl</see>s. This value is derermined by the ViewMode property.
          /// </summary>
          [Obsolete("Use ViewControlMode and ViewControlFrameMode instead")]
          public FieldControlRenderMode DefaultControlRenderMode { get; set; }
  
          private FieldControlControlMode _viewControlMode = FieldControlControlMode.None;
          public FieldControlControlMode ViewControlMode
          {
              get
              {
                  // backward compatibility
                  if (_viewControlMode == FieldControlControlMode.None) 
                  {
                      switch (this.DefaultControlRenderMode)
                      {
                          case FieldControlRenderMode.Browse:
                              return FieldControlControlMode.Browse;
                          case FieldControlRenderMode.Default:
                          case FieldControlRenderMode.Edit:
                          case FieldControlRenderMode.InlineEdit:
                              return FieldControlControlMode.Edit;
                      }
                  }
  
                  return _viewControlMode;
              }
              set
              {
                  _viewControlMode = value;
              }
          }
          private FieldControlFrameMode _viewControlFrameMode;
          public FieldControlFrameMode ViewControlFrameMode
          {
              get
              {
                  // backward compatibility
                  if (_viewControlFrameMode == FieldControlFrameMode.None)
                  {
                      switch (this.DefaultControlRenderMode)
                      {
                          case FieldControlRenderMode.Browse:
                          case FieldControlRenderMode.InlineEdit:
                              return FieldControlFrameMode.NoFrame;
                          case FieldControlRenderMode.Default:
                          case FieldControlRenderMode.Edit:
                              return FieldControlFrameMode.ShowFrame;
                      }
                      return FieldControlFrameMode.ShowFrame;
                  }
                  return _viewControlFrameMode;
              }
              set
              {
                  _viewControlFrameMode = value;
              }
          }
  
          /// <summary>
          /// Gets or sets the friendly name of the view. By default it is the title of the <see cref="SenseNet.ContentRepository.Content"> it wraps.</see>
          /// </summary>
          public string DisplayName { get; set; }
  
          /// <summary>
          /// Gets or sets the  description of the view. By default it is the description of the <see cref="SenseNet.ContentRepository.Content"> it wraps.</see>
          /// </summary>
          public string Description { get; set; }
  
          /// <summary>
          /// Gets or sets the icon of the view. By default it is the icon of the <see cref="SenseNet.ContentRepository.Content"> it wraps.</see>
          /// </summary>
          public string Icon { get; set; }
  
          /// <summary>
          /// Gets the list of owned <see cref="SenseNet.Portal.UI.Controls.FieldControl">FieldControl</see>s. ContentView is build around this collection.
          /// </summary>
          public List<FieldControl> FieldControls { get; private set; }
  
          private Exception _contentException;
          /// <summary>
          /// Gets or sets the ContentException. If not null this exception is displayed within the view.
          /// </summary>
          public Exception ContentException
          {
              get { return _contentException; }
              set { _contentException = value; }
          }
          public bool NeedToValidate { get; set; }
          public bool IsUserInputValid { get; protected set; }
  
          #region Shortcuts
          /// <summary>
          /// Gets the Path of the ContentHandler in a safe way. (This ContentHandler is wrapped by the Content which is owned by this ContentView) (Is a shortcut)
          /// </summary>
          public string ParentPath
          {
              get
              {
                  if (this.Content == null || this.Content.ContentHandler == null || this.Content.ContentHandler.ParentId == 0)
                      return String.Empty;
                  return this.Content.ContentHandler.ParentPath;
              }
          }
  
          /// <summary>
          /// Gets the <see cref="SenseNet.ContentRepository.Storage.Node">ContentHandler</see> wrapped by the <see cref="SenseNet.ContentRepository.Content ">Content</see> which is owned by this ContentView. (Is a shortcut)
          /// </summary>
          public Node ContentHandler
          {
              get { return this.Content.ContentHandler; }
          }
  
          /// <summary>
          /// Gets the ContentType property of the <see cref="SenseNet.ContentRepository.Content ">Content</see> owned by this ContentView. (Is a shortcut)
          /// </summary>
          public ContentType ContentType
          {
              get { return this.Content.ContentType; }
          }
  
          /// <summary>
          /// Gets the Name of the Content owned by this ContentView in a safe way. (Is a shortcut)
          /// </summary>
          public string ContentName
          {
              get
              {
                  if (this.Content == null)
                      return String.Empty;
                  return this.Content.Name;
              }
          }
  
          /// <summary>
          /// Gets the Name of the ContentType of the <see cref="SenseNet.ContentRepository.Content ">Content</see> owned by this ContentView in a safe way. (Is a shortcut)
          /// </summary>
          public string ContentTypeName
          {
              get
              {
                  if (this.Content == null || this.Content.ContentType == null)
                      return String.Empty;
                  return this.Content.ContentType.Name;
              }
          }
  
          [Obsolete("This method is obsolete. Please use GetValue instead.", false)]
          protected string GetProperty(string name)
          {
              return GetValue(name);
          }
  
          protected string GetValue(string name, bool storedData = false, CultureInfo cultureInfo = null)
          {
              try
              {
                  return GetValue(name, this.Content, storedData, cultureInfo);
              }
              catch (Exception ex) //logged
              {
                  Logger.WriteException(ex);
                  ContentException = ex;
                  return "";
              }
          }
  
          /// <summary>
          /// Get the raw value of a field. This makes sense only in case of 
          /// number or datetime fields where the field may produce a special
          /// string representation of its value (e.g. a number with thousand 
          /// separators or a currency value extended with the currency sign).
          /// </summary>
          /// <param name="name"></param>
          /// <returns></returns>
          protected string GetRawValue(string name)
          {
              try
              {
                  return GetValue(name, this.Content, false, null, false);
              }
              catch (Exception ex) //logged
              {
                  Logger.WriteException(ex);
                  ContentException = ex;
                  return "";
              }
          }
  
          /// <summary>
          /// Gets a the specified property belonging to the Content of the View in a safe way.
          /// </summary>
          /// <param name="name">The property name. Can be hierarchical.</param>
          /// <returns>String value of the property specified</returns>
          internal static string GetValue(string name, SNC.Content parentContent, OutputMethod outputMethod, bool storedData = false, CultureInfo cultureInfo = null)
          {
              switch (outputMethod)
              {
                  case OutputMethod.Default:
                      throw new NotSupportedException("OutputMethod cannot be Default");
                  case OutputMethod.Raw:
                      return GetValue(name, parentContent, storedData, cultureInfo);
                  case OutputMethod.Text:
                      return System.Web.HttpUtility.HtmlEncode(GetValue(name, parentContent, storedData, cultureInfo));
                  case OutputMethod.Html:
                      return Sanitizer.Sanitize(GetValue(name, parentContent, storedData, cultureInfo));
                  default:
                      throw new NotImplementedException("Unknown OutputMethod: " + outputMethod);
              }
          }
          internal static string GetValue(string name, SNC.Content parentContent, bool storedData = false, CultureInfo cultureInfo = null, bool formattedValue = true)
          {
              string[] parts = name.Split(new char[] { '.' });
              if (parts.Length == 0)
                  return "";
  
              string currPart = "";
              #region special values
  
              if (parts[0].ToLower() == "current" && parts.Length > 1)
              {
                  switch (parts[1].ToLower())
                  {
                      case "url":
                          if (parts.Length > 2)
                          {
                              switch (parts[2].ToLower())
                              {
                                  case "hostname":
                                      foreach (string url in SNP.Page.Current.Site.UrlList.Keys)
                                      {
                                          if (SenseNet.Portal.Virtualization.PortalContext.Current.OriginalUri.ToString().IndexOf(string.Concat(SenseNet.Portal.Virtualization.PortalContext.Current.OriginalUri.Scheme, "://", url, "/")) == 0)
                                          {
                                              return (url);
                                          }
                                      }
                                      return ("");
                                  case "host":
                                      foreach (string url in SNP.Page.Current.Site.UrlList.Keys)
                                      {
                                          if (SenseNet.Portal.Virtualization.PortalContext.Current.OriginalUri.ToString().IndexOf(string.Concat(SenseNet.Portal.Virtualization.PortalContext.Current.OriginalUri.Scheme, "://", url, "/")) == 0)
                                          {
                                              return (string.Concat(SenseNet.Portal.Virtualization.PortalContext.Current.OriginalUri.Scheme, "://", url));
                                          }
                                      }
                                      return ("");
                                  case "name":
                                      return (SenseNet.Portal.Virtualization.PortalContext.Current.OriginalUri.OriginalString);
                                  default:
                                      return ("");
                              }
                          }
                          else
                              return ("");
                      case "user":
                          if (User.Current != null && parts.Length > 1)
                          {
                              switch (parts[2].ToLower())
                              {
                                  case "isauthenticated":
                                      return (User.Current.IsAuthenticated ? "1" : "0");
                                  default:
                                      return (((User)User.Current).GetProperty(parts[2]).ToString());
                              }
                          }
                          else
                              return ("");
                      case "site":
                          if (PortalContext.Current.Site != null)
                          {
                              return PortalContext.Current.Site.GetProperty(parts[2]).ToString();
                          }
                          else
                              return ("");
                      case "page":
                          if (PortalContext.Current.Page != null)
                          {
                              return PortalContext.Current.Page.GetProperty(parts[2]).ToString();
                          }
                          else
                              return ("");
                  }
              }
  
              #endregion
  
              object obj = null;
              object previousListObj = null; // Needed because of IList fields with no indexing
              foreach (string _currPart in parts)
              {
                  currPart = _currPart;
                  int index = 0;
                  //bool isIndexed = false;
  
                  #region Custom properties
  
                  // Check if current part is indexed and if it is get the index and take it off the current part
                  if (IsIndexedField(currPart))
                  {
                      index = GetIndexedFieldValue(currPart);
                      //isIndexed = true;
                      currPart = StripIndex(currPart);
                  }
  
                  if (currPart == "Count")
                  {
                      if (previousListObj is ICollection)
                          return (((ICollection)previousListObj).Count.ToString());
                      else if (obj is ICollection)
                          return (((ICollection)obj).Count.ToString());
                  }
  
                  if (currPart == "AsList()" || Regex.IsMatch(currPart, "AsList[(](['\"](.*)['\"])[)]"))
                  {
                      if (previousListObj is IList)
                      {
                          Match firstMatch = Regex.Match(currPart, "AsList[(](['\"](.*)['\"])[)]");
                          string separatorString = firstMatch.Groups[1].Value;
                          if (separatorString == String.Empty)
                              separatorString = ", "; // default
                          List<string> elements = new List<string>();
                          foreach (object currObj in previousListObj as IList)
                              elements.Add(currObj.ToString());
                          return string.Join(separatorString, elements.ToArray());
                      }
                  }
  
                  if (_currPart == "Load()" && obj is string)
                  {
                      obj = Node.Load<Node>(obj as string);
                      if (!(obj is Node))
                          return "";
                      else
  
gt:                         parentContent = SNC.Content.Load(((Node)obj).Id);