function isObject(target) {
  return typeof target === "object" && target !== null;
}
 
let targetMap = new WeakMap();
 
let activeEffect;
let effectStack = [];
 
let shouldTrack = true;
const trackStack = [];
 
function pauseTracking() {
  trackStack.push(shouldTrack);
  shouldTrack = false;
}
 
function enableTracking() {
  trackStack.push(shouldTrack);
  shouldTrack = true;
}
 
function resetTracking() {
  const last = trackStack.pop();
  shouldTrack = last === undefined ? true : last;
}
 
// 新增一个被包装的push函数。
function wrappedPush(...args) {
  pauseTracking();
  const result = Array.prototype.push.apply(this, args);
  resetTracking();
  return result;
}
 
function track(target, key) {
  if (!shouldTrack) {
    return;
  }
 
  if (!activeEffect) {
    return;
  }
 
  let keyToDepMap = targetMap.get(target);
  if (!keyToDepMap) {
    keyToDepMap = new Map();
    targetMap.set(target, keyToDepMap);
  }
 
  let effects = keyToDepMap.get(key);
  if (!effects) {
    effects = new Set();
    keyToDepMap.set(key, effects);
  }
 
  // 建立副作用函数到包含他的集合的映射
  activeEffect.deps.add(effects);
 
  effects.add(activeEffect);
}
 
function trigger(target, key) {
  let keyToDepMap = targetMap.get(target);
  if (!keyToDepMap) {
    return;
  }
 
  let effects = keyToDepMap.get(key);
  if (!effects) {
    return;
  }
 
  // 【特殊处理】如果是数组的length发生变化,触发所有的副作用函数。
  if (Array.isArray(target) && key === "length") {
    keyToDepMap.forEach((effects) => {
      effects.forEach((effect) => {
        effect();
      });
    });
    return;
  }
 
  // 拿到触发时的副作用函数,防止后续重复添加导致死循环
  [...effects].forEach((effect) => {
    // 防止副作用函数中更改依赖,自己触发自己,爆栈。
    if (effect === activeEffect) {
      return;
    }
    if (effect.options.scheduler) {
      // 如果有scheduler,那么运行
      effect.options.scheduler();
    } else {
      effect();
    }
  });
}
function reactive(target) {
  if (!isObject(target)) {
    console.error("target must be an object");
    return;
  }
  const proxyValue = new Proxy(target, {
    get: (target, key) => {
      //  如果判断是数组的push方法直接返回包装的函数。
      // 简化写法,另外的函数还有'push' 'pop', 'shift', 'unshift', 'splice'等。
      if (Array.isArray(target) && key === "push") {
        return wrappedPush;
      }
 
      track(target, key); // 收集依赖
      const result = Reflect.get(target, key);
      // 如果get的结果是一个对象,那么把它变成响应式的再返回。
      if (isObject(result)) {
        return reactive(result);
      }
      return result;
    },
    set: (target, key, value) => {
      const result = Reflect.set(target, key, value);
      if (result !== value) {
        trigger(target, key); //  触发更新
      }
      return result;
    }
  });
  return proxyValue;
}
 
function ref(rawValue) {
  let _value = isObject(rawValue) ? reactive(value) : rawValue;
  const refValue = {
    get value() {
      track(refValue, "value");
      return _value;
    },
    set value(_newValue) {
      if (_newValue !== _value) {
        //实际上要比较原始的值是否相同,而不是代理的。
        _value = _newValue;
        trigger(refValue, "value");
      }
    }
  };
  return refValue;
}
 
function effect(fn, options = {}) {
  function reactiveEffect() {
    activeEffect = reactiveEffect;
    // 运行前先入栈
    effectStack.push(activeEffect);
 
    // 运行之前,先清除依赖。
    const { deps } = activeEffect;
    if (deps) {
      deps.forEach((dep) => {
        dep.delete(activeEffect);
      });
    }
 
    const result = fn();
    effectStack.pop();
    // 运行后指向栈顶
    activeEffect = effectStack[effectStack.length - 1];
    return result;
  }
 
  // 源码用数组优化空间,这里简单用set。
  reactiveEffect.deps = new Set();
 
  reactiveEffect.options = options;
  if (!options.lazy) {
    // 不自动运行一次收集依赖。
    reactiveEffect();
  }
 
  return reactiveEffect;
}
 
export {
  reactive,
  ref,
  effect
};