/*
 * Copyright (C) 2012 eXo Platform SAS.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.crsh.telnet.term;

import junit.framework.AssertionFailedError;
import org.crsh.auth.AuthInfo;
import org.crsh.plugin.CRaSHPlugin;
import org.crsh.telnet.term.spi.TermIO;
import org.crsh.telnet.term.spi.TermIOHandler;

import java.io.IOException;
import java.security.Principal;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public class IOHandler extends CRaSHPlugin<TermIOHandler> implements TermIOHandler {

  /** . */
  private final BlockingQueue<IOEvent> eventQueue = new LinkedBlockingQueue<IOEvent>();

  /** . */
  private final BlockingQueue<IOAction> actionQueue = new LinkedBlockingQueue<IOAction>();

  @Override
  public TermIOHandler getImplementation() {
    return this;
  }

  public void handle(TermIO io, Principal user, AuthInfo authInfo) {
    while (true) {
      IOAction action = null;
      while (action == null) {
        try {
          action = actionQueue.take();
        } catch (InterruptedException e) {
          // We ignore it on purpose
          // as it can come from the client clause
          // but we want to obtain a next event to continue the
          // unit test
        }
      }
      try {
        if (action instanceof IOAction.Read) {
          int code = io.read();
          CodeType codeType = io.decode(code);
          eventQueue.add(new IOEvent.IO(code, codeType));
        } else if (action instanceof IOAction.Write) {
          IOAction.Write write = (IOAction.Write)action;
          io.write(write.s);
        } else if (action instanceof IOAction.Close) {
          io.close();
        } else if (action instanceof IOAction.Flush) {
          io.flush();
        } else if (action instanceof IOAction.CRLF) {
          io.writeCRLF();
        } else if (action instanceof IOAction.Del) {
          io.writeDel();
        } else if (action instanceof IOAction.Left) {
          io.moveLeft();
        } else if (action instanceof IOAction.End) {
          break;
        } else {
          throw new UnsupportedOperationException("Unexpected action " + action);
        }
      } catch (IOException e) {
        e.printStackTrace();
        eventQueue.add(new IOEvent.Error(e));
      }
    }
  }

  public IOHandler add(IOAction action) {
    actionQueue.add(action);
    return this;
  }

  public int getActionCount() {
    return actionQueue.size();
  }

  public void assertEvent(IOEvent expectedEvent) {
    try {
      IOEvent event = eventQueue.poll(2, TimeUnit.SECONDS);
      expectedEvent.assertEquals(event);
    } catch (InterruptedException e) {
      AssertionFailedError afe = new AssertionFailedError();
      afe.initCause(e);
      throw afe;
    }
  }

  public IOEvent take() {
    try {
      return eventQueue.take();
    } catch (InterruptedException e) {
      AssertionFailedError afe = new AssertionFailedError();
      afe.initCause(e);
      throw afe;
    }
  }

  public IOEvent poll() {
    return eventQueue.poll();
  }

  public IOEvent peek() {
    return eventQueue.peek();
  }
}
