/*
 * This file is part of the Poliqarp suite.
 * 
 * Copyright (C) 2004-2009 by Instytut Podstaw Informatyki Polskiej
 * Akademii Nauk (IPI PAN; Institute of Computer Science, Polish
 * Academy of Sciences; cf. www.ipipan.waw.pl).  All rights reserved.
 * 
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation and appearing in the file gpl.txt included in the packaging
 * of this file.  (See http://www.gnu.org/licenses/translations.html for
 * unofficial translations.)
 * 
 * A commercial license is available from IPI PAN (contact
 * Michal.Ciesiolka@ipipan.waw.pl or ipi@ipipan.waw.pl for more
 * information).  Licensees holding a valid commercial license from IPI
 * PAN may use this file in accordance with that license.
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
 * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE.
 */

package ipipan.poliqarp.connection;

import java.io.*;
import java.util.concurrent.*;

/**
 * A <code>MessageReceiver</code> is a thread that a
 * <code>PoliqarpConnection</code> object maintains for handling incoming
 * messages. Synchronous messages are kept in a synchronized FIFO queue so
 * that they can be retrieved upon request in the order of receiving;
 * asynchronous messages are passed to the handler, if present.
 */
final class MessageReceiver extends Thread 
{
   private static final int QUEUE_SIZE = 1000;
      
   private BlockingQueue<String> msgqueue;
   private SynchronousQueue<String> synqueue;
   private BufferedReader in;
   private AsyncHandler asyncHandler;
   private boolean directUnsafe = false;

   public MessageReceiver(BufferedReader reader)
   {
      in = reader;
      msgqueue = new ArrayBlockingQueue<String>(QUEUE_SIZE);
      synqueue = new SynchronousQueue<String>();
   }

   public void setAsyncHandler(AsyncHandler handler)
   {
      asyncHandler = handler;
   }

   public synchronized void setDirectUnsafeMode(boolean val)
   {
      directUnsafe = val;
      if (!val)
         notify();
   }

   public String getSyncMessage() throws InterruptedException
   {
      return synqueue.take();
   }

   private void dispatch(String s) throws InterruptedException
   {
      final String realMsg = s.substring(2, s.length());
      switch (s.charAt(0)) {
         case 'R': /* synchronous message */
            synchronized (this) {
               if (!directUnsafe) 
                  msgqueue.put(realMsg);
               else 
                  synqueue.put(realMsg);
               while (directUnsafe) {
                  try {
                     wait();
                  } catch (InterruptedException exn) {}
               }
            }
            break;
         case 'M': /* asynchronous message */
            if (asyncHandler != null) {
               Thread t = new Thread() {
                  public void run() {
                     synchronized (asyncHandler) {
                        asyncHandler.handle(realMsg);
                     }
                  }
               };
               t.start();
            } else {
               System.err.println("async message arrived on null handler: " + 
                  s);
            }
            break;
         default:
            System.err.println("Incorrect message type: " + s);
            break;
      } 
   }

   public void run() 
   {
      try {
         for (;;) {
            String s = in.readLine();
            if (s != null) 
               dispatch(s);
         }
      } catch (Exception e) {}
   }

   public String getMessage() throws InterruptedException
   {
      String res = msgqueue.take();
      return res;
   }
}
