// Personal Webpage Engine
// Copyright(c) 2008 Mihail Kochanov
// You can contact me at StilgarAD@yahoo.com
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.

//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.

//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//------------------------------------------------------------------------------
//
// Personal Web Page Engine
//
//------------------------------------------------------------------------------

using System;
using System.Configuration;
using System.Data.Linq;
using System.Linq;
using System.Web.Security;
using System.Web.UI.WebControls;

public partial class ViewNewsItem : System.Web.UI.Page
{
    private BlogNETDataContext dc;
    public BlogNETDataContext DataContext
    {
        get { return dc; }
    }

    private int newsItemID;
    private NewsItem newsItem;

    #region PageEvents

    protected void Page_PreInit(object sender, EventArgs e)
    {
        if (!Int32.TryParse(Request.QueryString[Constants.QueryString.NewsItemID], out newsItemID))
        {
            Session[Constants.Session.ErrorMessage] = Constants.Error.InvalidQueryString;
            Server.Transfer("~/Error.aspx");
        }

        dc = new BlogNETDataContext(ConfigurationManager.ConnectionStrings["BlogNETDB"].ConnectionString);
        DataLoadOptions dlo = new DataLoadOptions();
        dlo.LoadWith<NewsItem>(ni => ni.Tags);
        dc.LoadOptions = dlo;
    }

    protected void Page_Init(object sender, EventArgs e)
    {
        var newsItems = from ni in dc.NewsItems
                        where ni.NewsItemID == newsItemID
                        select ni;
        try
        {
            newsItem = newsItems.Single();
        }
        catch (InvalidOperationException)
        {
            Session[Constants.Session.ErrorMessage] = Constants.Error.ArticleNotFound;
            Server.Transfer("~/Error.aspx");
        }

        CurrentNewsItem.NewsItem = newsItem;
        CurrentNewsItem.DataContext = this.DataContext;
        this.Title = newsItem.Title;

        #region Pagers

        string commentsPageSize = Application[Constants.Application.CommentsPageSize] as string;
        int size = String.IsNullOrEmpty(commentsPageSize) ? Constants.Settings.CommentsPageSize : Convert.ToInt32(commentsPageSize);
        dpCommentsLower.PageSize = size;
        dpCommentsUpper.PageSize = size;

        #endregion

        //for some bizarre reason the DP does not take into consideration the query string argument
        //if I set the page size programatically but declare the query string field in markup
        dpCommentsUpper.QueryStringField = Constants.QueryString.Page;
        dpCommentsLower.QueryStringField = Constants.QueryString.Page;
    }

    protected void Page_Load(object sender, EventArgs e)
    {

        if (!Request.IsAuthenticated)
        {
            InvisibleCaptcha.InititializeCaptcha(this, ViewState);
        }

        if (!IsPostBack)
        {
            ViewState[Constants.ViewState.LastCommentID] = newsItem.LastCommentID;
        }

        //hide pager if there are no comments
        if (CurrentNewsItem.NewsItem.CommentsCount <= 0)
        {
            dpCommentsLower.Visible = false;
            dpCommentsUpper.Visible = false;
        }
        else
        {
            dpCommentsLower.Visible = true;
            dpCommentsUpper.Visible = true;
        }


        switch (CurrentNewsItem.NewsItem.AllowComments)
        {
            case CommentsPolicy.Everyone:
                {
                    pnlCommentsDisabled.Visible = false;
                    pnlCommentsEnabled.Visible = true;
                }
                break;
            case CommentsPolicy.Registered:
                {
                    if (Request.IsAuthenticated)
                    {
                        pnlCommentsDisabled.Visible = false;
                        pnlCommentsEnabled.Visible = true;
                    }
                    else
                    {
                        pnlCommentsDisabled.Visible = true;
                        pnlCommentsEnabled.Visible = false;

                        lblCommentsStatus.Text = "You must log in to post comments on this article";
                    }
                }
                break;
            case CommentsPolicy.Forbidden:
                {
                    pnlCommentsDisabled.Visible = true;
                    pnlCommentsEnabled.Visible = false;

                    lblCommentsStatus.Text = "Comments on this article are disabled";
                }
                break;
        }

    }

    protected void Page_Unload(object sender, EventArgs e)
    {
        if (dc != null)
        {
            dc.Dispose();
        }
    }

    #endregion

    #region ControlEvents

    protected void ldsComments_Selecting(object sender, LinqDataSourceSelectEventArgs e)
    {
        var comments = from comment in dc.Comments
                       orderby comment.CommentID ascending
                       where comment.NewsItemID == newsItemID
                       select comment;

        e.Result = comments;
    }

    protected void btnSubmit_Click(object sender, EventArgs e)
    {
        //checks whether the last comment has changed. (it has been deleted or new one has been posted)
        if ((int?)ViewState[Constants.ViewState.LastCommentID] != newsItem.LastCommentID)
        {
            commentSubmitError.Visible = true;
            ViewState[Constants.ViewState.LastCommentID] = newsItem.LastCommentID;
            return;
        }

        if (!Page.IsValid)
        {
            return;
        }

        string username;

        int newLineCount = 0;

        //max length of message is 10001 and I don't care that it should be in config
        if (txtCommentText.Text.Length > 10001)
        {
            Session[Constants.Session.ErrorMessage] = Constants.Error.InvalidCommentLength;
            Server.Transfer("~/Error.aspx");
        }
        else
        {
            foreach (char c in txtCommentText.Text)
            {
                if (c == '\n') newLineCount++;
            }

            //max number of new lines is 51
            if (newLineCount > 51)
            {
                Session[Constants.Session.ErrorMessage] = Constants.Error.InvalidCommentLength;
                Server.Transfer("~/Error.aspx");
            }
        }

        if (Request.IsAuthenticated)
        {
            username = User.Identity.Name;
        }
        else
        {
            InvisibleCaptcha.IsCaptchaValid(this, ViewState, true);
            string textBoxUsername = ((TextBox)lvUsername.FindControl("txtNickname")).Text.Trim();
            username = (textBoxUsername == "" ? "Guest" : textBoxUsername.HtmlEncode()) + " (Unregistered)";
        }

        Comment newComment = new Comment
        {
            AuthorUsername = username,
            PostDate = DateTime.Now,
            NewsItemID = newsItemID,
            Text = txtCommentText.Text.HtmlEncode().ReplaceUrlsWithLinks().TrimEnd(null).Replace("  ", " &nbsp;").Replace(Environment.NewLine, "<br />"),
            IPAddress = Request.UserHostAddress
        };

        //Transaction is needed to avoid breaking data integrity between the two SubmitChanges() calls
        //This is very unlikely to occur so I'll just let it pop as a general error.
        try
        {
            dc.Connection.Open();
            dc.Transaction = dc.Connection.BeginTransaction();

            dc.Comments.InsertOnSubmit(newComment);
            newsItem.CommentsCount++;
            dc.SubmitChanges();

            //Need to update the LastCommentID and submit again
            newsItem.LastCommentID = newComment.CommentID;
            dc.SubmitChanges();

            dc.Transaction.Commit();
        }
        catch(Exception ex)
        {
            if (dc.Transaction != null)
            {
                dc.Transaction.Rollback();
            }

            throw ex;
        }
        finally
        {
            if (dc.Connection.State == System.Data.ConnectionState.Open)
            {
                dc.Connection.Close();
            }
        }

        Cache.Remove(Constants.Cache.LatestComments);

        string commentsPageSize = Application[Constants.Application.CommentsPageSize] as string;
        int size = String.IsNullOrEmpty(commentsPageSize) ? Constants.Settings.CommentsPageSize : Convert.ToInt32(commentsPageSize);
        int pageNumber = (newsItem.Comments.Count() - 1) / size + 1;

        Response.Redirect(Utilities.GenerateCommentUrl(newComment, pageNumber));
    }

    protected void ldsComments_Deleted(object sender, LinqDataSourceStatusEventArgs e)
    {
        //if we are not deleting the last comment updating the LastCommentID is not needed
        if (((Comment)e.Result).CommentID != newsItem.LastCommentID)
        {
            newsItem.CommentsCount--;
            dc.SubmitChanges();
        }
        else
        {
            //Transaction is needed to avoid breaking data integrity between the two SubmitChanges() calls
            //This is very unlikely to occur so I'll just let it pop as a general error.
            try
            {
                dc.Connection.Open();
                dc.Transaction = dc.Connection.BeginTransaction();

                newsItem.CommentsCount--;
                dc.SubmitChanges();

                //Need to update the LastCommentID and submit again
                newsItem.LastCommentID = (from c in newsItem.Comments
                                          orderby c.CommentID descending
                                          select c.CommentID).Take(1).SingleOrDefault();

                if (newsItem.LastCommentID == 0)
                {
                    newsItem.LastCommentID = null;
                }

                dc.SubmitChanges();

                dc.Transaction.Commit();
            }
            catch (Exception ex)
            {
                if (dc.Transaction != null)
                {
                    dc.Transaction.Rollback();
                }

                throw ex;
            }
            finally
            {
                if (dc.Connection.State == System.Data.ConnectionState.Open)
                {
                    dc.Connection.Close();
                }
            }
        }

        Cache.Remove(Constants.Cache.LatestComments);

        Response.Redirect(Request.Url.ToString());
    }

    #endregion
}
