import Command from '../command';
import { COMMAND_TYPE } from '../constants';
import { updateDisplayName } from '../scene-helpers';

function getTargetInfo(object, propertyPath) {
  const keys = propertyPath.split('.');
  const propertyName = keys.pop();
  let target = object;
  keys.forEach(key => {
    if (!target) return;
    target = target[key];
  });
  return {
    target,
    propertyName,
  };
}

function cloneValue(value) {
  if (value === undefined || value === null) {
    return value;
  }
  if (value && value.isColor) {
    return value.clone();
  }
  if (typeof value === 'object') {
    return JSON.parse(JSON.stringify(value));
  }
  return value;
}

function assignValue(target, propertyName, value) {
  if (!target) return;
  if (target[propertyName] && target[propertyName].isColor) {
    target[propertyName].set(value);
    return;
  }
  target[propertyName] = value;
}

export default class SetValueCommand extends Command {
  constructor(service, uuid, propertyPath, newValue) {
    super(service);
    this.type = COMMAND_TYPE.SET_VALUE;
    this.name = '修改属性';
    this.updatable = true;
    this.object = service.getObjectByUuid(uuid);
    this.propertyPath = propertyPath;
    const { target, propertyName } = getTargetInfo(this.object, propertyPath);
    this.oldValue = target ? cloneValue(target[propertyName]) : undefined;
    this.newValue = cloneValue(newValue);
  }

  execute() {
    if (!this.object) return;
    const { target, propertyName } = getTargetInfo(this.object, this.propertyPath);
    assignValue(target, propertyName, this.newValue);
    if (this.propertyPath === 'name') {
      updateDisplayName(this.object);
    }
    if (this.propertyPath === 'material.opacity' && this.object.material) {
      this.object.material.transparent = Number(this.object.material.opacity) < 1;
      this.object.material.needsUpdate = true;
    }
    if (this.propertyPath.indexOf('material.') === 0 && this.object.material) {
      this.object.material.needsUpdate = true;
    }
    this.object.updateMatrixWorld(true);
  }

  undo() {
    if (!this.object) return;
    const { target, propertyName } = getTargetInfo(this.object, this.propertyPath);
    assignValue(target, propertyName, this.oldValue);
    if (this.propertyPath === 'name') {
      updateDisplayName(this.object);
    }
    if (this.propertyPath === 'material.opacity' && this.object.material) {
      this.object.material.transparent = Number(this.object.material.opacity) < 1;
      this.object.material.needsUpdate = true;
    }
    if (this.propertyPath.indexOf('material.') === 0 && this.object.material) {
      this.object.material.needsUpdate = true;
    }
    this.object.updateMatrixWorld(true);
  }

  canUpdate(command, deltaTime, mergeWindow) {
    return (
      command &&
      command.type === this.type &&
      this.object &&
      command.object &&
      command.object.uuid === this.object.uuid &&
      command.propertyPath === this.propertyPath &&
      deltaTime <= mergeWindow
    );
  }

  update(command) {
    this.newValue = cloneValue(command.newValue);
  }

  getChangedKeys() {
    return [this.propertyPath];
  }
}
