using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.IO;
using System.Net;
using System.Xml;
using System.Text.RegularExpressions;

namespace RSSReader
{
    public partial class Form1 : Form
    {
        //---stałe ikon---
        const int ICO_OPEN = 0;
        const int ICO_CLOSE = 1;
        const int ICO_POST = 2;

        //---plik zawierający listę subskrybowanych kanałów---
        string feedsList = @"\Feeds.txt";

        //---aktualna ścieżka aplikacji---
        string appPath = string.Empty;

        //---ostatnio wprowadzony adres URL (subskrypcja)---
        string lastURLEntered = string.Empty;

        //---używane do wyświetlania paneli z prośbą o cierpliwość---
        Panel displayPanel;

        //---używane do wyświetlania zawartości artykułów---
        Form2 frm2 = new Form2();
        
        public Form1()
        {
            InitializeComponent();
        }

        //---uruchamiane po wybeaniu węzła w kontrolce TreeView---
        private void TreeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            //---przy zaznaczeniu węzła kanału---
            if (e.Node.ImageIndex != ICO_POST && e.Node.Parent != null)
            {
                mnuUnsubscribe.Enabled = true;
                mnuRefreshFeed.Enabled = true;
            }
            else
            {  //---przy zaznaczeniu węzła artykułu---
                mnuUnsubscribe.Enabled = false;
                mnuRefreshFeed.Enabled = false;
            }
        }

        //---uruchamiane po wybraniu węzła w kontrolce TreeView
        // i naciśnięciu klawisza Enter---
        private void TreeView1_KeyDown(object sender, KeyEventArgs e)
        {
            TreeNode node = TreeView1.SelectedNode;
            //---jeżeli naciśnięty został klawisz Enter---
            if (e.KeyCode == System.Windows.Forms.Keys.Enter)
            {
                //---jeżeli zaznaczony jest węzeł artykułu---
                if (node.ImageIndex == ICO_POST)
                {
                    //---tytuł artykułu jako nagłówek formularza Form2---
                    frm2.Text = node.Text;

                    //---modyfikator kontrolki webBrowser1 w formularzu Form2 musi mieć wartość Internal---
                    //---kontrolka ta powinna wyświetlać zawartość wybranego artykułu---
                    frm2.webBrowser1.DocumentText = node.Tag.ToString();

                    //---wyświetlenie formularza Form2---
                    frm2.Show();
                }
            }
        }

        //---odświeżenie aktualnego kanału---
        private void mnuRefreshFeed_Click(object sender, EventArgs e)
        {
            //---w przypadku braku połączenia z internetem---
            if (!IsConnected())
            {
                MessageBox.Show("Brak połączenia z internetem.");
                return;
            }

            //---pobranie węzła wymagającego odświeżenia---
            TreeNode CurrentSelectedNode = TreeView1.SelectedNode;
            string url = CurrentSelectedNode.Tag.ToString();

            //---pobranie nazwy pliku tego kanału---
            string FileName =
               appPath + @"\" + RemoveSpecialChars(url) + ".xml";

            try
            {
                //---tworzenie kopii bezpieczeństwa aktualnego kanału---
                File.Copy(FileName, FileName + "_Kopia", true);

                //---usunięcie kanału z dysku---
                File.Delete(FileName);

                //---ponowne załadowanie kanału---
                if (SubscribeFeed(url))
                {
                    //---usunięcie węzła wymagającego odświeżenia---
                    CurrentSelectedNode.Remove();
                }
                else //---odświeżenie kanału się nie powiodło---
                {
                    //---odtworzenie kanału z kopii bezpieczeństwa---
                    File.Copy(FileName + "_Kopia", FileName, true);
                    MessageBox.Show("Odświeżenie się nie powiodło. Spróbuj ponownie.");
                }

                //---usunięcie pliku kopii bezpieczeństwa---
                File.Delete(FileName + "_Kopia");
            }
            catch (Exception ex)
            {
                MessageBox.Show("Odświeżanie się nie powiodło (" + ex.Message + ")");
            }
        }

        //---anulowanie subskrypcji kanału---
        private void mnuUnsubscribe_Click(object sender, EventArgs e)
        {
            //---pobranie węzła kanału---
            TreeNode CurrentSelectedNode = TreeView1.SelectedNode;

            //---prośba o potwierdzenie anulowania---
            DialogResult result =
               MessageBox.Show("Usuń " + CurrentSelectedNode.Text + "?",
               "Anuluj subskrypcję", MessageBoxButtons.YesNo,
               MessageBoxIcon.Question,
               MessageBoxDefaultButton.Button1);

            try
            {
                if (result == DialogResult.Yes)
                {
                    //---adres URL anulowanego kanału---
                    string urlToUnsubscribe = CurrentSelectedNode.Tag.ToString();

                    //---ładowanie wszystkich kanałów z listy---
                    TextReader textreader = File.OpenText(feedsList);
                    string[] feeds = textreader.ReadToEnd().Split('|');
                    textreader.Close();

                    //---ponowne zapisanie listy kanałów z pominięciem anulowanego---
                    TextWriter textwriter = File.CreateText(feedsList);
                    for (int i = 0; i <= feeds.Length - 2; i++)
                    {
                        if (feeds[i] != urlToUnsubscribe)
                        {
                            textwriter.Write(feeds[i] + "|");
                        }
                    }
                    textwriter.Close();

                    //---usunięcie węzła z kontrolki TreeView---
                    CurrentSelectedNode.Remove();
                    MessageBox.Show("Subskrypcja kanału anulowana!");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //---odczytanie ścieżki aplikacji---
            appPath = Path.GetDirectoryName(
                 System.Reflection.Assembly.GetExecutingAssembly().
                 GetName().CodeBase);

            //---lista kanałów ma być przechowywana w katalogu aplikacji---
            feedsList = appPath + feedsList;

            try
            {
                //---tworzenie węzła głównego---
                TreeNode node = new TreeNode()
                {
                    ImageIndex = ICO_CLOSE,
                    SelectedImageIndex = ICO_OPEN,
                    Text = "Subskrybowane kanały"
                };

                //---dodawanie węzła do drzewa---
                TreeView1.Nodes.Add(node);
                TreeView1.SelectedNode = node;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return;
            }

            try
            {
                //---ładowanie wszystkich subskrybowanych kanałów---
                if (File.Exists(feedsList))
                {
                    TextReader textreader = File.OpenText(feedsList);

                    //---odczytanie adresów URL subskrybowanych kanałów---
                    string[] feeds = textreader.ReadToEnd().Split('|');
                    textreader.Close();

                    //---dodawanie poszczególnych kanałów do drzewa---
                    for (int i = 0; i <= feeds.Length - 2; i++)
                        SubscribeFeed(feeds[i]);
                }
                else
                {
                    //---wstępne subskrybowanie domyślnych kanałów---
                    SubscribeFeed("http://helion.pl/rss/");                    
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        //---w przypadku udanej subskrypcji zwraca wartość prawdy---
        private bool SubscribeFeed(string URL)
        {
            bool succeed = false;
            try
            {
                //---wyświetlenie paneli z komunikatem---
                if (displayPanel == null)
                {
                    displayPanel = CreatePanel("Pobieranie kanału...Proszę czekać.");
                    this.Controls.Add(displayPanel);
                }
                else
                {
                    displayPanel.BringToFront();
                    displayPanel.Visible = true;
                    Cursor.Current = Cursors.WaitCursor;
                    Application.DoEvents();
                }

                //---pobieranie kanału---
                string title = String.Empty;
                string[] posts = DownloadFeed(URL, ref title).Split((char)12);
                if (posts.Length > 0 && posts[0] != String.Empty)
                {
                    //---dodawanie do węzła głównego---
                    TreeNode FeedTitleNode = new TreeNode()
                    {
                        Text = title,
                        Tag = URL,  //---przechowuje adres URL kanału---
                        ImageIndex = ICO_CLOSE,
                        SelectedImageIndex = ICO_OPEN
                    };

                    //---dodawanie tytułu kanału---
                    TreeView1.Nodes[0].Nodes.Add(FeedTitleNode);

                    //---dodawanie poszczególnych elementów (artykułów)---
                    for (int i = 0; i <= posts.Length - 2; i++)
                    {
                        //---pobieranie każdego artykułu w postaci "nagłówek:opis"---
                        string[] str = posts[i].Split((char)3);

                        TreeNode PostNode = new TreeNode()
                        {
                            Text = str[0], //---nagłówek---
                            Tag = str[1],  //---opis---
                            ImageIndex = ICO_POST,
                            SelectedImageIndex = ICO_POST
                        };

                        //---dodawanie artykułów do drzewa---
                        TreeView1.Nodes[0].Nodes
                            [TreeView1.Nodes[0].Nodes.Count - 1].
                            Nodes.Add(PostNode);
                    }
                    //---subskrypcja zakończona sukcesem---
                    succeed = true;

                    //---zaznaczenie nowego kanału i wyświetlenie jego artykułów---
                    TreeView1.SelectedNode = FeedTitleNode;
                }
                else
                    succeed = false;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                //---błędy przy subskrybowaniu kanału---
                succeed = false;
            }
            finally
            {
                //---czyści panel i kursor---
                Cursor.Current = Cursors.Default;
                displayPanel.Visible = false;
				
				//---aktualizacja interfejsu użytkownika---
                Application.DoEvents();
            }
            return succeed;
        }

        //---usuwa znaki specjalne z adresu URL---
        private string RemoveSpecialChars(string str)
        {
            string NewString = String.Empty;
            Regex reg = new Regex("[A-Z]|[a-z]");

            MatchCollection coll = reg.Matches(str);
            for (int i = 0; i <= coll.Count - 1; i++)
                NewString = NewString + coll[i].Value;

            return NewString;
        }

        //---pobieranie kanału i wybieranie tytułu oraz opisu każdego artykułu---
        private string DownloadFeed(string feedURL, ref string title)
        {
            XmlDocument xml = new XmlDocument();

            //---na początek artykuły ładowane są z dysku---
            string FileName =
                appPath + @"\" + RemoveSpecialChars(feedURL) + ".xml";

            if (File.Exists(FileName))
            {
                xml.Load(FileName);
            }
            else
            {
                //---sprawdzenie, czy jest dostęp do sieci---
                if (IsConnected())
                {
                    WebRequest ftpReq = null;
                    WebResponse ftpResp = null;
                    Stream ftpRespStream = null;
                    StreamReader reader = null;
                    bool getRSSFeedFailed = false;
                    try
                    {
                        //---pobieranie dokumentu RSS---
                        ftpReq = WebRequest.Create(feedURL);
                        ftpResp = ftpReq.GetResponse();
                        ftpRespStream = ftpResp.GetResponseStream();
                        reader = new StreamReader(ftpRespStream, System.Text.Encoding.UTF8);

                        //---ładowanie dokumentu RSS do obiektu klasy XMLDocument---
                        xml.Load(reader);

                        //---zapisanie lokalnej kopii dokumentu kanału---
                        xml.Save(FileName);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                        getRSSFeedFailed = true;
                    }
                    finally
                    {
                        if (ftpRespStream != null)
                        {
                            ftpRespStream.Dispose();
                            ftpRespStream.Close();
                        };
                        if (ftpResp != null) ftpResp.Close();
                    }
                    if (getRSSFeedFailed) return String.Empty;
                }
                else
                {
                    return String.Empty;
                }
            }

            //---pobieranie tytułu kanału---
            XmlNode titleNode = xml.SelectSingleNode(@"rss/channel/title");
            title = titleNode.InnerText;

            //---wybranie wszystkich elementów <rss><channel><item>---
            XmlNodeList nodes = xml.SelectNodes("rss/channel/item");

            string result = String.Empty;
            foreach (XmlNode node in nodes)
            {
                //---wybranie elementów <title> i <description> dla każdego artykułu---
                result += node.SelectSingleNode("title").InnerText + ((char)3);
                result += node.SelectSingleNode("description").InnerText + ((char)12);
            }
            return result;
        }

        //---sprawdza, czy urządzenie jest połączone z internetem---
        private bool IsConnected()
        {
            try
            {
                string hostName = Dns.GetHostName();
                IPHostEntry curhost = Dns.GetHostEntry(hostName);
                return (curhost.AddressList[0].ToString() != IPAddress.Loopback.ToString());
            }
            catch (Exception)
            {
                return false;
            }
        }

        //---tworzy panel wyświetlający komunikat---
        private Panel CreatePanel(string str)
        {
            //---panel tła---
            Panel panel1 = new Panel()
            {
                BackColor = Color.Black,
                Location = new Point(52, 13),
                Size = new Size(219, 67),
                Visible = false
            };
            panel1.BringToFront();

            //---panel pierwszego planu---
            Panel panel2 = new Panel()
            {
                BackColor = Color.LightYellow,
                Location = new Point(3, 3),
                Size = new Size(panel1.Size.Width - 6, panel1.Size.Height - 6)
            };

            //---dodajemy etykietę wyświetlającą tekst---
            Label label = new Label()
            {
                Font = new Font(FontFamily.GenericSansSerif, 12, FontStyle.Bold),
                TextAlign = ContentAlignment.TopCenter,
                Location = new Point(3, 3),
                Size = new Size(panel2.Size.Width - 6, panel2.Size.Height - 6),
                Text = str
            };

            //---dodanie etykiety do panelu Panel2---
            panel2.Controls.Add(label);

            //---dodanie panelu Panel2 do panelu Panel1---
            panel1.Controls.Add(panel2);
            return panel1;
        }

        private void mnuSubscribe_Click(object sender, EventArgs e)
        {
            if (!IsConnected())
            {
                MessageBox.Show("Brak połączenia z internetem.");
                return;
            }

            //---dodawanie referencji biblioteki to Microsoft.VisualBasic.dll---
            string URL = Microsoft.VisualBasic.Interaction.InputBox(
                "Proszę podać adres URL kanału", "Adres kanału", lastURLEntered, 0, 0);

            if (URL != String.Empty)
            {
                lastURLEntered = URL;

                //---jeżeli kanał został zasubskrybowany---
                if (SubscribeFeed(URL))
                {
                    //---zapisanie do listy kanałów---
                    TextWriter textwriter = File.AppendText(feedsList);
                    textwriter.Write(URL + "|");
                    textwriter.Close();
                }
                else
                {
                    MessageBox.Show("Kanał nie został zaskubskrybowany. " +
                    "Sprawdź, czy wprowadzony adres URL " +
                    "jest poprawny i czy możliwy jest dostęp" +
                    "do internetu.");
                }
            }
        }

        private void mnuCollapseAllFeeds_Click(object sender, EventArgs e)
        {
            TreeView1.CollapseAll();
        }
    }
}