import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.io.*;
import java.util.ArrayList;
import java.nio.charset.StandardCharsets;



/**
* Die Klasse MySocket verwaltet eine Client-Server-Verbindung. Sie wartet auf eingehende Nachrichten und
  * informiert ihren Listener, wenn eine Nachricht eingangen ist. Die Nachricht kann dann beim Socket abgeholt werden.
  * Außerdem können Nachrichten in Form von Strings versendet werden.
  * Der Listener wird außerdem informiert, wenn die Verbindung zusammengebrochen / beendet ist.
  * Der Listener muss das Interface MySocketListener implementieren.
  *
  *
  * @version 1.0 vom 15.11.2012
* @author Thomas Schaller
  */
  
public class MySocket extends Thread{
  
  // Anfang Attribute2
  private boolean aktiv;
  private Socket socket;
  private BufferedReader eingang=null;
  PrintWriter ausgang=null;
  MySocketListener listener=null;
      
      // Ende Attribute2
      
      /** Dieser Konstruktor erstellt einen MySocket aus einem schon vorhandenen Socket und ist für die Verwendung durch MyServer gedacht.
  * @param s Socket, der durch MySocket verwaltet werden soll.
    * @param listener Listener, der über eingehende Nachrichten und das Beenden der Verbindung informiert wird. Der Listener muss MySocketListener implementieren.
    */
  public MySocket(Socket s, MySocketListener listener){
    this.socket = s;
    this.listener = listener;
    try {
      eingang = new BufferedReader(new InputStreamReader(socket.getInputStream()));
      ausgang = new PrintWriter(socket.getOutputStream());
      aktiv = true;
      this.start();
    } catch(Exception e) {;
      System.out.println(e);
      e.printStackTrace();
      aktiv = false;
    } // end of try
  }
      
      /** Dieser Konstruktor erstellt einen neuen Socket. Er verbindet sich mit einem Server an der angegebenen Adresse unter dem angegebenen Port. Der Listener wird
      * über eingehende Nachrichten und das Beenden der Verbindung informiert.
      * @param address IP-Adresse oder Domain-Name des Servers
  * @param port    Portnummer, die der Server abhört
    * @param listener Listener, der über eingehende Nachrichten und das Beenden der Verbindung informiert wird. Der Listener muss MySocketListener implementieren.
    */
  public MySocket(String address, int port, MySocketListener listener) {
    this.listener = listener;
    try {
      this.socket = new Socket(address, port);
      eingang = new BufferedReader(new InputStreamReader(socket.getInputStream()));
      ausgang = new PrintWriter(socket.getOutputStream());
      aktiv = true;
      this.start();
    } catch(Exception e) {
      System.out.println(e);
      e.printStackTrace();
      aktiv = false;
    } // end of try
  }
        
  // Anfang Methoden2
    /** Verwaltet das Annehmen der Nachrichten (Diese Methode bitte nicht direkt aufrufen, sie wird automatisch gestartet).
      */
  public void run(){
    try {
      while (aktiv){
        if (listener != null && eingang.ready()) listener.nachrichtEmpfangen(this);
        Thread.sleep(5);
      }
    }
    catch (Exception e) {
        if(socket!=null)try{socket.close();}catch(Exception ex){;}
      System.err.println(e);
      e.printStackTrace();
    }
    aktiv = false;
    if (listener != null) listener.verbindungBeendet(this);
    
  }
  
     
    
    
    /** Liefert die eingangene Nachricht. Diese Methode bitte nur aufrufen, wenn der Listener über den Eingang einer Nachricht informiert wurde, da die Methode sonst bis
    * zur nächsten eingehenden Nachricht wartet.
    * @return Nachricht
    */
  public String holeNachricht() {
    try{
      
      
      if (aktiv && eingang.ready()) {
        
        String s = eingang.readLine();
        System.out.println(s);
        byte[] b = Base64.decode(s);
        /*    for (int i = 0; i < b.length ; i++) {
        System.out.print(b[i]);
        System.out.print(" ");
        } // end of for
        System.out.println("");*/
        
        return s;
      } // end of if
    }
    catch(Exception e) {
      e.printStackTrace();
      
      return null;
    }
    
    
    return null;
  }
    
    /** Versendet eine Nachricht an den Server.
    * @param nachricht Zu sendender Text.
    */
  public void sendeNachricht(String nachricht) {
    try {
      //byte[] b = nachricht;
      /*  for (int i = 0; i < b.length ; i++) {
      System.out.print(b[i]);
      System.out.print(" ");
      } // end of for
      System.out.println("");*/
      //ausgang.println(Base64.encode(nachricht));
      ausgang.println(nachricht);
      // System.out.println(Base64.encode(nachricht));
      ausgang.flush();
    } catch(Exception e) {
      System.out.println(e);
      e.printStackTrace();
      if (listener != null) listener.verbindungBeendet(this);
      aktiv = false;
    }
  }
    
    /** Bricht eine bestehende Client-Server-Verbindung ab.
    */
    
  public void trenneVerbindung() {
    aktiv = false;
    if (socket != null) {
      try {
        socket.close();
      } catch(Exception e) {
        System.out.println(e);
        e.printStackTrace();
      }
      
    } // end of if
    socket = null; // end of try
  }
    
    /** Liefert den Socket der Verbindung. Kann benutzt werden, um dann aus dem Socket weitere Informationen wie IP-Adressen oder Portnummern zu ermitteln.
    * @return Socket, der Verbindung.
    */
  public Socket getSocket() {
    return socket;
  }
    
    /** Liefert den Status der Verbindung.
    * @return true, falls die Verbindung noch aktiv ist, sonst false.
    */
  public boolean isAktiv() {
    return aktiv;
  }
    
    /** Räumt alles auf, damit der Thread nicht weiterläuft (bitte nicht aufrufen).
    */
  public void finalize() {
    // Schließen der Streams und des Sockets, wenn
    // die Verbindung gelöscht wird wird
    try {
      trenneVerbindung();
    }
    catch(Exception e) {
      System.out.println(e);
      e.printStackTrace();
    }
  }
  // Ende Methoden2
}
