import java.io.IOException;

import org.jruby.Ruby; import org.jruby.RubyArray; import org.jruby.RubyClass; import org.jruby.RubyHash; import org.jruby.RubyModule; import org.jruby.RubyNumeric; import org.jruby.RubyObject; import org.jruby.RubyObjectAdapter; import org.jruby.RubyRegexp; import org.jruby.RubyString; import org.jruby.anno.JRubyMethod; import org.jruby.exceptions.RaiseException; import org.jruby.javasupport.JavaEmbedUtils; import org.jruby.runtime.Arity; import org.jruby.runtime.Block; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.callback.Callback; import org.jruby.exceptions.RaiseException; import org.jruby.runtime.load.BasicLibraryService; import org.jruby.util.ByteList;

public class HpricotCss {

public void FILTER(String id) {
    IRubyObject[] args = new IRubyObject[fargs];
    System.arraycopy(fvals, 0, args, 0, fargs);
    mod.callMethod(ctx, id, args);
    tmpt.rb_clear();
    fargs = 1;
}

public void FILTERAUTO() {
    try {
        FILTER(new String(data, ts, te - ts, "ISO-8859-1"));
    } catch(java.io.UnsupportedEncodingException e) {}
}

public void PUSH(int aps, int ape) {
    RubyString str = RubyString.newString(runtime, data, aps, ape-aps);
    fvals[fargs++] = str;
    tmpt.append(str);
}

private IRubyObject self, mod, str, node;
private int cs, act, eof, p, pe, ts, te, aps, ape, aps2, ape2;
private byte[] data;

private int fargs = 1;
private IRubyObject[] fvals = new IRubyObject[6];
private RubyArray focus;
private RubyArray tmpt;
private Ruby runtime;
private ThreadContext ctx;

public HpricotCss(IRubyObject self, IRubyObject mod, IRubyObject str, IRubyObject node) {
    this.self = self;
    this.mod = mod;
    this.str = str;
    this.node = node;
    this.runtime = self.getRuntime();
    this.ctx = runtime.getCurrentContext();
    this.focus = RubyArray.newArray(runtime, node);
    this.tmpt = runtime.newArray();

    fvals[0] = focus;

    if(!(str instanceof RubyString)) {
        throw runtime.newArgumentError("bad CSS selector, String only please.");
    }

    ByteList bl = ((RubyString)str).getByteList();

    data = bl.bytes;
    p = bl.begin;
    pe = p + bl.realSize;
    eof = pe;
}

%%{

machine hpricot_css;

action a {
  aps = p;
}

action b {
  ape = p;
  PUSH(aps, ape);
}

action c {
  ape = p;
  aps2 = p;
}

action d {
  ape2 = p;
  PUSH(aps, ape);
  PUSH(aps2, ape2);
}

commas    = space* "," space*;
traverse  = [>+~];
sdot      = "\\.";
utfw      = alnum | "_" | "-" |
            (0xc4 0xa8..0xbf) | (0xc5..0xdf 0x80..0xbf) |
            (0xe0..0xef 0x80..0xbf 0x80..0xbf) |
            (0xf0..0xf4 0x80..0xbf 0x80..0xbf 0x80..0xbf);
utfword   = utfw+;
utfname   = (utfw | sdot)+;
quote1    = "'" [^']* "'";
quote2    = '"' [^"]* '"';

cssid     = "#" %a utfname %b;
cssclass  = "." %a utfname %b;
cssname   = "[name=" %a utfname %b "]";
cssattr   = "[" %a utfname %c space* [^ \n\t]? "=" %d space* (quote1 | quote2 | [^\]]+) "]";
csstag    = utfname >a %b;
cssmod    = ("even" | "odd" | (digit | "n" | "+" | "-")* );
csschild  = ":" %a ("only" | "nth" | "last" | "first") "-child" %b ("(" %a cssmod %b ")")?;
csspos    = ":" %a ("nth" | "eq" | "gt" | "lt" | "first" | "last" | "even" | "odd") %b ("(" %a digit+ %b ")")?;
pseudop   = "(" [^)]+ ")";
pseudoq   = "'" (pseudop+ | [^'()]*) "'" |
            '"' (pseudop+ | [^"()]*) '"' |
                (pseudop+ | [^"()]*);
pseudo    = ":" %a utfname %b ("(" %a pseudoq %b ")")?;

main     := |*
  cssid      => { FILTER("ID"); };
  cssclass   => { FILTER("CLASS"); };
  cssname    => { FILTER("NAME"); };
  cssattr    => { FILTER("ATTR"); };
  csstag     => { FILTER("TAG"); };
  cssmod     => { FILTER("MOD"); };
  csschild   => { FILTER("CHILD"); };
  csspos     => { FILTER("POS"); };
  pseudo     => { FILTER("PSUEDO"); };
  commas     => { focus = RubyArray.newArray(runtime, node); };
  traverse   => { FILTERAUTO(); };
  space;
*|;

write data nofinal;

}%%

public IRubyObject scan() {

%% write init; %% write exec;

    return focus;
}

}