package aufgabe09;

import aufgabe09.entitaeten.Bestellposten;
import aufgabe09.entitaeten.Bestellung;
import aufgabe09.entitaeten.Produkt;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

public class DAO {

  private EntityManagerFactory eMF =
          Persistence.createEntityManagerFactory("Aufgabe09sose10PU");
  private EntityManager em = eMF.createEntityManager();

  public void produktAnlegen(String pname, int vk) {
    Produkt p = new Produkt(pname, vk);
    persist(p);
  }

  public void bestellungAnlegen(String kunde) {
    Bestellung b = new Bestellung(kunde);
    persist(b);
  }

  public void produktInBestellungErgaenzen(int bst, int pn, int anz) {
    Bestellung b = em.find(Bestellung.class, bst);
    if (b != null) {
      Produkt p = em.find(Produkt.class, pn);
      if (p != null) {
        Bestellposten bp = new Bestellposten(b, p, anz);
        b.addPosten(bp);
        //p.addPosten(bp); in Bestellung
        persist(b);
      } else {
        System.out.println("Produkt nicht gefunden");
      }
    } else {
      System.out.println("Bestellung nicht gefunden");
    }
  }

  public void produktAusBestellungLoeschen(int bstnr, int pnr) {
    Bestellung b = em.find(Bestellung.class, bstnr);
    if (b != null) {
      Bestellposten p = em.find(Bestellposten.class, pnr);
      if (p != null) {
        //Produkt pro = p.getProdukt(); in Bestellung
        //pro.deletePosten(p);
        b.deletePosten(p);
        remove(p);
        persist(b);
        //persist(pro);
      } else {
        System.out.println("Posten nicht gefunden");
      }
    } else {
      System.out.println("Bestellung nicht gefunden");
    }
  }

  public void produkteAnzeigen() {
    for (Produkt p : (List<Produkt>) em.createQuery(
            "SELECT p FROM Produkt p").
            getResultList()) {
      System.out.print(p);
    }
  }

  public void bestellungenAnzeigen() {
    for (Bestellung b : (List<Bestellung>) em.createQuery(
            "SELECT b FROM Bestellung b").
            getResultList()) {
      System.out.print(b);
    }
  }

  public void produktbestellungshaeufigkeiten() {
    for (Object[] o : (List<Object[]>) em.createQuery(
            "SELECT p.produktnr,p.verkaufspreis,SUM(b.anzahl) "
            + "  FROM Produkt p JOIN p.posten b "
            + "GROUP BY p.produktnr,p.verkaufspreis").
            getResultList()) {
      System.out.print(o[0] + " Bestellhaeufigkeit:" + o[2] + " Gesamtpreis:" + ((Integer) o[1] * (Long) o[2]) + "\n");
    }
  }

  public void nieBestellteProdukte() {
    for (Produkt p : (List<Produkt>) em.createQuery(
            "SELECT p FROM Produkt p"
            + " WHERE p.posten IS EMPTY").
            getResultList()) {
      System.out.print(p);
    }
  }

  public void produktpaareInBestellungen(int haeufig) {
    for (Object[] o : (List<Object[]>) em.createQuery(
            "SELECT p1.produktnr, p2.produktnr FROM Produkt p1, Produkt p2 "
            + "WHERE NOT(p1.produktnr = p2.produktnr)"
            + "  AND :haeufig<= (SELECT COUNT(b.bestnr) "
            + "                    FROM Bestellung b JOIN b.posten po1 JOIN b.posten po2"
            + "                   WHERE po1.produkt.produktnr=p1.produktnr "
            + "                     AND po2.produkt.produktnr=p2.produktnr) ").
            setParameter("haeufig", (long) haeufig).
            getResultList()) {
      if ((Integer) o[0] < (Integer) o[1]) {
        System.out.println("Paar: " + o[0] + " - " + o[1]);
      }
    }
  }

  public void bestellsummenBerechnen() {
    for (Bestellung b : em.createQuery("SELECT b FROM Bestellung b", Bestellung.class).getResultList()) {
      int summe = 0;
      for (Bestellposten bp : b.getPosten()) {
        summe += bp.getAnzahl() * bp.getProdukt().getVerkaufspreis();
      }
      System.out.println("Bestellung " + b.getBestnr() + ": Summe:" + summe);
    }
    /* nur in Hibernate
    for (Object[] o : (List<Object[]>) em.createQuery(
    "SELECT b.bestnr, SUM(po.anzahl*po.produkt.verkaufspreis) "
    + "  FROM Bestellung b JOIN b.posten po "
    + "  GROUP BY b.bestnr").
    getResultList()) {
    System.out.println("Bestellnummer:" + o[0] + " Gesamtsumme:" + o[1]);
    }
     */
  }

  public void kundenMitNamensteil(String teil) {
    for (String s : (List<String>) em.createQuery(
            "SELECT DISTINCT b.kunde "
            + "  FROM Bestellung b "
            + "  WHERE b.kunde LIKE '%" + teil + "%'").
            getResultList()) {
      System.out.println("Name: " + s);
    }
  }

  public void kundenMitNBestellungen(int n) {
    for (String s : (List<String>) em.createQuery(
            "SELECT DISTINCT b.kunde "
            + "  FROM Bestellung b "
            + "  WHERE :n <= (SELECT COUNT(bs) "
            + "              FROM Bestellung bs "
            + "              WHERE bs.kunde=b.kunde)").
            setParameter("n", (long) n).
            getResultList()) {
      System.out.println("Name: " + s);
    }
  }

  public void kundeAlleBestellungen() {
    for (String s : (List<String>) em.createQuery(
            "SELECT DISTINCT b.kunde "
            + "  FROM Bestellung b "
            + "  WHERE NOT EXISTS (SELECT bs.kunde "
            + "              FROM Bestellung bs "
            + "              WHERE NOT(bs.kunde=b.kunde))").
            getResultList()) {
      System.out.println("Name: " + s);
    }
  }

  void startdaten() {
    int anzahl = 0;
    try {
      anzahl = em.createQuery("SELECT p FROM Produkt p").getResultList().size();
    } catch (Exception e) {
      // nur zur Prüfung, ob überhaupt daten existieren
    }
    if (anzahl == 0) {
      Produkt[] p = {new Produkt("Bier", 129), new Produkt("Wodka", 799),
        new Produkt("Wein", 599), new Produkt("Korn", 199)};
      Bestellung b1 = new Bestellung("Anton M");
      for (Produkt pr : p) {
        b1.addPosten(new Bestellposten(b1, pr, 3));
      }
      Bestellung b2 = new Bestellung("Berti M");
      b2.addPosten(new Bestellposten(b2, p[0], 4));
      b2.addPosten(new Bestellposten(b2, p[2], 3));
      Bestellung b3 = new Bestellung("Conni M");
      b3.addPosten(new Bestellposten(b3, p[0], 2));
      b3.addPosten(new Bestellposten(b3, p[3], 5));
      //em.getTransaction().begin();
      persist(b1);
      persist(b2);
      persist(b3);
      persist(new Produkt("Wasser", 19));
      persist(new Bestellung("Det M"));
      //em.getTransaction().commit();
    }
  }

  /* Ausführung der Anfrage ql, für die eine "normale" Liste von
   * Objekten als Ergebnis erwartet wird. */
  public void anfragen(String ql) {
    //System.out.println(ql);
    try {
      Query query = em.createQuery(ql);
      Collection erg = query.getResultList();
      for (Iterator it = erg.iterator(); it.hasNext();) {
        System.out.println(it.next());
      }
    } catch (Exception e) {
      System.out.println("Anfrage gescheitert: " + e.getMessage());
    } finally {
      System.out.println("-------------------");
    }
  }

  /* Detailliertere Analyse des Ergebnisses der Anfrage ql, falls
   * Object-Arrays in der Ergebnisliste stehen, werden Elemente der
   * Arrays einzeln ausgegeben. */
  public void anfragen2(String ql) {
    System.out.println(ql);
    try {
      Query query = em.createQuery(ql);
      Collection erg = query.getResultList();
      for (Iterator it = erg.iterator(); it.hasNext();) {
        Object o = it.next();
        System.out.println(o + " :: " + o.getClass().getName());
        if (o.getClass().getName().equals("[Ljava.lang.Object;")) {
          Object oa[] = (Object[]) o;
          for (int i = 0; i < oa.length; i++) {
            System.out.println("  " + oa[i]);
          }
        }
      }
    } catch (Exception e) {
      System.out.println("Anfrage gescheitert: " + e.getMessage());
    }
  }

  public void schliessen() {
    if (em != null && em.isOpen()) {
      em.close();
    }
    if (eMF != null && eMF.isOpen()) {
      eMF.close();
    }
  }

  public void persist(Object object) {
    em.getTransaction().begin();
    try {
      em.persist(object);
      em.getTransaction().commit();
    } catch (ConstraintViolationException e) {
      System.out.println("Validierungproblem:");
      for (ConstraintViolation c : e.getConstraintViolations()) {
        System.out.println(c.getMessage());
      }
      em.getTransaction().rollback();
    } catch (Exception e) {
      System.out.print("Persistenzproblem: ");
      System.out.println(e.getClass());
      System.out.println(e.getMessage());
      Throwable c= e.getCause();
      if(c!=null && c instanceof ConstraintViolationException)
        for (ConstraintViolation cv : ((ConstraintViolationException)c).getConstraintViolations()) 
        System.out.println(cv.getMessage());
      if(em.getTransaction().isActive())
        em.getTransaction().rollback();
    } finally {
      //em.close();
    }
  }

  public void remove(Object object) {
    em.getTransaction().begin();
    try {
      em.remove(object);
      em.getTransaction().commit();
    } catch (ConstraintViolationException e) {
      System.out.println("Validierungproblem:");
      for (ConstraintViolation c : e.getConstraintViolations()) {
        System.out.println(e.getMessage());
      }
      em.getTransaction().rollback();
    } catch (Exception e) {
      System.out.print("Persistenzproblem: ");
      System.out.println(e.getMessage());
      em.getTransaction().rollback();
    } finally {
      //em.close();
    }
  }
}
