
// ChatGroup.java
// Andrew Davison, April 2005, ad@fivedots.coe.psu.ac.th

/* Třída ChatGroup spravuje dva seznamy typu ArrayLists: chatUsers 
   a messages.

   chatUsers obsahuje objekty typu Chatter, z nichž každý uchovává
   jméno klienta, UID a počet dosud přečtených zpráv.

   Seznam messages tvoří řetězce (diskusní příspěvky).
   Jakmile systém opustí všichni uživatelé, dojde k jeho smazání.

   Nový uživatel je přidán pouze pokud zatím neexistuje jiný
   téhož jména.

   Většina operací závisí na existenci objektu typu Chatter s
   příslušným jménem a UID.

   Všechny veřejné metody jsou synchronizované, neboť se může stát,
   že k objektu ChatGroup bude v jendom okamžiku přistupovat více
   servletových vláken doGet().
*/

import java.util.*;


public class ChatGroup
{
  private ArrayList chatUsers;
  private ArrayList messages;
  private int numUsers;


  public ChatGroup()
  {  chatUsers = new ArrayList();  
     messages = new ArrayList();
     numUsers = 0;
  }


  synchronized public int addUser(String name)
  // přidá uživatele, je-li vše ok vratí uid, jinak vrátí -1
  {
    if (numUsers == 0)   // nikdo není přihlášen
      messages.clear();

    if (isUniqueName(name)) {
      Chatter c = new Chatter(name);
      chatUsers.add(c);  
      messages.add("(" + name + ") se připojil k diskusi");
      numUsers++;
      return c.getUID();
    }
    return -1;
  }


  private boolean isUniqueName(String name)
  /* Vrátí true, pokud objekt typu Chatter jménem name dosud neexistuje. */
  { Chatter c;
    for(int i=0; i < chatUsers.size(); i++) {
      c = (Chatter) chatUsers.get(i);
      if ( c.getUserName().equals(name) )
        return false;
    }
    return true;
  }  // konec metody: findUser()



  synchronized public boolean delUser(String name, int uid)
  // vymaže specifikovaného uživatele
  { if (uid == -1)
      return false;

    Chatter c;
    for(int i=0; i < chatUsers.size(); i++) {
      c = (Chatter) chatUsers.get(i);
      if (c.matches(name, uid)) {
        chatUsers.remove(i);
        messages.add("(" + name + ") odešel");
        numUsers--;
        return true;
      }
    }
    return false;
  }  // konec metody: delUser()


  private Chatter findUser(String name, int uid)
  // vrátí objekt typu Chatter, pokud existuje, jinak null
  { if (uid == -1)
      return null;

    Chatter c;
    for(int i=0; i < chatUsers.size(); i++) {
      c = (Chatter) chatUsers.get(i);
      if (c.matches(name, uid))
        return c;
    }
    return null;
  }  // konec metody: findUser()



  synchronized public boolean storeMessage(String name, int uid, String msg)
  /* Přidá příspěvek msg do seznamu messages. Je jen na klientech, aby si ji
     pomocí zprávy "read" přečetli.
  */
  { Chatter c = findUser(name, uid);
    if (c != null) {
      messages.add("(" + name + ") " + msg);
      return true;
    }
    return false;
  }  // konec metody: storeMessage()


  synchronized public String read(String name, int uid)
  /* Přečte všechny nepřečtené příspěvky od poslední zprávy "read".

     Zpráva může být neviditelná -- pomocí následujícího formátu
     může být adresována jediné osobě:
          msg / toName

     Zpráva tohoto typu se do seznamu vráceného klientovi nepřidává.
  */
  { StringBuffer msgs = new StringBuffer();
    Chatter c = findUser(name, uid);

    if (c != null) {
      int msgsIndex = c.getMsgsIndex();  // kam jsme minule dočetli
      String msg;
      for(int i=msgsIndex; i < messages.size(); i++) {
        msg = (String) messages.get(i);
        if (isVisibleMsg(msg, name))
          msgs.append( msg + "\n" );
      }
      c.setMsgsIndex( messages.size() );  // aktualizuj index čtení klienta
    }
    return msgs.toString();
  }  // konec metody: read()


  private boolean isVisibleMsg(String msg, String name)
  /* příspěvek je viditelný, pokud nemá část "/ name", nebo
     name se odkazuje na stejného uživatele, nebo zpráva je _od_
     stejného uživatele.
  */
  {
    int index = msg.indexOf("/");
    if (index == -1)  // neobsahuje znak '/', takže jde o veřejný příspěvek
      return true;

    // nemá částo "/ name"
    String toName = msg.substring(index+1).trim();
    if (toName.equals(name))  // pro tohoto uživatele
      return true;
    else {   // pro jiného uživatele
      if (msg.startsWith("("+name))   // ale od tohoto uživatele
        return true;
      else     // od někoho jiného
        return false;
    }
  }  // konec metody: isVisibleMsg()



  synchronized public String who()
  //  Vrátí seznam aktuálně přihlášených uživatelů
  { Chatter c;
    StringBuffer whoList = new StringBuffer();
    for(int i=0; i < chatUsers.size(); i++) {
      c = (Chatter) chatUsers.get(i);
      whoList.append("" + (i+1) + ". " + c.getUserName() + "\n");
    }
    return whoList.toString();
  }  // konec metody: who()


}  // konec třídy: ChatGroup
