/**
 * Oppgave19_4.java  - "Programmering i Java", 4.utgave - 2011-02-28
 * Løsningen er en utvidelse av en løsning utviklet av Simon Toresen og videreutviklet
 * av Hans Roar Sandberg for tidligere utgaver av boka.
 *
 * Denne oppgaven lager et grensesnitt til oppgave 2, kapittel 13. For å gjøre
 * koblingen mellom det gamle registeret og den grafiske tabellen benytter vi
 * en egen utgave av TableModel. Se kapittel 19.9.
 */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import javax.swing.table.*;
import mittBibliotek.MinDialog;

public class Oppgave19_4 {
  public static void main(String[] args) {
    /* Oppretter et register. */
    Register register = new Register();

    register.regNyVare(new Vare("Anir vertikal mus PS/2 kontakt", 1, 399.0, 10, 3));
    register.regNyVare(new Vare("Logitech Cordless Mousemann Wheel PS2", 2, 549.0, 10, 3));
    register.regNyVare(new Vare("Logitech Cordless Wheel PS2 med hjul", 3, 469.0, 10, 3));
    register.regNyVare(new Vare("Logitech Desktop iTouch trådløs", 4, 889.0, 10, 3));
    register.regNyVare(new Vare("Logitech Desktop PRO trådløs tast", 5, 1099.0, 10, 3));
    register.regNyVare(new Vare("Logitech Gaming Mouse PS2/USB", 6, 399.0, 10, 3));
    register.regNyVare(new Vare("Logitech Pilot mus PS2 OEM med hjul", 7, 129.0, 10, 3));
    register.regNyVare(new Vare("Logitech Pilot Wheel PS2/USB med hjul", 8, 249.0, 10, 3));
    register.regNyVare(new Vare("Microsoft Cordless Wheel mus PS/2", 9, 395.0, 10, 3));
    register.regNyVare(new Vare("Microsoft Defender PS/2", 10, 109.0, 10, 3));
    register.regNyVare(new Vare("Microsoft Defender seriell", 11, 109.0, 10, 3));
    register.regNyVare(new Vare("Microsoft Explorer PS2/USB optisk", 12, 495.0, 10, 3));
    register.regNyVare(new Vare("Microsoft Intelli mus PS/2 med hjul", 13, 179.0, 10, 3));
    register.regNyVare(new Vare("Multicom mus med surfehjul PS/2", 14, 89.0, 10, 3));
    register.regNyVare(new Vare("Multicom mus med surfehjul seriell", 15, 89.0, 10, 3));

    /* Oppretter et vindu. */
    MittVindu vindu = new MittVindu("Oppgave19_4", register);
    vindu.setVisible(true);
  }
}

/**
 * Klassen MittVindu
 */
class MittVindu extends JFrame {
  private static final String NY_VARE_TEKST = "Ny vare";  // til bruk på ...
  private static final String ENDRE_VARE_TEKST = "Endre data";  // ...
  private static final String SLETT_VARE_TEKST = "Slett vare";  // ... trykk-knappene

  private RegisterModell varedata;
  private JTable vareliste;
  private VareDialog enDialog = new VareDialog(this);

  public MittVindu(String tittel, Register register) {
    super(tittel);

    varedata = new RegisterModell(register);
    vareliste = new JTable(varedata);

    /* Setter ikke-prop. font slik at varenummer og priser blir pent under hverandre */
    Font std = vareliste.getFont();
    Font nyF = new Font("Monospaced", std.getStyle(), std.getSize());
    vareliste.setFont(nyF);

    vareliste.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    add(new JScrollPane(vareliste), BorderLayout.NORTH);
    add(new TrykkPanel(), BorderLayout.SOUTH);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    pack();
  }

  /* Beskriver TrykkPanel */
  private class TrykkPanel extends JPanel {
    public TrykkPanel() {
      setLayout(new GridLayout(1, 3));
      Knappelytter lytter = new Knappelytter();

      JButton knapp = new JButton(NY_VARE_TEKST);
      knapp.setMnemonic('N');
      knapp.addActionListener(lytter);
      add(knapp);

      knapp = new JButton(ENDRE_VARE_TEKST);
      knapp.setMnemonic('E');
      knapp.addActionListener(lytter);
      add(knapp);

      knapp = new JButton(SLETT_VARE_TEKST);
      knapp.setMnemonic('S');
      knapp.addActionListener(lytter);
      add(knapp);
    }
  }

  /* Lytter etter knappetrykk. */
  private class Knappelytter implements ActionListener {
    public void actionPerformed(ActionEvent hendelse) {
      if (hendelse.getActionCommand().equals(NY_VARE_TEKST)) {
        Vare enVare = enDialog.visDialogNy();
        varedata.registrerNyVare(enVare);
        varedata.fireTableDataChanged();
      } else {
        int valgtRad = vareliste.getSelectedRow();
        if (valgtRad < 0) {
          JOptionPane.showMessageDialog(null, "Du må velge rad først!");
        } else {
          if (hendelse.getActionCommand().equals(ENDRE_VARE_TEKST)) {
            Vare vare = varedata.finnVare(valgtRad);
            enDialog.visDialogEndre(vare);
            varedata.fireTableDataChanged();
          } else if (hendelse.getActionCommand().equals(SLETT_VARE_TEKST)) {
            varedata.fjernVare(valgtRad);
            varedata.fireTableDataChanged();
          }
        }
      }
    }
  }
}


/**
 * Klassen RegisterModell.
 *
 */
class RegisterModell extends AbstractTableModel {

  private static final String[] kolonnenavn = { "Artikkel", "Varenr", "Pris",  "På lager", "Bestillingsgrense" };
  private Register register;

  public RegisterModell(Register register) {
    this.register = register;
  }

  /**
   * TableModel metoder
   *
   * Dette er metoder som JTable benytter til å finne størrelse og data. Ved å
   * lage våre egne utgaver av disse kan vi lage en direkte kobling til registeret.
   */

  /* Registrerer ny vare */
  public void registrerNyVare(Vare nyVare) {
    if (nyVare != null) {
      if (!register.regNyVare(nyVare)) {
        JOptionPane.showMessageDialog(null, "Vare med dette nr. er registrert fra før.");
      }
      fireTableDataChanged();
    }
  }

  /* Henter ut vare fra bestemt rad */
  public Vare finnVare(int valgtRad) {
    int varenr = finnVarenr(valgtRad);
    return register.finnVare(varenr);
  }

  /* Fjerner en vare fra registeret */
  public void fjernVare(int valgtRad) {
    int varenr = finnVarenr(valgtRad);
    register.fjernVare(varenr);
    fireTableDataChanged();
  }

  /* Hjelpmetode */
  private int finnVarenr(int valgtRad) {
    int nr = 0;
    try {
      nr = Integer.parseInt(getValueAt(valgtRad, 1).toString().trim());
    } catch (NumberFormatException e) {
      System.out.println("Feil oppstått: " + e); // skal aldri skje
    }
    return nr;
  }

  /* Kolonnenavnene */
  public String getColumnName(int indeks) {
    return (indeks >= 0 && indeks < kolonnenavn.length) ? kolonnenavn[indeks] : "";
  }

  /* Brukeren skal ikke kunne editere i dataene */
  public boolean isCellEditable(int rad, int kolonne) {
    return false;
  }

  /* Antall kolonner. */
  public int getColumnCount() {
    return kolonnenavn.length;
  }

  /* Antall rader er lik antall varer i registeret. */
  public int getRowCount() {
    return (register == null) ? 0 : register.finnAntallVarer();
  }

  /* Verdier som skal plasseres i tabellen. */
  public Object getValueAt(int rad, int kolonne) {
    java.util.Formatter form = new java.util.Formatter();
    switch (kolonne) {
      case 0:
        return register.finnIndeksertVare(rad).getVarenavn();

      case 1: {
        int varenr = register.finnIndeksertVare(rad).getVarenr();
        return form.format("%6d", varenr);
      }

      case 2: {
        double pris = register.finnIndeksertVare(rad).getPris();
        return form.format("%8.2f", pris);
      }

      case 3: {
        int påLager = register.finnIndeksertVare(rad).getPåLager();
        return form.format("%6d", påLager);
      }

      case 4: {
        int grense = register.finnIndeksertVare(rad).getGrense();
        return form.format("%6d", grense);
      }

      default:
        return null;
    }
  }
}

/**
 * Klassen VareDialog
 */
class VareDialog extends MinDialog {
  private JTextField varenrFelt = new JTextField(4);
  private JTextField vareFelt = new JTextField(20);
  private JTextField varePrisFelt = new JTextField(8);
  private JTextField varePåLagerFelt = new JTextField(8);
  private JTextField vareGrenseFelt = new JTextField(8);

  public VareDialog(JFrame forelder) {
    super(forelder, "Vare");
     add(new JPanel(), BorderLayout.NORTH);
     add(new JPanel(), BorderLayout.WEST);
     add(new DataPanel(), BorderLayout.CENTER);
     add(new JPanel(), BorderLayout.EAST);
     add(getKnappepanel(), BorderLayout.SOUTH);
     pack();
   }

   private class DataPanel extends JPanel {
    public DataPanel() {
      setLayout(new GridLayout(5, 2));
      add(new JLabel("Varenr:  ",JLabel.RIGHT));
      add(varenrFelt);
      add(new JLabel("Artikkel:  ",JLabel.RIGHT));
      add(vareFelt);
      add(new JLabel("Pris:  ",JLabel.RIGHT));
      add(varePrisFelt);
      add(new JLabel("Lagerbeholdning:  ",JLabel.RIGHT));
      add(varePåLagerFelt);
      add(new JLabel("Bestillingsgrense:  ",JLabel.RIGHT));
      add(vareGrenseFelt);
    }
   }

   public void visDialogEndre(Vare varen) { // for endring av data
    varenrFelt.setText("" + varen.getVarenr());
    varenrFelt.setEditable(false);
    vareFelt.setText(varen.getVarenavn());
    vareFelt.setEditable(false);
    java.util.Formatter form = new java.util.Formatter();
    varePrisFelt.setText(form.format("%8.2f", varen.getPris()).toString());
    varePåLagerFelt.setText("" + varen.getPåLager());
    vareGrenseFelt.setText("" + varen.getGrense());
    setOk(false);
    pack();
    setVisible(true);
    if (isOk()) {
      varen.setPris(Double.parseDouble(varePrisFelt.getText()));
      varen.setPåLager(Integer.parseInt(varePåLagerFelt.getText()));
      varen.setGrense(Integer.parseInt(vareGrenseFelt.getText()));
    }
  }

   public Vare visDialogNy() {  // ny vare
    varenrFelt.setText("");
    vareFelt.setText("");
    varePrisFelt.setText("");
    varePåLagerFelt.setText("");
    vareGrenseFelt.setText("");
    setOk(false);
    pack();
    setVisible(true);
    if (isOk()) {
      return new Vare(vareFelt.getText(),Integer.parseInt(varenrFelt.getText()), Double.parseDouble(varePrisFelt.getText()),
                    Integer.parseInt(varePåLagerFelt.getText()), Integer.parseInt(vareGrenseFelt.getText()));
    }
    return null;
   }

   protected boolean okData() {
      try {
        varenrFelt.setText(ryddOpp(varenrFelt.getText()));
        int varenr = Integer.parseInt(varenrFelt.getText());
        varePrisFelt.setText(ryddOpp(varePrisFelt.getText()));
        double varePris = Double.parseDouble(varePrisFelt.getText());
        varePåLagerFelt.setText(ryddOpp(varePåLagerFelt.getText()));
        int påLager = Integer.parseInt(varePåLagerFelt.getText());
        vareGrenseFelt.setText(ryddOpp(vareGrenseFelt.getText()));
        int grense = Integer.parseInt(vareGrenseFelt.getText());
        if (varePris < 0.0 || påLager < 0 || grense < 0) {
          throw new NumberFormatException();
        }
      } catch (NumberFormatException e) {
        JOptionPane.showMessageDialog(VareDialog.this,"Alle data unntatt artikkelnavn må være tall. Alle tall unntatt pris må være heltall. Negative tall godtas ikke.");
       return false;
     }
     return true;
   }

   /* Fjerner blanke og skifter ut ev. desimalkomma med punktum */
   private String ryddOpp(String formattertTall) {
     StringBuilder tall = new StringBuilder();
     for (int i = 0; i < formattertTall.length(); i++) {
       if (formattertTall.charAt(i) != ' ') {  // hopper over alle blanke
         if (formattertTall.charAt(i) == ',') {
           tall.append('.');
         } else {
           tall.append(formattertTall.charAt(i));
         }
       }
     }
     return tall.toString();
   }
}

/***** problemdomene-klasser kommer her ********/

/**
 * Klassen Register
 */
class Register {
  private ArrayList<Vare> liste = new ArrayList<Vare>();

  public int finnAntallVarer() {
    return liste.size();
  }

  /**
   * Denne metoden er ny i forhold til kapittel 13. Trengs i klassen RegisterModell.
   */
  public Vare finnIndeksertVare(int indeks) {
    return (indeks >= 0 && indeks < liste.size()) ? liste.get(indeks) : null;
  }

  /**
   * Henter ut vare med et bestemt nummer.
   * Returnerer null hvis vare med dette nummer ikke fins.
   */
  public Vare finnVare(int varenr) {
    for (Vare enVare : liste) {
      if (enVare.getVarenr() == varenr) {
        return enVare;
      }
    }
    return null;
  }

  /**
   * Registrerer ny vare.
   * Returnerer false hvis vare med dette nummer allerede er registrert.
   */
  public boolean regNyVare(Vare vare) {
    if (finnVare(vare.getVarenr()) != null)  {
      return false;  // RETUR, vare med dette nr fins fra før
    }
    liste.add(vare);
    return true;
  }

  /**
   * Fjerner en vare fra registerer.
   * Returnerer false hvis vare med dette nummer allerede er registrert.
   */
  public boolean fjernVare(int varenr) {
    for (int i = 0; i < liste.size(); i++) {
      Vare vare = liste.get(i);
      if (vare.getVarenr() == varenr) {
        liste.remove(i);
        return true;
      }
    }
    return false;
  }

  /**
   * Registrerer endring i varebeholdningen.
   * Returnerer -1 hvis vare med dette nummer allerede er registrert.
   * Returnerer 0 hvis ikke nok på lager. Endringen registreres ikke.
   * Returnerer 1 hvis OK.
   */
  public int oppdaterVarebeholdning(int varenr, int antSolgt) {
    Vare vare = finnVare(varenr);
    if (vare == null) {
      return -1;
    } else {
      if (vare.endreLagerbeholdning(antSolgt)) {
        return 1;
      } else {
        return 0;
      }
    }
  }

  /**
   * Lager bestillingsliste.
   */
  public String lagBestillingsliste() {
    String rapport = "";
    for (Vare enVare : liste) {
      double mengde = enVare.finnBestKvantum();
      if (mengde > 0.000001) {
        rapport += enVare.getVarenavn() + " bestill : "+ mengde;
      }
    }
    return rapport;
  }

  public String toString() {
    ArrayList<String> tabell = new ArrayList<String>();
    for (Vare enVare : liste) {
      tabell.add(enVare.toString());
    }
    java.text.Collator koll = java.text.Collator.getInstance(); // norsk sortering, se side 407-408
    StringBuilder buffer = new StringBuilder(); // se side 278
    while (tabell.size() != 0) {
      int minst = 0;
      for (int i = 1; i < tabell.size(); i++) {
        String varelinje = tabell.get(i);
        if (koll.compare(varelinje, tabell.get(minst)) < 0) minst = i;
      }
      buffer.append(tabell.get(minst) + "\n");
      tabell.remove(minst);
    }
    return buffer.toString();
  }
}

/**
 * Vare.java
 * Modifisert utgave av klassen Vare, side 186.
 */

class Vare {
  public static final double MOMS = 24.0;
  public static final double MOMSFAKTOR = 1.0 + MOMS / 100.0;
  public static final int BESTILLINGSFAKTOR = 4;

  private final String varenavn;
  private final int varenr;
  private double pris; // pris pr. enhet
  private int påLager;
  private int grense; // for bestilling

  public Vare(String varenavn, int varenr, double pris, int påLager, int grense) {
    this.varenavn = varenavn;
    this.varenr = varenr;
    this.pris = pris;
    this.påLager = påLager;
    this.grense = grense;
  }

  public Vare(String varenavn, int varenr, int påLager, int grense) {
    this(varenavn, varenr, 0.0, påLager, grense);
  }

  public String getVarenavn() {
    return varenavn;
  }

  public int getVarenr() {
    return varenr;
  }

  public double getPris() {
    return pris;
  }

  public void setPris(double pris) {
    this.pris = pris;
  }

  public int getGrense() {
    return grense;
  }

  public void setGrense(int nyGrense) {
    grense = nyGrense;
  }

  public int getPåLager() {
    return påLager;
  }

  public void setPåLager(int nyPåLager) {
    påLager = nyPåLager;
  }

  public int finnBestKvantum() {
    return (påLager < grense) ? BESTILLINGSFAKTOR * grense : 0;
  }

  /**
   * Endring av lagerbeholdning.
   * Den kan være positiv eller negativ. Men det er ikke
   * mulig å ta ut mer enn det som fins på lager. Hvis klienten
   * prøver på det, vil metoden returnere false, og intet uttak gjøres.
   */
  public boolean endreLagerbeholdning(int endring) {
    if (påLager + endring < 0) {
      return false;
    } else {
      påLager += endring;
      return true;
    }
  }

  public String toString() {
    java.util.Formatter f = new java.util.Formatter();
    f.format("%.2f", pris);
    return varenr + ": " + varenavn + ", pris pr. enhet kr " + f.toString() + " u.moms.\n"
                         + "Antall på lager: " + påLager + ", nedre grense for bestilling: " + grense + ".";
  }
}