//Copyright (C) Troy Magennis

using System;
using System.Collections;   
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Xml.Linq;
using SampleSupport;
using QuerySamples;
using System.Xml;
using System.Data.SqlClient;
using System.Diagnostics;

using System.Drawing;
using System.Linq.Expressions;
using System.Dynamic;
using System.Text;
using Excel = Microsoft.Office.Interop.Excel;


namespace SampleQueries {
    [Title("Rozdział 8 - Nowe funkcje w C# 4.0")]
    [Prefix("Listing_8_")]
    public class Chapter08Samples : SampleHarness
    {
        # region Sample Data

        public class Contact
        {
            // Konstruktor zdefiniowany z parametrami opcjonalnymi
            public Contact(
                string firstName,
                string lastName,
                DateTime dateOfBirth,
                string email = "",      // opcjonalny
                string phone = "",      // opcjonalny
                string state = "Inne")  // opcjonalny
            {
                FirstName = firstName;
                LastName = lastName;
                DateOfBirth = dateOfBirth;
                Email = email;
                Phone = phone;
                State = state;
            }


            public string FirstName { get; set; }
            public string LastName { get; set; }
            public string Email { get; set; }
            public string Phone { get; set; }
            public DateTime DateOfBirth { get; set; }
            public string State { get; set; }

            public static List<Contact> SampleData()
            {
                return new List<Contact>
                {
                    new Contact ("Bartłomiej", "Gajewski",      new DateTime(1945,10,19), phone: "885 983 885", email: "gajewski@aspiring-technology.com", state: "MA" ),
                    new Contact ("Alfred",     "Wieczorek",     new DateTime(1973,12,09), phone: "848 553 848", email: "al1@aspiring-technology.com", state: "WM" ),
                    new Contact ("Adam",       "Gadomski",      new DateTime(1959,10,03), phone: "115 999 115", email: "adamg@aspiring-technology.com", state: "OP" ),
                    new Contact ("Jan",        "Detka",         new DateTime(1950,12,16), phone: "677 602 677", email: "jan.detka@aspiring-technology.com", state: "MA" ),
                    new Contact ("Cezary",     "Zbytek",        new DateTime(1935,02,10), phone: "603 303 603", email: "czbytek@aspiring-technology.com", state: "LU" ),
                    new Contact ("Stanisław",  "Kowal",         new DateTime(1950,02,20), phone: "546 607 546", email: "kowals@aspiring-technology.com", state: "WM" ),
                    new Contact ("Cyryl",      "Latos",         new DateTime(1951,10,21), phone: "278 918 278", email: "latos@aspiring-technology.com", state: "WM" ),
                    new Contact ("Bernard",    "Radliński",     new DateTime(1946,05,18), phone: "715 920 715", email: "bernard@aspiring-technology.com", state: "WP" ),
                    new Contact ("Maciej",     "Karaś",         new DateTime(1977,09,17), phone: "364 202 364", email: "mac.karas@aspiring-technology.com", state: "WP" ),
                    new Contact ("Adrian",     "Horwat",        new DateTime(1922,05,23), phone: "165 737 165", email: "adrianh@aspiring-technology.com", state: "SW" )
                };
            }
        }

        #endregion

        [SampleSupport.Category("Funkcje C# 4.0")]
        [Title("Listing 8-1 : COM Interop i parametry opcjonalne")]
        [Description("Przykład demonstruje, w jaki sposób parametry opcjonalne ułatwiają wywoływanie funkcji Excela za pomocą COM-Interop.")]
        public void Listing_8_1_COMInteropWithAndWithoutOptionalParameters()
        {
            string fileName = Path.Combine(
                Environment.CurrentDirectory, "Data/SampleExcel.xlsx");


     

            // Stary sposób – przed parametrami opcjonalnymi
            var excel = new Microsoft.Office.Interop.Excel.Application();
            try
            {
                Microsoft.Office.Interop.Excel.Workbook workBook =
                excel.Workbooks.Open(fileName, Type.Missing,
                        Type.Missing, Type.Missing, Type.Missing,
                        Type.Missing, Type.Missing, Type.Missing,
                        Type.Missing, Type.Missing, Type.Missing,
                        Type.Missing, Type.Missing, Type.Missing,
                        Type.Missing);
                      
                // wykonaj pracę w Excelu...
                
                workBook.Close(false, fileName);
            }
            finally
            {
                excel.Quit();
            }

            // Nowy sposób – użycie parametrów opcjonalnych
            var excelNew = new Microsoft.Office.Interop.Excel.Application();
            try
            {
                Microsoft.Office.Interop.Excel.Workbook workBook =
                    excelNew.Workbooks.Open(fileName);

                // wykonaj pracę w Excelu...
                
                workBook.Close(false, fileName);
            }
            finally
            {
                excelNew.Quit();
            }
        }

        [SampleSupport.Category("Funkcje C# 4.0")]
        [Title("Listing 8-2 : Parametry opcjonalne")]
        [Description("Przykład demonstruje, jak wywoływać metody używając parametrów opcjonalnych.")]
        [LinkedClass("OldWay")]
        [LinkedClass("NewWay")]
        public void Listing_8_2_OptionalParameters()
        {
            OldWay oldWay = new OldWay();
            oldWay.DoSomething(
                "({0},{1}) Stary sposób - wartości domyślne z metod przeciążonych.");

            NewWay newWay = new NewWay();

            // poniższe wyrażenie są logicznie identyczne
            // (poza przekazywanymi ciągami znaków)
            newWay.DoSomething(
                "({0},{1}) Nowy sposób - wartości domyślne w definicjach parametrów.");

            newWay.DoSomething(
                "({0},{1}) Nowy sposób - przekazywane tylko formatString i param1.",
                0);

            newWay.DoSomething(
                "({0},{1}) Nowy sposób - przekazywane formatString, param1 i param2.",
                0,
                true);
        }

        public class OldWay
        {
            // wiele przeciążeń wywołuje jedną implementację
            // metody głównej przetwarzającej wszystkie dane wejściowe
            public void DoSomething(string formatString)
            {
                // przekazywanie 0 jako wartości domyślnej param1
                // oraz true jako wartości domyślnej param2
                DoSomething(formatString, 0, true);
            }

            public void DoSomething(string formatString, int param1)
            {
                DoSomething(formatString, param1, true);
            }

            public void DoSomething(string formatString, bool param2)
            {
                DoSomething(formatString, 0, param2);
            }

            // faktyczna implementacja. Wszystkie wariacje wywołują tę
            // metodę w celu zaimplementowania jej funkcjonalności.
            public void DoSomething(
                string formatString,
                int param1,
                bool param2)
            {
                Console.WriteLine(
                    String.Format(formatString, param1, param2));
            }
        }

        public class NewWay
        {
            // parametry opcjonalne mają wskazane wartości domyślne.
            // parametry opcjonalne muszą następować po zwykłych.
            public void DoSomething(
                string formatString,
                int param1 = 0,
                bool param2 = true)
            {
                Console.WriteLine(
                    String.Format(formatString, param1, param2));
            }

            public void M1(string s, int i = 1) { }
            //public void M2(System.Drawing.Point p = new System.Drawing.Point()) { }
            public void M3(System.Drawing.Point p = default(System.Drawing.Point)) { }
            public void M4(int i = 1, params string[] values) { }

            /* BŁĘDY -
            //"Parametry opcjonalne muszą występować po obowiązkowych"
            public void M1 (int i = 1, string s) {} 

            //"Domyślna wartość 'p' musi być stałą czasu kompilacji"
            // Nie można użyć konstruktora z parametrami.
            public void M2(Point p = new Point(0,0)) {}

            //"Domyślna wartość 'p' musi być stałą czasu kompilacji"
            //(Musi być typem prostym (tylko struct lub typy wbudowane))
            public void M5(StringBuilder p = new StringBuilder()) {}

             //"Parametr typu ref lub out nie może mieć wartości domyślnej"
            public void M6(int i = 1, out string s = "") {}

            //"Nie można wskazać wartości domyślnej dla tablicy parametrów"
            public void M7(int i = 1, params string[] values = "test") {}

            */

        }

        [SampleSupport.Category("Funkcje C# 4.0")]
        [Title("Listing 8-3 : Argumenty nazwane.")]
        [Description("Przykład demonstruje, jak wywoływać metody za pomocą argumentów nazwanych.")]
        [LinkedClass("NewWay")]
        public void Listing_8_3_NamedArguments()
        {
            // odwracanie kolejności argumentów
            Point p1 = new Point(y: 100, x: 10);

            // używanie wyrażenia jako wartości argumentu
            int width = 600;
            Point p2 = new Point(y: width - 100, x: 10);

            /* BŁĘDY
             
            //"Argument nazwany Named 'x' wskazuje parametr, dla którego 
            // przekazano już wartość za pomocą pozycji"
            Point p3 = new Point(10, x: 10);

            // "Specyfikacje argumentów nazwanych muszą występować po 
            // wskazaniu wszytskich argumentów ustalonych"
            Point p4 = new Point(y: 100, 10);

            // "Najbardziej zbliżone przeciążenie '.ctor' nie zawiera 
            // parametru 'x'"
            Point p5 = new Point(x: 10);
             * 
             */

            // pomijanie argumentów i zmiana ich kolejności
            NewWay newWay = new NewWay();

            // pomijanie parametru opcjonalnego
            newWay.DoSomething(
                "({0},{1}) Nowy sposób - param1 pominięty.",
                param2: false);

            // dowolna kolejność, ale jeśli parametr nie ma
            // wartości domyślnej, musi zostać wymieniony z nazwy!
            newWay.DoSomething(
                param2: false,
                formatString: "({0},{1})  Nowy sposób - parametry" +
                              " wskazane za pomocą nazw w dowolnej kolejności.",
                param1: 5);
        }

        [SampleSupport.Category("Funkcje C# 4.0")]
        [Title("Listing 8-4 : Argumenty nazwane i parametry opcjonalne w zapytaniach LINQ")]
        [Description("Przykład demonstruje, jak używać argumentów nazwanych i parametrów opcjonalnych w zapytaniach LINQ.")]
        [LinkedClass("Contact")]
        public void Listing_8_4_NamedAndOptionalLINQParameters()
        {
            var q = from c in Contact.SampleData()
                    where c.State == "MA"
                    select new Contact(
                        c.FirstName, c.LastName,
                        state: c.State,
                        dateOfBirth: c.DateOfBirth
                        );

            foreach (var c in q)
                Console.WriteLine("{0}, {1} ({2}) - {3}",
                    c.LastName, c.FirstName,
                    c.DateOfBirth.ToShortDateString(), c.State);
        }        
        
        [SampleSupport.Category("Funkcje C# 4.0")]
        [Title("Listing 8-7 : Typ dynamiczny służący do parsowania pliku CSV.")]
        [Description("Class to represent a dynamic type that will allow the LINQ code (or any other code) to parse a single comma-separated line and access data at runtime based on thenames in the header row of the text file.")]
        [LinkedClass("CsvLine")]
        public void Listing_8_7_CSVLine()
        {
            // Kod CSVLine.
        }
        
        public class CsvLine : System.Dynamic.DynamicObject
        {
            string[] _lineContent;
            List<string> _headers;

            public CsvLine(string line, List<string> headers)
            {
                this._lineContent = line.Split(',');
                this._headers = headers;
            }

            public override bool TryGetMember(
                GetMemberBinder binder, 
                out object result )
            {
                result = null;

                // znajdź pozycję indeksu i pobierz jej wartość
                int index = _headers.IndexOf(binder.Name);
                if (index >= 0 && index < _lineContent.Length)
                {
                    result = _lineContent[index];
                    return true;
                }

                return false;
            }

            public override bool TryGetIndex(
                GetIndexBinder binder, 
                object[] indexes, 
                out object result)
            {
                result = null;

                int index = (int)indexes[0];
                if (index >= 0 && index < _lineContent.Length)
                {
                    result = _lineContent[index];
                    return true;
                }

                return false;
            }
        }

        [SampleSupport.Category("Funkcje C# 4.0")]
        [Title("Listing 8-8 : Typ dynamiczny służący do parsowania plików CSV")]
        [Description("Klasa IEnumerable class odczytuje linię nagłówka i zwraca każdą linię zawartości jako instancję typu dynamicznego CsvLine.")]
        [LinkedClass("CsvParser")]
        public void Listing_8_8_CsvParser()
        {
            // Kod pokazujący CsvParser.
        }

        public class CsvParser : IEnumerable
        {
            List<string> _headers;
            string[] _lines;

            public CsvParser(string csvContent)
            {
                _lines = csvContent.Split('\n');

                // pobierz wiersz nagłówka i zapamiętaj pozycje
                if (_lines.Length > 0)
                    _headers = _lines[0].Split(',').ToList();
            }

            public IEnumerator GetEnumerator()
            {
                // pomiń linię nagłówka
                bool header = true;

                foreach (var line in _lines)
                    if (header)
                        header = false;
                    else
                        yield return new CsvLine(line, _headers);
            }
        }

        [SampleSupport.Category("Funkcje C# 4.0")]
        [Title("Listing 8-9 : LINQ dla typów dynamicznych")]
        [Description("Przykład demonstruje, jak używać typów dynamicznych do parsowania i programowania plików CSV.")]
        [LinkedClass("CsvLine")]
        [LinkedClass("CsvParser")]
        public void Listing_8_9_DynamicLookupOnCSVFile()
        {
            string content =
                "FirstName,LastName,State\nTroy,Magennis,TX\nJanet,Doherty,WA";

            var q = from dynamic c in new CsvParser(content)
                    where c.State == "WA"
                    select c;

            foreach (var c in q)
            {
                Console.WriteLine("{0}, {1} ({2})",
                    c.LastName,
                    c.FirstName,
                    c.State);
            }
        }

        [SampleSupport.Category("Funkcje C# 4.0")]
        [Title("Listing 8 : Słowo kluczowe dynamic")]
        [Description("Przykład demonstruje, jak używać słowa kluczowego dynamic w C#.")]
        public void Listing_8_DynamicKeyword()
        {
            dynamic o = 1;
            o.ThisMethodIsNotDefinedAnywhere();

            object os = "test";
            
            // BŁĄD - 'object' nie zawiera 
            // definicji 'ToUpper'
            // os.ToUpper();
            
            // musimy rzutować, aby uzyskać dostęp do metod 
            // bazowego typu string.
            ((string)os).ToUpper();

            // var działa, ale tylko w zakresie metody,
            // nie może zostać użyte w typie generycznym.
            var vs = "test";

            vs.ToUpper();

            // BŁĄD - 'var' może występować tylko wewnątrz
            // deklaracji zmiennej lokalnej
            //List<var> list = new List<var>();


            // dynamic działa we wszystkich przypadkach
            dynamic ds = "test";
            ds.ToUpper();
            List<dynamic> dlist = new List<dynamic>();
        }

        // Listing 8-13
        public IEnumerable<List<dynamic>> GetExcelRowEnumerator(
            string fileName,
            int sheetIndex)
        {
            // zadeklaruj tablicę w celu przechowania naszych wartości
            object[,] valueArray = null;

            // utwórz referencję COM do Excela
            var excel = new Excel.Application();
            try
            {
                //Excel.Workbook workBook = excel.Workbooks.Open(fileName,
                //Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                //Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                //Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                //Type.Missing, Type.Missing);

                // otwórz skoroszyt za pomocą nazwy pliku
                Excel.Workbook workBook = excel.Workbooks.Open(fileName);

                if (workBook != null &&
                     sheetIndex < workBook.Sheets.Count)
                {
                    // pobierz arkusz Excela, 1 dla pierwszego itd.
                    Excel.Worksheet sheet =
                        workBook.Sheets[sheetIndex];

                    // znajdź zakres komórek wykorzystanych w arkuszu
                    Excel.Range usedRange = sheet.UsedRange;

                    // wczytaj do tablicy, jest to najszybszy sposób
                    // uzyskania wszystkich wartości za jednym razem
                    valueArray = usedRange.get_Value(
                        Excel.XlRangeValueDataType.xlRangeValueDefault);
                }

                workBook.Close(false, fileName);
            }
            finally
            {
                // po skończeniu pracy z Excelem, zamknij go.
                excel.Quit();
            }

            // zbuduj wiersz i zwróć je pojedynczo instrukcją yield
            for (int rowIndex = 1;
                   rowIndex <= valueArray.GetLength(0);
                   rowIndex++)
            {
                List<dynamic> row =
                    new List<dynamic>(
                        valueArray.GetLength(1));

                // zbuduj listę wartości kolumn dla wiersza
                for (int colIndex = 1;
                      colIndex <= valueArray.GetLength(1);
                      colIndex++)
                {

                    row.Add(
                      valueArray[rowIndex, colIndex]);
                }

                yield return row;
            }
        }

        [SampleSupport.Category("Funkcje C# 4.0")]
        [Title("Listing 8-14 : COM Interop i LINQ")]
        [Description("Przykład demonstruje, jak używać COM interop i LINQ.")]
        public void Listing_8_14_COMInteropLINQ()
        {
            string filename = Path.Combine(
                Environment.CurrentDirectory, "Data/SampleExcel.xlsx");

            const int firstNameCol = 0;
            const int lastNameCol = 1;
            const int stateCol = 5;

            var q = from row in GetExcelRowEnumerator(filename, 1)
                    where row[stateCol] == "WM"
                    select row;

            Console.WriteLine("Klienci w WM ({0})", q.Count());

            foreach (var row in q)
            {
                Console.WriteLine("{0}, {1}", 
                    row[lastNameCol].ToUpper(), row[firstNameCol] );
            }
        }

        [SampleSupport.Category("Funkcje C# 4.0")]
        [Title("Listing 8 : COM Interop")]
        [Description("Przykład Microsoftu demonstruje, jak używać COM interop.")]
        public void Listing_8_COMInterop()
        {
            // Z przykładów Microsoftu
            var excel = new Microsoft.Office.Interop.Excel.Application();
            excel.Visible = true;
            excel.Workbooks.Add();                    // argumenty opcjonalne
            excel.Cells[1, 1].Value = "Process Name"; // właściwość dynamiczna typu set
            excel.Cells[1, 2].Value = "Memory Usage"; // właściwość dynamiczna typu set

            var processes = Process.GetProcesses()
              .OrderByDescending(p => p.WorkingSet64)
              .Take(10);

            int i = 2;
            foreach (var p in processes)
            {
                excel.Cells[i, 1].Value = p.ProcessName;   // właściwość dynamiczna typu set
                excel.Cells[i, 2].Value = p.WorkingSet64;  // właściwość dynamiczna typu set
                i++;
            }

            Microsoft.Office.Interop.Excel.Range range = excel.Cells[1, 1];   // konwersja dynamiczna
            Excel.Chart chart = excel.ActiveWorkbook.Charts.
              Add(After: excel.ActiveSheet);         // argumenty nazwane i dynamiczne

            chart.ChartWizard(
              Source: range.CurrentRegion,
              Title: "Memory Usage in " + Environment.MachineName); // argumenty nazwane i opcjonalne

            //chart.ChartStyle = 45;


            chart.CopyPicture(Excel.XlPictureAppearance.xlScreen,
              Excel.XlCopyPictureFormat.xlBitmap,
              Excel.XlPictureAppearance.xlScreen);
        }

    }
}
