import java.awt.*;
import java.awt.event.*;
import java.applet.Applet;
import java.io.*;
import java.util.*;


public class Redaktor1 extends Frame implements ActionListener {

  //failid asuvad kaartidel yxteise peal.
  CardLayout cardLayout = new CardLayout();
  Panel cardPanel = new Panel(cardLayout);

  //aluminse menyy nupud
  Panel buttonPanel = new Panel(new GridLayout(1, 0));

  //siia hakatakse panema avatud failide nuppe
  Panel fileButtonPanel = new Panel(new GridLayout(1, 0));

  //passwordi dialogi panel.
  Dialog passwordDialog = new Dialog(this, "Parool", true);
  //password ise
  TextField passwordField = new TextField("", 30);

  //kasutaja mratu rea pikkus, mille jrgi salvestamisel tykeldatakse ridu
  TextField rowLen = new TextField("", 5);
  // kas passwordi kysitaxe faili lugemisel/salvestamisel.
  Checkbox passwordCheckbox = new Checkbox();

  // alumise menyy nupud
  Button readButton=new Button("Loe");
  Button saveButton=new Button("Salvesta");
  Button closeButton=new Button("Sulge");
  Button prefButton=new Button("Seaded");
  Button exitButton=new Button("Vlju");
  // passwordi dialoogi nupuke
  Button okButton=new Button("Valmis");

  // sisseloetud failide nimed.
  Map files = new HashMap();
  // parajasti aktiivne nupp (ehk siis ka fail millega tegeldakse)
  String activeButtonName = null;

  public Redaktor1(){
    // tekitame alumise nupurea.

    setLayout(new BorderLayout());

    buttonPanel.add(readButton);
    buttonPanel.add(saveButton);
    buttonPanel.add(closeButton);
    buttonPanel.add(prefButton);
    buttonPanel.add(exitButton);

    add(fileButtonPanel, BorderLayout.NORTH);
    add(buttonPanel, BorderLayout.SOUTH);
    add(cardPanel, BorderLayout.CENTER);

    // teeme valmis ka passwordi dialoogi, hiljem hea kasutada
    Panel passwordPanel = new Panel();
    passwordPanel.add(new Label("Parool:"));
    passwordPanel.add(passwordField);
    passwordPanel.add(okButton);
    passwordDialog.add(passwordPanel);

    // seadete paneel
    Panel prefPanel = new Panel(new GridLayout(0, 2));
    prefPanel.add(new Label("Rea pikkus:"));
    prefPanel.add(rowLen);
    prefPanel.add(new Label("Ksi parooli:"));
    prefPanel.add(passwordCheckbox);

    Panel prefPanel1 = new Panel(new FlowLayout(FlowLayout.LEFT));
    prefPanel1.add(prefPanel);
    cardPanel.add("pref", prefPanel1);

    // ks tyhi paneel ka et seaded ei tuleks kohe ette kui yhtki faili pole avatud.
    Panel emptyPanel = new Panel();
    cardPanel.add("empty", emptyPanel);

    // paneme nuppude kylge tegevused.
    saveButton.addActionListener(this);
    readButton.addActionListener(this);
    closeButton.addActionListener(this);
    exitButton.addActionListener(this);
    prefButton.addActionListener(this);
    okButton.addActionListener(this);

    // rea pikkuse teksboxi kontrollime et sinna miskit jama ei kirjutata.
    // ainult numbrid.
    rowLen.addKeyListener( new KeyListener(){
	                            public void keyTyped(KeyEvent e) {}
	                            public void keyPressed(KeyEvent e) {}
                              public void keyReleased(KeyEvent e){
                                if (rowLen.getText()== null || rowLen.getText().trim().length() == 0) {
                                  rowLen.setText("");
                                  return;
                                }
                                String numbers = "";
                                int pos = rowLen.getCaretPosition();
                                for( int i=0; i<rowLen.getText().length(); i++) {
                                  if (Character.isDigit(rowLen.getText().charAt(i))) {
                                    numbers += rowLen.getText().charAt(i);
                                  }
                                }
                                rowLen.setText(numbers);
                                rowLen.setCaretPosition(pos);
                              }
                            }
                           );

    // redaktori konstrueerimine lpetatud, nitame alustusex tyhjust.
    cardLayout.show(cardPanel, "empty");
  }

  public void actionPerformed(ActionEvent e){
    if (e.getSource() == saveButton) {
      // pressiti Salvesta, eks proovime salvestada
      write();
    } else if (e.getSource() == readButton) {
      // pressiti Loe, peax midagi siis sisse lugema..
      open();
    } else if (e.getSource() == closeButton) {
      // Sulge, koristame nupukese ja kaardi ja faili nime.
      close();
    } else if (e.getSource() == exitButton) {
      // Vlju
      setVisible(false);
      System.exit(0);
    } else if (e.getSource() == prefButton) {
      // Seaded, nitame seadete kaarti.
      cardLayout.show(cardPanel, "pref");
      this.setTitle("Seaded");
      activeButtonName = null;
      this.validate();
    } else if (e.getSource() == okButton) {
      // Valmis, passwordi dialogi juures. koristame dialogi.
      passwordDialog.dispose();
    } else if (e.getSource() instanceof Button) {
      // Nii, vajutati faili nupukest...
      // ...jtame meelde buttoni nime
      activeButtonName = ((Button)e.getSource()).getName();
      // ...muudame akna tiitlit vastavalt faili nimele ...
      File f = (File)files.get(activeButtonName);
      if (f==null) {
        this.setTitle("");
      } else {
        this.setTitle(f.getAbsolutePath());
      }

      // ... ja nitame valitud faili
      cardLayout.show(cardPanel, activeButtonName);
      this.validate();
    }
  }

  private void open() {
    // faili kysimise dialog
    FileDialog fd = new FileDialog(this);
    fd.setMode(FileDialog.LOAD);
    fd.show();
    //faili nimi viks nyyd olemas olla, kontrollime...
    try {
      if (fd.getFile() != null) {
        // kasutaja valis mingi faili, enne uue nupu lisamist prooviks lugeda...
        File f = new File(fd.getDirectory(), fd.getFile());
        byte[] bytes = readFile(f);
        // kysime passwordi
        askPassword();
        String content = new String(crypt(bytes));

        // lisame uue nupu..
        Button b = new Button(f.getName());
        b.addActionListener(this);
        fileButtonPanel.add(b);

        // lisame uue texti vlja ja kaardi
        TextArea ta = new TextArea();
        ta.setText(content);
        ta.setName("t" + b.getName());

        cardPanel.add(b.getName(), ta);
        cardLayout.show(cardPanel, b.getName());

        // ...jtame faili nime meelde salvestamisex
        files.put(b.getName(), f);

        // paneme avatava faili aktiivseks
        activeButtonName = b.getName();

        // akna tiitli muudame vastavalt faili nimele
        this.setTitle(f.getAbsolutePath());

        // ja lpuks siis muutused nhtavax
        this.validate();
      }
    } catch (IOException ioe) {
      System.out.println("Viga: " + ioe);
    }
  }

  private void write() {
    if (activeButtonName == null) {
      // pole aktiivset faili pole ka miskit salvestada.
      return;
    }

    Component c = findComponent(cardPanel, "t" + activeButtonName);
    if (c == null) {
      // ei leitud vastavat text areat !?
      // kui ei leitud siis ei saa ka midagi salvestada.
      return;
    }

    String content = "";
     // vaatame kas kasutaja on mranud rea rea pikkuse
    if (rowLen.getText().length() > 0) {

       // maksimaalne rea pikkus
      int maxLen = Integer.parseInt(rowLen.getText());

      // hakkame ridu juppideks tegema, kui vaja.
      try {
        BufferedReader in = new BufferedReader (
                              new StringReader(((TextArea)c).getText()));
        StringWriter writer = new StringWriter();
        PrintWriter out = new PrintWriter(writer);

       // esimene rida...
        String line = in.readLine();
        while (line != null) {
          while (line.length() > maxLen) {
           // rida liiga pikk, vaatame kas kusagil on  tyhik
            int breakPos = line.substring(0, maxLen).lastIndexOf(' ');
            if (breakPos > 0) {
              // oli tyhik, poolitame rea tyhiku kohalt
              out.println(line.substring(0, breakPos));
              // ...aga tyhiku ise jtame jrgmisest jupist vlja.
              line = line.substring(breakPos + 1);
            } else {
              // tyhikut polnud, vtame siis lihtsalt max pikkusega jupi
              // (vibolla peaks punkti ja koma ka vaatama??)
              out.println(line.substring(0, maxLen));
              line = line.substring(maxLen);
            }
          }
          out.println(line);
          // jrgmine rida...
          line = in.readLine();
        }
        content = writer.toString();
      } catch (IOException ioe) {}
    } else {
      content = ((TextArea)c).getText();
    }
    byte[] bytes =  content.getBytes();

    // salvestatava faili nime valimise dialoog.
    FileDialog fd = new FileDialog(this);
    fd.setMode(FileDialog.SAVE);
    File f = (File)files.get(activeButtonName);
    fd.setFile(f.getName());
    fd.setDirectory(f.getParent());
    fd.show();

    // nyyd viks nimi olemas olla, vaatame...

    try {
      if (fd.getFile() != null) {
        // kasutaja valis faili, salvestame sinna
        File out = new File(fd.getDirectory(), fd.getFile());
        // kysime enne parooli ka, kui vaja...
        askPassword();
        writeFile(out, crypt(bytes) );
      }
    } catch (IOException ioe) {
      System.out.println("Viga: " + ioe);
    }
  }

  private void close() {
    // lpetame failiga tegutsemise...

    // faili nime viskame vlja..
    files.remove(activeButtonName);

    if (activeButtonName != null) {
      // kaardi tekstvljaga koristame ra...
      Component c = findComponent(cardPanel, "t" + activeButtonName);
      if (c != null) {
        cardPanel.remove(c);
      }
      // nupukese koristame ra..
      c = findComponent(fileButtonPanel, activeButtonName);
      if (c != null) {
        fileButtonPanel.remove(c);
      }
    }

    // teeme aktiivseks mne teise faili...

    Iterator it = files.keySet().iterator();

    if (it.hasNext()) {
      activeButtonName = (String)it.next();
      cardLayout.show(cardPanel, activeButtonName);
      File f = (File)files.get(activeButtonName);
      if (f==null) {
        this.setTitle("");
      } else {
        this.setTitle(f.getAbsolutePath());
      }
    } else {
      // yhtki faili enam jrgi jnd, nitame tyhjust.
      this.setTitle("");
      activeButtonName = null;
      cardLayout.show(cardPanel, "empty");
    }
    this.validate();
  }

  private void askPassword(){
    // kysime parooli kui vaja (st kui kasutaja on linnukese teind seadetes)
    passwordField.setText("");
    if (passwordCheckbox.getState()) {
      passwordDialog.setSize(350, 120);
      passwordDialog.setLocationRelativeTo(cardPanel);
      passwordDialog.show();
    }
  }

  // komponendi otsimine mingisugusest konteinerist... (kaardi paneelilt ja nupukeste hulgast)
  private Component findComponent(Container parent, String name) {
    if (parent != null && name != null) {
      Component[] c  = parent.getComponents();
      for(int i = 0; i<c.length; i++) {
        if ( name.equals(c[i].getName()) ) {
          return c[i];
        }
      }
    }
    return null;
  }

  // faili sisu masiivi
  private byte[] readFile(File file) throws IOException {
    FileInputStream input = null;
    try {
      input = new FileInputStream(file);
      final int len = (int) file.length();
      byte[] bytes = new byte[len];
      input.read(bytes);
      return bytes;
    } finally {
      if (input != null) {
        input.close();
      }
    }
  }

  // massiivi sisu faili
  private void writeFile(File file, byte[] bytes) throws IOException {
    FileOutputStream output = null;
    try {
      output = new FileOutputStream(file);
      output.write(bytes);
      output.flush();
    } finally {
      if (output != null) {
        output.close();
      }
    }
  }

  // kodeerime exclusive OR'iga, siis pole eraldi funktsioone vaja.
  private byte[] crypt(byte[] bytes) {
    byte[] password = passwordField.getText().getBytes();
    if (password.length == 0) {
      // parooli ei antud.
      return bytes;
    }

    byte[] cryptedBytes = new byte[bytes.length];
    for(int i = 0, j = 0; i < bytes.length; i++) {
      cryptedBytes[i] = (byte)(bytes[i] ^ password[j++]);
      if (j >= password.length) {
        j=0;
      }
    }
    return cryptedBytes;
  }

  // algus.
  public static void main (String[] args){
    Frame f = new Redaktor1();
    f.setSize(500, 500);
    f.setVisible(true);
  }
}
