// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.sdk.internal.v8native.value;

import org.chromium.sdk.JsValue;
import org.chromium.sdk.JsValue.Type;
import org.chromium.sdk.internal.v8native.V8Helper;
import org.chromium.sdk.internal.v8native.protocol.input.data.FunctionValueHandle;
import org.chromium.sdk.internal.v8native.protocol.input.data.ObjectValueHandle;
import org.chromium.sdk.internal.v8native.protocol.input.data.RefWithDisplayData;
import org.chromium.sdk.internal.v8native.protocol.input.data.ValueHandle;
import org.chromium.sdk.internal.v8native.value.LoadableString.Factory;

/**
 * A representation of a datum (value) in the remote JavaScript VM. The class must be immutable.
 * When additional (or more recent) data arrives, new instance should be put into
 * {@link ValueLoader} map.
 */
public abstract class ValueMirror {
  /**
   * Merges to {@link ValueMirror}s into one. Since {@link ValueMirror} is immutable
   * this is the right way to gather data.
   * @return possibly new {@link ValueMirror}, or any of the old ones, making a preference for
   *      'base' parameter.
   */
  static ValueMirror merge(ValueMirror base, ValueMirror alternative) {
    if (base.hasProperties()) {
      if (alternative.hasProperties()) {
        // Fall through.
      } else {
        return base;
      }
    } else {
      if (alternative.hasProperties()) {
        return alternative;
      } else {
        // Fall through.
      }
    }
    int lenDiff = base.getStringLength() - alternative.getStringLength();
    if (lenDiff < 0) {
      return alternative;
    } else {
      return base;
    }
  }

  /**
   * Constructs a {@link ValueMirror} given a V8 debugger object specification if it's possible.
   */
  public static ValueMirror create(final RefWithDisplayData refWithDisplayData, Factory loadableStringFactory) {
    final Type type = V8Helper.calculateType(refWithDisplayData.type(),
        refWithDisplayData.className(), false);

    return new ValueMirror(refWithDisplayData.ref()) {
      @Override
      public Type getType() {
        return type;
      }

      @Override
      public String getClassName() {
        return refWithDisplayData.className();
      }

      @Override
      public LoadableString getStringValue() {
        // try another format
        Object valueObj = refWithDisplayData.value();
        String valueStr;
        if (valueObj == null) {
          valueStr = refWithDisplayData.type(); // e.g. "undefined"
        } else {
          valueStr = valueObj.toString();
        }
        return new LoadableString.Immutable(valueStr);
      }

      @Override
      public boolean hasProperties() {
        return false;
      }

      @Override
      public SubpropertiesMirror getProperties() {
        return null;
      }
    };
  }


  /**
   * Constructs a ValueMirror given a V8 debugger object specification.
   * @param valueHandle containing the object specification from the V8 debugger
   */
  public static ValueMirror create(final ValueHandle valueHandle, final Factory factory) {
    final Type type = V8Helper.calculateType(valueHandle.type(), valueHandle.className(), true);

    return new ValueMirror(valueHandle.handle()) {
      @Override
      public Type getType() {
        return type;
      }

      @Override
      public LoadableString getStringValue() {
        return V8Helper.createLoadableString(valueHandle, factory);
      }

      @Override
      public SubpropertiesMirror getProperties() {
        ObjectValueHandle objectValueHandle = valueHandle.asObject();
        if (objectValueHandle == null) {
          return SubpropertiesMirror.EMPTY;
        }
        // unused
        valueHandle.handle();
        SubpropertiesMirror subpropertiesMirror;
        if (type == Type.TYPE_FUNCTION) {
          FunctionValueHandle functionValueHandle = objectValueHandle.asFunction();
          subpropertiesMirror = new SubpropertiesMirror.FunctionValueBased(functionValueHandle);
        } else {
          subpropertiesMirror = new SubpropertiesMirror.ObjectValueBased(objectValueHandle);
        }
        return subpropertiesMirror;
      }

      @Override
      public boolean hasProperties() {
        return true;
      }

      @Override
      public String getClassName() {
        return valueHandle.className();
      }
    };
  }

  public static ValueMirror create(long ref, final Type type,
      final String className, final LoadableString loadableString,
      final SubpropertiesMirror subpropertiesMirror) {
    return new ValueMirror(ref) {
      @Override
      public JsValue.Type getType() {
        return type;
      }

      @Override
      public String getClassName() {
        return className;
      }

      @Override
      public LoadableString getStringValue() {
        return loadableString;
      }

      @Override
      public SubpropertiesMirror getProperties() {
        return subpropertiesMirror;
      }

      @Override
      public boolean hasProperties() {
        return getProperties() != null;
      }
    };
  }

  private final long ref;

  protected ValueMirror(long ref) {
    this.ref = ref;
  }

  public abstract JsValue.Type getType();

  public abstract SubpropertiesMirror getProperties();

  public long getRef() {
    return ref;
  }

  public abstract LoadableString getStringValue();

  public abstract String getClassName();

  public abstract boolean hasProperties();

  int getStringLength() {
    LoadableString loadableString = getStringValue();
    if (loadableString == null) {
      return 0;
    }
    return loadableString.getCurrentString().length();
  }
}

@@ -2159,88 +2159,18 @@
 ef()
-) %7B%0A      @Override%0A      public Type getT
+, t
 ype
-(
 ) %7B%0A
-        return type;%0A      %7D%0A%0A
     
@@ -2873,17 +2873,16 @@
 %7D;%0A  %7D%0A%0A
-%0A
   /**%0A  
@@ -3125,38 +3125,32 @@
  factory) %7B%0A    
-final 
 Type type = V8He
@@ -3216,25 +3216,24 @@
 e(), true);%0A
-%0A
     return n
@@ -3271,88 +3271,18 @@
 le()
-) %7B%0A      @Override%0A      public Type getT
+, t
 ype
-(
 ) %7B%0A
-        return type;%0A      %7D%0A%0A
     
@@ -4376,22 +4376,16 @@
 ong ref,
- final
  Type ty
@@ -4544,96 +4544,18 @@
 (ref
-) %7B%0A      @Override%0A      public JsValue.Type getT
+, t
 ype
-(
 ) %7B%0A
-        return type;%0A      %7D%0A%0A
     
@@ -4993,16 +4993,45 @@
 ong ref;
+%0A  protected final Type type;
 %0A%0A  prot
@@ -5056,16 +5056,27 @@
 long ref
+, Type type
 ) %7B%0A    
@@ -5087,24 +5087,46 @@
 .ref = ref;%0A
+    this.type = type;%0A
   %7D%0A%0A  publi
@@ -5127,25 +5127,16 @@
  public 
-abstract 
 JsValue.
@@ -5149,17 +5149,39 @@
 etType()
-;
+ %7B%0A    return type;%0A  %7D
 %0A%0A  publ

refactor — all implementation of ValueMirror have type property, so, we can push it down