package xtc.lang.blink;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import xtc.lang.blink.EventLoop.ReplyHandler;
import xtc.lang.blink.Event.BlinkEventSource;
import xtc.lang.blink.Event.Java2NativeCompletionEvent;
import xtc.lang.blink.Event.DeathEvent;
import xtc.lang.blink.Event.RawTextMessageEvent;

/**
 * Event utility classes.
 */
public class EventUtil {

  /** A base class for composing the event reply handlers. */
  static abstract class CompositionalReplyHandler extends ReplyHandler {

    /** The list of the handlers. The ordering of this handler matters.*/
    protected final ReplyHandler[] handlers;

    /** Constructor. */
    CompositionalReplyHandler(ReplyHandler[] handlers) {
      this.handlers = handlers;
      assert handlers != null && handlers.length > 0;
    }
  }

  static final class EventReplyHandler extends ReplyHandler {
    static final class EventFilter{
      private BlinkEventSource source;
      private Class<?> eventClass;
      public EventFilter(BlinkEventSource source, Class<?> eventClass) {
        this.eventClass = eventClass;
        this.source = source;
      }
      public boolean matches(Event event) {
        return this.source == event.getSource() && eventClass.isInstance(event);
      }
    }

    private final EventFilter[] eventFilters;

    /**
     * @param eventClasses
     */
    public EventReplyHandler(EventFilter[] filters) {
      assert filters != null;
      this.eventFilters = filters;
    }
    boolean dispatch(Event e) {
      for(EventFilter f: eventFilters) {
        if (f.matches(e)) {
          setResult(e);
          return true;
        }
      }
      return false;
    }
    Event getEvent() {
      return (Event)getResult();
    }
  }

  /** Conjunctive Composition of the multiple reply handlers.*/
  static final class ConjunctiveReplyHandler extends CompositionalReplyHandler {
    
    ConjunctiveReplyHandler(ReplyHandler[] handlers) {
      super(handlers);
    }
    
    /** Convenience constructors */    
    ConjunctiveReplyHandler(ReplyHandler r1, ReplyHandler r2) {
      this(new ReplyHandler[] {r1, r2});
    }
    ConjunctiveReplyHandler(ReplyHandler r1, ReplyHandler r2, ReplyHandler r3) {
      this(new ReplyHandler[] {r1, r2, r3});
    }

    /** The dispatch implementation. */
    boolean dispatch(Event e) {
      if (getResult() != null) {return true;}
      int numSatisfied = 0;
      Object result = null;
      for(final ReplyHandler h : handlers) {
        if (h.dispatch(e)) {
          result = h.getResult();
          numSatisfied++;
        }
      }      
      if (numSatisfied == handlers.length) {
        assert result != null;
        setResult(result);
        return true;
      } else {
        return false;  
      }
    }
  }

  static abstract class SingleSourceReplyHandler extends ReplyHandler {
    /** The event source to keep track of. */
    protected final BlinkEventSource sourceFilter;

    public SingleSourceReplyHandler(final BlinkEventSource sourceFilter) {
      this.sourceFilter = sourceFilter;
    }

    public BlinkEventSource getSourceFilter() {
      return sourceFilter;
    }
  }


  /** A Declarative reply handler using regular expression. */
  static final class RegExpReplyHandler extends SingleSourceReplyHandler {

    /** The specification - regular expression.*/
    private final Pattern p;

    /** The message accumulated for all the dispatch method calls. */
    private final StringBuffer buf;

    /** The pattern matter. */
    private final Matcher m;
    
    /**
     * @param sourceFilter The event source filter.
     * @param regex The regular expression.
     */
    RegExpReplyHandler(BlinkEventSource sourceFilter, String regex) {
      super(sourceFilter);
      this.p = Pattern.compile(regex);
      this.buf = new StringBuffer();
      this.m = p.matcher(buf);
    }

    /** 
     * Dispatch the debugger's message event.
     * @param e The event.
     * @return true the regular expression is found
     */
    boolean dispatch(Event e) {
      if (getResult() != null) {return true;}
      if (e.getSource() != sourceFilter ) {
        return false;
      } 
      if (e instanceof RawTextMessageEvent == false ) {
        return false;
      }
      RawTextMessageEvent me = (RawTextMessageEvent)e; 
      String msg = new String(me.getMessage());
      buf.append(msg);
      m.reset();
      if (m.find()) {
        setResult(m);
        return true;
      } else {
        return false;
      }
    }

    /**
     * Getter method. 
     * @return The matcher.
     */
    public Matcher getMatcher() {
      return m;
    }
  }

  static abstract class EventClassReplyHandler extends SingleSourceReplyHandler {
    EventClassReplyHandler(BlinkEventSource sourceFilter) {
      super(sourceFilter);
    }
    public Event getEvent() {
      return (Event)getResult();
    }
  }
  /** Observe the end of the death of the event source. */
  static final class DeathReplyHandler extends EventClassReplyHandler {
    
    public DeathReplyHandler(BlinkEventSource sourceFilter) {
      super(sourceFilter);
    }
    boolean dispatch(Event e) {
      if (getResult() != null) {return true;}
      if (e instanceof DeathEvent 
          && e.getSource() == sourceFilter) {
        setResult(e);
        return true;
      }
      return false;
    }
  }

  static final class J2CCompletionEventHandler extends EventClassReplyHandler {

    public J2CCompletionEventHandler(JavaDebugger sourceFilter) {
      super(sourceFilter);
    }
    
    boolean dispatch(Event e) {
      if (getResult() != null) {return true;}
      if (e instanceof Java2NativeCompletionEvent 
          && e.getSource() == sourceFilter) {
        setResult(e);
        return true;
      }
      return false;
    }    
  }
}
