using System;
using System.Data;
using System.Web.UI;

public partial class BugTrackerByHand : Page
{
   protected void Page_Load(object sender, EventArgs e)
   {
      if (!IsPostBack)
      {
         // Wywołanie metody, która tworzy tabele i związki.
         DataSet ds = CreateDataSet();

         // Ustawienie pierwszej tabeli jako źródła danych dla siatki.
         BugsGridView.DataSource = ds.Tables["Bugs"];
         BugsGridView.DataBind();

         BugConstraintsGridView.DataSource = ds.Tables["Bugs"].Constraints;
         BugConstraintsGridView.DataBind();
      }
   }

   private DataSet CreateDataSet()
   {
      // Ustanowienie egzemplarza nowego obiektu DataSet, który wypełnimy tabelami i związkami.
      DataSet dataSet = new DataSet();

      // Utworzenie tabeli Bugs i jej kolumn imitujących atrybuty z bazy danych SQL.
      DataTable tblBugs = CreateBugsTable();
      dataSet.Tables.Add(tblBugs);

      // Utworzenie tabeli Product i jej kolumn imitujących atrybuty z bazy danych SQL.
      DataTable tblProduct = CreateProductTable();
      dataSet.Tables.Add(tblProduct);

      // Utworzenie tabeli People i jej kolumn imitujących atrybuty z bazy danych SQL.
      DataTable tblPeople = CreatePeopleTable();
      dataSet.Tables.Add(tblPeople);

      // Tworzymy ograniczenie klucza zewnętrznego People.PersonID = Bugs.ReporterID.
      CreateForeignKeyAndDataRelation(dataSet, "BugToPerson", tblPeople, "PersonID", tblBugs, "ReporterID");

      // Tworzymy ograniczenie klucza zewnętrznego Product.ProductID = Bugs.ProductID.
      CreateForeignKeyAndDataRelation(dataSet, "BugToProduct", tblProduct, "ProductID", tblBugs, "ProductID");

      return dataSet;
   }

   #region Generic Table Methods

   private void AddNewPrimaryKeyColumn(DataTable table, string ColumnName)
   {
      AddNewColumn(table, "System.Int32", ColumnName, false);
      DataColumn PkColumn = table.Columns[ColumnName];

      // Ustawienie kolumny tak, aby pole automatycznie zwiększało wartość (autoinkrementacja).
      PkColumn.AutoIncrement = true;       // Autoinkrementacja.
      PkColumn.AutoIncrementSeed = 1;      // Rozpoczęcie od wartości 1.
      PkColumn.AutoIncrementStep = 1;      // Zwiększenie o 1.

      // Upewnienie się, że wszystkie wartości są unikalne.
      string constraintName = String.Format("Unique_{0}", ColumnName);
      UniqueConstraint constraint = new UniqueConstraint(constraintName, PkColumn);
      table.Constraints.Add(constraint);

      // Ustawienie kolumny jako klucza podstawowego tabeli.
      DataColumn[] columnArray = new DataColumn[] { PkColumn };
      table.PrimaryKey = columnArray;
   }

   private void AddNewColumn(DataTable table, string ColumnType, string ColumnName, bool AllowNulls, object DefaultValue, int MaxLength)
   {
      DataColumn newColumn = table.Columns.Add(ColumnName, Type.GetType(ColumnType));
      newColumn.AllowDBNull = AllowNulls;
      newColumn.MaxLength = MaxLength;
      newColumn.DefaultValue = DefaultValue;
   }

   private void AddNewColumn(DataTable table, string ColumnType, string ColumnName, bool AllowNulls, object DefaultValue)
   {
      AddNewColumn(table, ColumnType, ColumnName, AllowNulls, DefaultValue, -1);
   }

   private void AddNewColumn(DataTable table, string ColumnType, string ColumnName, bool AllowNulls)
   {
      AddNewColumn(table, ColumnType, ColumnName, AllowNulls, null, -1);
   }

   private void CreateForeignKeyAndDataRelation(DataSet dataSet, string relationName, DataTable parentTable, string primaryKeyColumnName, DataTable childTable, string foreignKeyColumnName)
   {
      // Pobranie odniesień do powiązanych kolumn.
      DataColumn primaryKeyColumn = parentTable.Columns[primaryKeyColumnName];
      DataColumn foreignKeyColumn = childTable.Columns[foreignKeyColumnName];
      String foreignKeyConstraintName = String.Format("FK_{0}", relationName);

      // Tworzymy ograniczenie klucza zewnętrznego.
      ForeignKeyConstraint fk = new ForeignKeyConstraint(foreignKeyConstraintName, primaryKeyColumn, foreignKeyColumn);
      fk.DeleteRule = Rule.Cascade;   // Jeżeli rekord nadrzędny zostanie usunięty, wtedy wszystkie powiązane z nim rekordy w tabeli potomnej również zostaną usunięte.
      fk.UpdateRule = Rule.Cascade;
      childTable.Constraints.Add(fk);  // Dodanie nowego ograniczenia.

      // Dodanie DataRelation przedstawiającego FKConstraint względem DataSet.
      DataRelation relation = new DataRelation(relationName, primaryKeyColumn, foreignKeyColumn);
      dataSet.Relations.Add(relation);
   }

   #endregion

   #region Bugs Table

   private DataTable CreateBugsTable()
   {
      DataTable tblBugs = new DataTable("Bugs");

      // Dodanie kolumn.
      AddNewPrimaryKeyColumn(tblBugs, "BugID");
      AddNewColumn(tblBugs, "System.Int32", "ProduktID", false, 1);
      AddNewColumn(tblBugs, "System.String", "Wersja", false, "0.1", 50);
      AddNewColumn(tblBugs, "System.String", "Opis", false, "", 8000);
      AddNewColumn(tblBugs, "System.Int32", "ZgłaszającyID", false);

      // Dodanie rekordów do tabeli.
      AddNewBug(tblBugs, 1, "0.1", "Awaria podczas wczytywania", 5);
      AddNewBug(tblBugs, 1, "0.1", "Nie pokazuje poprawnie osoby zgłaszającej błąd", 5);
      AddNewBug(tblBugs, 1, "0.1", "Nie wyświetla historii poprzednich działań", 6);
      AddNewBug(tblBugs, 1, "0.1", "Próba ponownego wczytania kończy się niepowodzeniem", 5);
      AddNewBug(tblBugs, 2, "0.1", "Utrata danych przez noc", 5);
      AddNewBug(tblBugs, 2, "0.1", "Kod HTML nie jest prawidłowo wyświetlany", 6);
      return tblBugs;
   }

   private void AddNewBug(DataTable bugTable, int product, string version, string description, int reporter)
   {
      DataRow newRow = bugTable.NewRow();
      newRow["ProductID"] = product;
      newRow["Version"] = version;
      newRow["Description"] = description;
      newRow["ReporterID"] = reporter;
      bugTable.Rows.Add(newRow);
   }


   #endregion

   #region Product Table

   private DataTable CreateProductTable()
   {
      DataTable tblProduct = new DataTable("lkProduct");

      // Dodanie kolumn.
      AddNewPrimaryKeyColumn(tblProduct, "ProductID");
      AddNewColumn(tblProduct, "System.String", "ProductDescription", false, "", 8000);

      // Dodanie rekordów do tabeli.
      AddNewProduct(tblProduct, "BugX Bug Tracking");
      AddNewProduct(tblProduct, "PIM - My Personal Information Manager");
      return tblProduct;
   }

   private void AddNewProduct(DataTable productTable, string description)
   {
      DataRow newRow = productTable.NewRow();
      newRow["ProductDescription"] = description;
      productTable.Rows.Add(newRow);
   }

   #endregion

   #region People Table

   private DataTable CreatePeopleTable()
   {
      DataTable tblPeople = new DataTable("People");

      // Dodanie kolumn.
      AddNewPrimaryKeyColumn(tblPeople, "PersonID");
      AddNewColumn(tblPeople, "System.String", "FullName", false, "", 8000);
      AddNewColumn(tblPeople, "System.String", "Email", false, "", 100);
      AddNewColumn(tblPeople, "System.String", "Phone", false, "", 20);
      AddNewColumn(tblPeople, "System.Int32", "Role", false, 0);

      // Dodanie rekordów do tabeli.
      AddNewPerson(tblPeople, "Dan Maharry", "danm@hmobius.com", "212-555-0285", 1);
      AddNewPerson(tblPeople, "Jesse Liberty", "jliberty@libertyassociates.com", "617-555-7301", 1);
      AddNewPerson(tblPeople, "Dan Hurwitz", "dhurwitz@stersol.com", "781-555-3375", 1);
      AddNewPerson(tblPeople, "John Galt", "jGalt@franconia.com", "617-555-9876", 1);
      AddNewPerson(tblPeople, "John Osborn", "jOsborn@oreilly.com", "617-555-3232", 3);
      AddNewPerson(tblPeople, "Ron Petrusha", "ron@oreilly.com", "707-555-0515", 2);
      AddNewPerson(tblPeople, "Tatiana Diaz", "tatiana@oreilly.com", "617-555-1234", 2);
      return tblPeople;
   }

   private void AddNewPerson(DataTable table, string name, string email, string phone, int role)
   {
      DataRow newRow = table.NewRow();
      newRow["FullName"] = name;
      newRow["email"] = email;
      newRow["Phone"] = phone;
      newRow["Role"] = role;
      table.Rows.Add(newRow);
   }

   #endregion

}
