using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web;
using System.Windows.Forms;
using Newtonsoft.Json.Linq;

namespace PostRetriever
{
  public partial class PostRetrievalForm : Form
  {
    public PostRetrievalForm()
    {
      InitializeComponent();
    }

    private void EnableControls(bool enabled)
    {
      getButton.Enabled = enabled;
      idLabel.Enabled = enabled;
      blogIdBox.Enabled = enabled;
      domainLabel.Enabled = enabled;
      domainBox.Enabled = enabled;
      startLabel.Enabled = enabled;
      startBox.Enabled = enabled;
      incLabel.Enabled = enabled;
      incBox.Enabled = enabled;
      copyButton.Enabled = enabled ? postGrid.RowCount > 0 : false;
    }

    private async void getButton_Click(object sender, EventArgs e)
    {
      if (postGrid.RowCount > 0)
      {
        var res = MessageBox.Show(
          Control.FromHandle(this.Handle),
          "This will clear the existing post list.",
          "Post List Not Empty",
          MessageBoxButtons.OKCancel
        );
        if (res == DialogResult.Cancel)
          return;

        postGrid.Rows.Clear();
      }
      else
      {
        // If the grid is empty let's assume we have to
        // add the columns that are needed for our data

        var col1 = postGrid.Columns.Add("Number", "Number");
        postGrid.Columns.Add("Title", "Title");
        var col3 =
          postGrid.Columns.Add("BlogLinkText", "Blog Link Text");
        var col4 = postGrid.Columns.Add("BlogLink", "Blog Link");
        var col5 =
          postGrid.Columns.Add("WLWLinkText", "WLW Link Text");
        var col6 =
          postGrid.Columns.Add("LiveWriterLink", "Live Writer Link");


        // The first column is numeric

        postGrid.Columns[col1].DefaultCellStyle.Alignment =
          DataGridViewContentAlignment.MiddleRight;
        postGrid.Columns[col1].ValueType = typeof(int);

        // We'll set the link columns to be the size of the header
        // rather than the content

        postGrid.Columns[col3].AutoSizeMode =
          DataGridViewAutoSizeColumnMode.ColumnHeader;
        postGrid.Columns[col4].AutoSizeMode =
          DataGridViewAutoSizeColumnMode.ColumnHeader;
        postGrid.Columns[col5].AutoSizeMode =
          DataGridViewAutoSizeColumnMode.ColumnHeader;
        postGrid.Columns[col6].AutoSizeMode =
          DataGridViewAutoSizeColumnMode.ColumnHeader;
      }

      // Let's disable the UI controls while we query our posts

      EnableControls(false);

      // And enable the grid control

      postGrid.Enabled = true;

      // Extract some info from the UI that willl be passed to
      // our main function

      string blogId = blogIdBox.Text;
      string domain = domainBox.Text;
      int start = Int32.Parse(startBox.Text);
      int inc = Int32.Parse(incBox.Text);

      await PopulatePostInfo(domain, blogId, start, inc, postGrid);

      EnableControls(true);
    }

    private async Task PopulatePostInfo(
      string domain, string blogId, int start, int inc,
      DataGridView grid
    )
    {
      // String to ask Excel to turn the current cell into a
      // link to the cell to the left

      const string toLink = "=HYPERLINK(INDIRECT(\"RC[-1]\",0))";

      int i = start;

      using (var client = new WebClient())
      {
        // First let's just find out how many posts there are
        // on this blog

        var initRes = await GetPosts(client, blogId, 0, 1);
        if (String.IsNullOrEmpty(initRes))
        {
          MessageBox.Show("Unable to access blog from provided ID");
          return;
        }

        var jo = JObject.Parse(initRes);
        var total = (int)jo["totalResults"];

        // While we haven't gotten to the end of our blogs or
        // hit an error, keep going

        while (i < total)
        {
          // Get the next batch of posts

          var results = await GetPosts(client, blogId, inc, i);

          if (String.IsNullOrEmpty(results)) break;

          // Parse the returned JSON, using LINQ to extract
          // an IEnumerable of tuples of the title string
          // and the post ID

          var o = JObject.Parse(results);
          var posts =
            from p in o["entries"]
            select
              new Tuple<string, string, string>(
                (string)p["title"],
                (string)p["urlId"],
                (string)p["permalinkUrl"]
              );

          int j = 0; // An index into this batch
          foreach (var post in posts)
          {
            // Add our new record to the gridview

            int row = grid.Rows.Add();
            int idx = 0;

            // Set the post count (counting down to 1)
            // in the first column

            grid.Rows[row].Cells[idx++].Value =
              (total + 1 - (i + j)).ToString();

            // Let's go an extra step and decode any HTML
            // characters (e.g. &quot; and &dash) found
            // in the post title before setting that in
            // the second column

            var sw = new StringWriter();
            HttpUtility.HtmlDecode(post.Item1, sw);
            string title = sw.ToString();
            grid.Rows[row].Cells[idx++].Value = title;

            // Insert the blog link next

            grid.Rows[row].Cells[idx++].Value = post.Item3;

            // Hardcode a standard value in the next column:
            // this will create a link in Excel based on the
            // value in the previous column

            grid.Rows[row].Cells[idx++].Value = toLink;

            // Generate the text for the link based on the
            // domain and the post ID and place it in the
            // next column
            
            string link =
              String.Format(
                "wlw://{0}/?postid={1}", domain, post.Item2
              );
            grid.Rows[row].Cells[idx++].Value = link;

            // Hardcode a standard value in the next column:
            // this will create a link in Excel based on the
            // value in the previous column

            grid.Rows[row].Cells[idx].Value = toLink;

            j++;
          }

          // We didn't get any posts in this batch, so let's
          // break out of the while loop

          if (j == 0) break;

          // Scroll the grid view down so the bottom of it
          // is visible
          
          grid.FirstDisplayedScrollingRowIndex = grid.RowCount - 1; 
          
          // Increment the counter ready for the next batch

          i += inc;
        }
      }
    }

    // Helper function to get a batch of posts from the blog

    private async Task<string> GetPosts(
      WebClient client, string blogId, int inc, int i
    )
    {
      // Generate the URL for the query

      var url = string.Format(
        "http://api.typepad.com/blogs/{0}/" +
        "post-assets.json?start-index={1}&max-results={2}",
        blogId, i, inc
      );

      // Try to execute it asynchronously via the WebClient
      // passed in (if it fails we'll just return an empty
      // string)

      try
      {
        return await client.DownloadStringTaskAsync(url);
      }
      catch
      {
        return "";
      }
    }

    private void copyButton_Click(object sender, EventArgs e)
    {
      // Copy the contents of the gridview to the clipboard

      postGrid.ClipboardCopyMode =
        DataGridViewClipboardCopyMode.EnableWithoutHeaderText;
      postGrid.SelectAll();
      var obj = postGrid.GetClipboardContent();
      postGrid.ClearSelection();
      Clipboard.SetDataObject(obj, true);
    }
  }
}
