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;
    }

}