/*
 * Copyright (c) 1998-2007 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 Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Sam
 */

package com.caucho.quercus.lib.spl;

import com.caucho.quercus.annotation.Name;
import com.caucho.quercus.annotation.Optional;
import com.caucho.quercus.env.*;
import com.caucho.quercus.lib.ArrayModule;
import com.caucho.util.L10N;
import com.caucho.vfs.WriteStream;

import java.io.IOException;
import java.util.IdentityHashMap;
import java.util.Map;

public class ArrayObject
  implements ArrayAccess,
             Countable,
             IteratorAggregate,
             Traversable
{
  private static L10N L = new L10N(ArrayObject.class);

  public static final int STD_PROP_LIST = 0x00000001;
  public static final int ARRAY_AS_PROPS = 0x00000002;

  private final Env _env;
  private Value _value;
  private int _flags;
  private QuercusClass _iteratorClass;

  @Name("__construct")
  public ArrayObject(Env env,
                     @Optional Value value,
                     @Optional int flags,
                     @Optional("ArrayIterator") String iteratorClassName)
  {
    if (value.isNull())
      value = new ArrayValueImpl();

    _env = env;
    _value = value;
    _flags = flags;

    QuercusClass iteratorClass = _env.findClass(iteratorClassName);

    if (iteratorClass == null || !iteratorClass.isA("Iterator"))
      throw new IllegalArgumentException(L.l("A class that implements Iterator must be specified"));

    _iteratorClass = iteratorClass;
  }

  public void append(Value value)
  {
    _value.put(value);
  }

  public void asort(@Optional long sortFlag)
  {
    sortFlag = 0; // qa/4a46

    if (_value instanceof ArrayValue)
      ArrayModule.asort(_env, (ArrayValue) _value, sortFlag);
  }

  public int count()
  {
    return _value.getCount(_env);
  }

  public Value exchangeArray(ArrayValue array)
  {
    Value oldValue = _value;

    _value = array;

    return oldValue;
  }

  public Value getArrayCopy()
  {
    return _value.copy();
  }

  public int getFlags()
  {
    return _flags;
  }

  public ObjectValue getIterator()
  {
    Value[] args = new Value[] { _value, LongValue.create(_flags) };

    return (ObjectValue) _iteratorClass.callNew(_env,args);
  }

  public String getIteratorClass()
  {
    return _iteratorClass.getName();
  }

  public void ksort(@Optional long sortFlag)
  {
    if (_value instanceof ArrayValue)
      ArrayModule.ksort(_env, (ArrayValue) _value, sortFlag);
  }

  public void natcasesort()
  {
    if (_value instanceof ArrayValue)
      ArrayModule.natcasesort((ArrayValue) _value);
  }

  public void natsort()
  {
    if (_value instanceof ArrayValue)
      ArrayModule.natsort((ArrayValue) _value);
  }

  public boolean offsetExists(Value offset)
  {
    return _value.get(offset).isset();
  }

  public Value offsetGet(Value offset)
  {
    return _value.get(offset);
  }

  public Value offsetSet(Value offset, Value value)
  {
    return _value.put(offset, value);
  }

  public Value offsetUnset(Value offset)
  {
    return _value.remove(offset);
  }

  public void setFlags(Value flags)
  {
    _flags = flags.toInt();
  }

  public void setIteratorClass(String iteratorClass)
  {
    _iteratorClass = _env.findClass(iteratorClass);
  }

  public void uasort(Callback func)
  {
    if (_value instanceof ArrayValue)
      ArrayModule.uasort(_env, (ArrayValue) _value, func,  0);
  }

  public void uksort(Callback func, @Optional long sortFlag)
  {
    if (_value instanceof ArrayValue)
      ArrayModule.uksort(_env, (ArrayValue) _value, func, sortFlag);
  }

  public Value __getField(StringValue key)
  {
    //php/4a4r
    return _value.get(key);
    
    /*
    if ((_flags & ARRAY_AS_PROPS) != 0) {
      return _value.get(key);
    }
    else {
      return NullValue.NULL;
    }
    */
  }

  static private void printDepth(WriteStream out, int depth)
    throws java.io.IOException
  {
    for (int i = depth; i > 0; i--)
      out.print(' ');
  }

  public void printRImpl(Env env,
                         WriteStream out,
                         int depth,
                         IdentityHashMap<Value, String> valueSet)
    throws IOException
  {

    if ((_flags & STD_PROP_LIST) != 0) {
      // XXX:
      out.print("ArrayObject");
      out.print(' ');
      out.println("Object");
      printDepth(out, 4 * depth);
      out.println("(");
      out.print(")");
    }
    else {
      out.print("ArrayObject");
      out.print(' ');
      out.println("Object");
      printDepth(out, 4 * depth);
      out.println("(");

      depth++;


      java.util.Iterator<Map.Entry<Value,Value>> iterator
        = _value.getIterator(env);

      while (iterator.hasNext()) {
        Map.Entry<Value, Value> entry = iterator.next();

        Value key = entry.getKey();
        Value value = entry.getValue();

        printDepth(out, 4 * depth);

        out.print("[" + key + "] => ");

        value.printR(env, out, depth + 1, valueSet);

        out.println();
      }

      depth--;

      printDepth(out, 4 * depth);
      out.println(")");
    }
  }

  public void varDumpImpl(Env env,
                          WriteStream out,
                          int depth,
                          IdentityHashMap<Value, String> valueSet)
    throws IOException
  {
    if ((_flags & STD_PROP_LIST) != 0) {
      // XXX:
      out.println("object(ArrayObject) (0) {");
      out.print("}");

    }
    else {
      out.println("object(ArrayObject) (" + _value.getSize() + ") {");

      depth++;

      java.util.Iterator<Map.Entry<Value,Value>> iterator
        = _value.getIterator(env);

      while (iterator.hasNext()) {
        Map.Entry<Value, Value> entry = iterator.next();

        Value key = entry.getKey();
        Value value = entry.getValue();

        printDepth(out, 2 * depth);

        out.print("[");

        if (key instanceof StringValue)
          out.print("\"" + key + "\"");
        else
          out.print(key);

        out.println("]=>");

        printDepth(out, 2 * depth);

        value.varDump(env, out, depth, valueSet);

        out.println();
      }

      depth--;

      printDepth(out, 2 * depth);

      out.print("}");
    }
  }
}
