//: Beispiele zum Artikel 'Swift Tutorial, Teil I'

import UIKit

// gewöhnliche Optionals
if(true) {
  var x: Int? = 3   // x kann eine ganze Zahl oder 'nil' enthalten
  var y = 2
  // var z1 = x + y   // nicht erlaubt, x könnte nil enthalten
  var z2 = x! + y     // erzwungenes Unwrapping, löst Fehler aus, wenn x == nil
}

// Implicitly Unwrapped Optionals
if(true) {
  var x: Int! = 3   // x kann eine ganze Zahl oder 'nil' enthalten
  var y = 2
  var z = x + y     // ok, liefert 5
  x = nil
  // z = x + y      // syntaktisch OK, liefert aber einen Laufzeitfehler
}

// if let
if(true) {
  func funktionZurTexteingabe() -> String {
    // nur als Beispiel
    return "123abc"
  }
  
  var s = funktionZurTexteingabe()
  if let n = Int(s) {   // n hat den Datentyp Int
    print(n + 4)        // ok
  }
}


// Arrays
if(true) {
  print("--- Umgang mit Arrays ---")
  var x = [1, 2, 3]  // Int-Array mit drei Elementen
  print(x[1])        // Ausgabe 2
  var y = [Int]()    // leeres Int-Array
  y.append(12)
  y.append(27)
  y.insert(8, at: 1) // y enthält jetzt [12, 8, 27]
  
  var z = [[Int]]()  // leeres 2D-Array
  
  var data1 = [Int](repeating: 0, count: 10)  // Array aus 10 0-Elementen
  print(data1)
  // Array as 10x10 0-Elementen
  var data2 = [[Int]](repeating: [Int](repeating: 0, count: 10), count: 10)
  print(data2)
  print()
  print()
}

// Tupel
if(true) {
  let data = (-10, 0, 10)
  data.0        // -10
  data.1        // 0
  data.2        // 10
  let position = (row: 4, col: 7)
  position.row  // 4
  position.col  // 7
  
  func getName() -> (vorname: String, nachname: String) {
    // ... Code, um den Namen aus einer Datenbank zu lesen
    let v = "Peter"
    let n = "Schmitt"
    return (vorname: v, nachname: n)
  }
  
  let (s1, s2) = getName()
  print(s2)  // Schmitt
}

// Funktionen
if(true) {
  // Definition einer Funktion mit benannten Parametern
  func f1(a: Int, b: String) {
    // ... Code
  }
  
  // Aufruf
  f1(a: 123, b: "Eine Zeichenkette")
  
  
  // Definition einer Funktion mit unbenannten Parametern
  func f2(_ a: Int, _ b: String) {
    // ... Code
  }
  
  // Aufruf
  f2(123, "Eine Zeichenkette")
  
  // Definition einer Funktion mit zweinamigen Parametern
  func f3(abc a: Int, efg b: String) {
    // ... Code
  }
  
  // Aufruf
  f3(abc: 123, efg: "Eine Zeichenkette")
  
  
}

// Closures
if(true) {
  let data = Array(1...20)        // Datentyp [Int]
  
  // Funktion definieren und an map übergeben
  func calc(n: Int) -> Double {
    return sin(Double(n) / 10.0)
  }
  let result1 = data.map(calc)    // Datentyp [Double]
  
  // ditto, aber mit Closure
  let result2 = data.map( { (n:Int) -> Double in return sin(Double(n) / 10) } )
  
  // Kurzschreibweise
  let result3 = data.map( { sin(Double($0) / 10) } )
  
  // nachgestellt
  let result4 = data.map() { sin(Double($0) / 10) }
}

// Schleifen
if(true) {
  for i in 1...3 {  // Ausgabe: 1, 2, 3
    print(i)
  }
  for i in 1..<3 {  // Ausgabe: 1, 2
    print(i)
  }
  let data = [ "Hello", "for", "loop!" ]
  for s in data {
    print(s)
  }

  var n=7
  while(n>0) {  // Ausgabe 7, 5, 3, 1
    print(n)
    n -= 2
  }
}

// Schleifen im Playground
if(true) {
  var x=0.0
  while(x < .pi * 2.0) {
    let y = sin(x)
    x += .pi / 10
  }
}

// eigene Klassen
// Definition der Klasse
class Rectangle {
  var width:  Double
  var height: Double
  
  // Init-Funktion = Konstruktur
  init(width: Double, height: Double) {
    self.width  = width
    self.height = height
  }
  func area() -> Double {
    return width * height
  }
}

let r = Rectangle(width: 2.3, height: 0.7)
print(r.area())  // 1.61

extension Rectangle {
  func perimeter() -> Double {
    return (width + height) * 2.0
  }
}
print(r.perimeter())  // Ausgabe 6.0

extension Rectangle : CustomStringConvertible {
  var description : String {
    return "w=\(width) h=\(height)"
  }
}
print(r)              // Ausgabe w=2.3  h=0.7
