/*
 * Copyright (c) 1998-2012 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source 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, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *   Free SoftwareFoundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.es;

/**
 * JavaScript object
 */
class NativeRegexp extends Native {
  static ESId INDEX = ESId.intern("index");
  static ESId INPUT = ESId.intern("input");

  static final int NEW = 1;
  static final int COMPILE = NEW + 1;
  static final int EXEC = COMPILE + 1;
  static final int TEST = EXEC + 1;
  static final int TO_STRING = TEST + 1;

  /**
   * Create a new object based on a prototype
   */
  private NativeRegexp(String name, int n, int len)
  {
    super(name, len);

    this.n = n;
  }

  /**
   * Creates the native Regexp object
   */
  static ESRegexpWrapper create(Global resin)
  {
    NativeRegexp nativeRegexp = new NativeRegexp("Regexp", NEW, 1);
    ESRegexp proto;

    try {
      proto = new ESRegexp("", "");
    } catch (Exception e) {
      throw new RuntimeException();
    }
    proto.prototype = resin.objProto;
    resin.regexpProto = proto;
    ESRegexpWrapper regexp = new ESRegexpWrapper(resin, nativeRegexp, proto);

    put(proto, "exec", EXEC, 1);
    put(proto, "compile", COMPILE, 2);
    put(proto, "test", TEST, 1);
    put(proto, "toString", TO_STRING, 0);

    proto.setClean();
    regexp.setClean();

    return regexp;
  }

  static private void put(ESObject proto, String name, int n, int len)
  {
    ESId id = ESId.intern(name);
    NativeRegexp fun = new NativeRegexp(name, n, len);

    proto.put(id, fun, DONT_ENUM);
  }

  public ESBase call(Call eval, int length) throws Throwable
  {
    switch (n) {
    case NEW:
      return create(eval, length);

    case TO_STRING:
      try {
        ESRegexp regexp = (ESRegexp) eval.getThis();
        String s = regexp.pattern.toString();
        String f = regexp.flags.toString();

        return ESString.create("/" + s + "/" + f);
      } catch (ClassCastException e) {
        throw new ESException("toString expected regexp object");
      }

    case EXEC:
      return exec(eval, length);

    case COMPILE:
      return compile(eval, length);

    case TEST:
      return test(eval, length);

    default:
      throw new ESException("Unknown object function");
    }
  }

  private ESBase create(Call eval, int length) throws Throwable
  {
    ESString pattern;
    ESString flags = null;

    if (length == 0)
      pattern = ESString.NULL;
    else
      pattern = eval.getArg(0).toStr();

    if (length > 1)
      flags =  eval.getArg(1).toStr();
    else
      flags = ESString.NULL;

    ESObject obj;
    obj = new ESRegexp(pattern, flags);

    return obj;
  }

  private ESBase compile(Call eval, int length) throws Throwable
  {
    ESString pattern;
    ESString flags = null;
    ESBase arg = eval.getArg(-1);

    if (arg instanceof ESThunk)
      arg = ((ESThunk) arg).toObject();

    if (! (arg instanceof ESRegexp))
      throw new ESException("compile must be bound to regexp");
    ESRegexp regexp = (ESRegexp) arg;

    if (length == 0)
      return esUndefined;
    else
      pattern = eval.getArg(0).toStr();

    if (length > 1)
      flags =  eval.getArg(1).toStr();
    else
      flags = ESString.NULL;

    regexp.compile(pattern, flags);

    return regexp;
  }

  static ESBase exec(Call eval, int length) throws Throwable
  {
    ESBase reg = eval.getArg(-1);
    ESRegexp regexp;

    if (reg instanceof ESThunk)
      reg = ((ESThunk) reg).toObject();

    if (reg instanceof ESRegexp)
      regexp = (ESRegexp) reg;
    else
      regexp = new ESRegexp(reg.toStr(), ESString.NULL);
    if (regexp.prototype == null)
      throw new RuntimeException();

    ESString string;

    Global global = Global.getGlobalProto();
    if (length == 0)
      string = global.getRegexp().getProperty(global.getRegexp().INPUT).toStr();
    else
      string = eval.getArg(0).toStr();

    global.getRegexp().setRegexp(regexp);
    if (! regexp.exec(string))
      return esNull;

    return esNull;
    /* java.util.regex
      
    ESArray array = global.createArray();
    for (int i = 0; i < regexp.regexp.length(); i++) {
      int begin = regexp.regexp.getBegin(i);
      int end = regexp.regexp.getEnd(i);
      if (begin < end && begin >= 0)
        array.setProperty(i, string.substring(begin, end));
      else
        array.setProperty(i, ESString.create("")); // XXX: possible?
    }

    // java.util.regex
    // array.setProperty(INDEX, ESNumber.create(regexp.regexp.getBegin(0)));
    array.setProperty(INPUT, string);

    return array;
    */
  }

  private ESBase test(Call eval, int length) throws Throwable
  {
    ESBase reg = eval.getArg(-1);
    ESRegexp regexp;

    if (reg instanceof ESThunk)
      reg = ((ESThunk) reg).toObject();

    if (reg instanceof ESRegexp)
      regexp = (ESRegexp) reg;
    else
      regexp = new ESRegexp(reg.toStr(), ESString.NULL);
    if (regexp.prototype == null)
      throw new RuntimeException();

    ESString string;
    ESRegexpWrapper globalRegexp = Global.getGlobalProto().getRegexp();
    if (length == 0)
      string = globalRegexp.getProperty(globalRegexp.INPUT).toStr();
    else
      string = eval.getArg(0).toStr();

    globalRegexp.setRegexp(regexp);
    return ESBoolean.create(regexp.exec(string));
  }
}
