/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import ts from 'typescript';

import {
  COMPONENT_NON_DECORATOR,
  COMPONENT_STATE_DECORATOR,
  COMPONENT_PROP_DECORATOR,
  COMPONENT_LINK_DECORATOR,
  COMPONENT_STORAGE_LINK_DECORATOR,
  COMPONENT_PROVIDE_DECORATOR,
  COMPONENT_OBJECT_LINK_DECORATOR,
  COMPONENT_CREATE_FUNCTION,
  COMPONENT_POP_FUNCTION,
  BASE_COMPONENT_NAME,
  CUSTOM_COMPONENT_EARLIER_CREATE_CHILD,
  COMPONENT_CONSTRUCTOR_UPDATE_PARAMS,
  CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID,
  COMPONENT_CONSTRUCTOR_UNDEFINED,
  CUSTOM_COMPONENT_NEEDS_UPDATE_FUNCTION,
  CUSTOM_COMPONENT_MARK_STATIC_FUNCTION,
  COMPONENT_COMMON,
  GENERATE_ID,
  ELMTID,
  STARTGETACCESSRECORDINGFOR,
  STOPGETACCESSRECORDING,
  BASE_COMPONENT_NAME_PU,
  OBSERVECOMPONENTCREATION,
  OBSERVECOMPONENTCREATION2,
  ISINITIALRENDER,
  UPDATE_STATE_VARS_OF_CHIND_BY_ELMTID,
  $$,
  COMPONENT_RECYCLE,
  COMPONENT_CREATE_RECYCLE,
  RECYCLE_NODE,
  ABOUT_TO_REUSE,
  COMPONENT_RERENDER_FUNCTION,
  OBSERVE_RECYCLE_COMPONENT_CREATION,
  FUNCTION,
  COMPONENT_IF_UNDEFINED,
  COMPONENT_PARAMS_LAMBDA_FUNCTION,
  COMPONENT_PARAMS_FUNCTION,
  COMPONENT_ABOUTTOREUSEINTERNAL_FUNCTION,
  NAME,
  COMPONENT_CALL,
  COMPONENT_CONSUME_DECORATOR,
  COMPONENT_STORAGE_PROP_DECORATOR,
  COMPONENT_LOCAL_STORAGE_PROP_DECORATOR,
  COMPONENT_LOCAL_STORAGE_LINK_DECORATOR,
  COMPONENTV2_LOCAL_DECORATOR,
  COMPONENTV2_CONSUMER_DECORATOR,
  COMPONENTV2_PROVIDER_DECORATOR,
  COMPONENT_ENV_DECORATOR,
  MAX_LINK_SOURCE_DATA_NESTING_LEVEL,
  API_VERSION_26,
  COMPONENT_BUILDER_DECORATOR,
  RECYCLEFLAG,
  ISPRERENDER,
  PUSH_RECYCLE_ELMTID_TO_RENDERSTACK,
  ABOUT_TO_BE_DELETE_FUNCTION_ID__,
  POP_RECYCLE_ELMTID_TO_RENDERSTACK
} from './pre_define';
import {
  stateCollection,
  linkCollection,
  propCollection,
  regularCollection,
  storagePropCollection,
  storageLinkCollection,
  provideCollection,
  consumeCollection,
  objectLinkCollection,
  isStaticViewCollection,
  builderParamObjectCollection,
  getLocalStorageCollection,
  builderParamInitialization,
  propInitialization,
  regularInitialization,
  stateInitialization,
  provideInitialization,
  privateCollection,
  regularStaticCollection,
  componentCollection,
  localStorageLinkCollection,
  localStoragePropCollection,
  envCollection,
  customEnvCollection
} from './validate_ui_syntax';
import {
  PropMapManager,
  createViewCreate,
  createCustomComponentNewExpression,
  isLocalStorageParameter,
  isBasicType,
  isObservedV2
} from './process_component_member';
import {
  LogType,
  LogInfo,
  componentInfo,
  storedFileInfo,
  CurrentProcessFile,
  createApiVersionCheck
} from './utils';
import {
  bindComponentAttr,
  parentConditionalExpression,
  createComponentCreationStatement,
  createFunction,
  ComponentAttrInfo,
  ifRetakeId,
  transferBuilderCall,
  createCollectElmtIdNode,
  createViewStackProcessorStatement,
  BuilderParamsResult
} from './process_component_build';
import {
  partialUpdateConfig,
  projectConfig,
  globalProgram
} from '../main';
import {
  GLOBAL_CUSTOM_BUILDER_METHOD,
  INNER_CUSTOM_BUILDER_METHOD,
  INNER_CUSTOM_LOCALBUILDER_METHOD
} from './component_map';
import {
  createReference,
  isProperty
} from './process_component_class';
import processStructComponentV2, { StructInfo, ParamDecoratorInfo } from './process_struct_componentV2';
import constantDefine from './constant_define';
import createAstNodeUtils from './create_ast_node_utils';
import logMessageCollection from './log_message_collection';

let decoractorMap: Map<string, Map<string, Set<string>>>;

export function processCustomComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[],
  log: LogInfo[], name: string, isBuilder: boolean = false, isGlobalBuilder: boolean = false,
  idName: ts.Expression = undefined, builderParamsResult: BuilderParamsResult = null,
  isInRepeatTemplate: boolean = false): void {
  decoractorMap = new Map(
    [[COMPONENT_STATE_DECORATOR, stateCollection],
      [COMPONENT_LINK_DECORATOR, linkCollection],
      [COMPONENT_PROP_DECORATOR, propCollection],
      [COMPONENT_NON_DECORATOR, regularCollection],
      [COMPONENT_PROVIDE_DECORATOR, provideCollection],
      [COMPONENT_OBJECT_LINK_DECORATOR, objectLinkCollection]]);
  const componentNode: ts.CallExpression = getCustomComponentNode(node);
  if (componentNode) {
    const parentComponentType: ParentType = componentCollection.currentClassName ? 
    getParentComponentType(componentCollection.currentClassName) : getParentComponentType('');
    const isRecycleComponent: boolean = isRecycle(name);
    const isReuseComponentInV2: boolean = isReuseInV2(name);
    logMessageCollection.checkNestedComponents(parentComponentType, isRecycleComponent, isReuseComponentInV2, node, log);
    logMessageCollection.checkIfReuseV2InRepeatTemplate(isInRepeatTemplate, isReuseComponentInV2, node, log);
    const hasChainCall: boolean = componentNode.parent &&
      ts.isPropertyAccessExpression(componentNode.parent);
    let ischangeNode: boolean = false;
    let customComponentNewExpression: ts.NewExpression = createCustomComponentNewExpression(
      componentNode, name, isBuilder, isGlobalBuilder);
    let argumentsArray: ts.PropertyAssignment[];
    const componentAttrInfo: ComponentAttrInfo = { reuseId: null, hasIdAttr: false, attrCount: 0, reuse: '' };
    if (isHasChild(componentNode)) {
      // @ts-ignore
      argumentsArray = componentNode.arguments[0].properties.slice();
      argumentsArray.forEach((item: ts.PropertyAssignment, index: number) => {
        if (isToChange(item, name)) {
          ischangeNode = true;
          const propertyAssignmentNode: ts.PropertyAssignment = ts.factory.updatePropertyAssignment(
            item, item.name, changeNodeFromCallToArrow(item.initializer as ts.CallExpression));
          argumentsArray.splice(index, 1, propertyAssignmentNode);
        }
      });
      if (ischangeNode) {
        const newNode: ts.ExpressionStatement = ts.factory.updateExpressionStatement(node,
          ts.factory.createNewExpression(componentNode.expression, componentNode.typeArguments,
            [ts.factory.createObjectLiteralExpression(argumentsArray, true)]));
        customComponentNewExpression = createCustomComponentNewExpression(
          newNode.expression as ts.CallExpression, name, isBuilder);
      }
    }
    let judgeIdStart: number;
    if (partialUpdateConfig.partialUpdateMode && idName) {
      judgeIdStart = newStatements.length;
    }
    let needCommon: boolean = false;
    if (hasChainCall) {
      if (partialUpdateConfig.partialUpdateMode) {
        const commomComponentNode: ts.Statement[] = [ts.factory.createExpressionStatement(
          createFunction(ts.factory.createIdentifier(COMPONENT_COMMON),
            ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null))];
        const immutableStatements: ts.Statement[] = [];
        bindComponentAttr(node, ts.factory.createIdentifier(COMPONENT_COMMON), commomComponentNode,
          log, true, false, immutableStatements, false, componentAttrInfo, isReuseComponentInV2);
        needCommon = commomComponentNode.length > 1 || immutableStatements.length > 0;
        if (componentAttrInfo.hasIdAttr && componentAttrInfo.attrCount === 1) {
          commomComponentNode[0] = createCommonIdAttrNode();
        }
        if (needCommon) {
          newStatements.push(createComponentCreationStatement(componentAttributes(COMPONENT_COMMON),
            commomComponentNode, COMPONENT_COMMON, isGlobalBuilder, false, undefined, immutableStatements,
            builderParamsResult, isRecycleComponent));
        }
      } else {
        newStatements.push(ts.factory.createExpressionStatement(
          createFunction(ts.factory.createIdentifier(COMPONENT_COMMON),
            ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null)));
        bindComponentAttr(node, ts.factory.createIdentifier(COMPONENT_COMMON), newStatements, log);
      }
    }
    if (isRecycleComponent && partialUpdateConfig.partialUpdateMode) {
      newStatements.push(createRecycleComponent(isGlobalBuilder));
    }
    addCustomComponent(node, newStatements, customComponentNewExpression, log, name, componentNode,
      isBuilder, isGlobalBuilder, isRecycleComponent, componentAttrInfo, builderParamsResult, isReuseComponentInV2);
    if (hasChainCall && (!partialUpdateConfig.partialUpdateMode || needCommon)) {
      newStatements.push(ts.factory.createExpressionStatement(
        createFunction(ts.factory.createIdentifier(COMPONENT_COMMON),
          ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null)));
    }
    if (isRecycleComponent && partialUpdateConfig.partialUpdateMode) {
      newStatements.push(componentAttributes(COMPONENT_RECYCLE));
    }
    if (partialUpdateConfig.partialUpdateMode && idName) {
      newStatements.splice(judgeIdStart, newStatements.length - judgeIdStart,
        ifRetakeId(newStatements.slice(judgeIdStart), idName));
    }
  }
}

function createCommonIdAttrNode(): ts.ExpressionStatement {
  return ts.factory.createExpressionStatement(
    createFunction(ts.factory.createIdentifier(COMPONENT_COMMON),
      ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION),
      // @ts-ignore
      [ts.factory.createTrue()]));
}

export function isRecycle(componentName: string): boolean {
  return storedFileInfo.getCurrentArkTsFile().recycleComponents.has(componentName);
}

export function isReuseInV2(componentName: string): boolean {
  return storedFileInfo.getCurrentArkTsFile().reuseComponentsV2.has(componentName);
}

function createRecycleComponent(isGlobalBuilder: boolean): ts.Statement {
  return createComponentCreationStatement(componentAttributes(COMPONENT_RECYCLE),
    [ts.factory.createExpressionStatement(
      createFunction(ts.factory.createIdentifier(COMPONENT_RECYCLE),
        ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null))
    ], COMPONENT_RECYCLE, isGlobalBuilder);
}

function componentAttributes(componentName: string): ts.Statement {
  return ts.factory.createExpressionStatement(
    ts.factory.createCallExpression(
      ts.factory.createPropertyAccessExpression(
        ts.factory.createIdentifier(componentName),
        ts.factory.createIdentifier(COMPONENT_POP_FUNCTION)
      ), undefined, []
    ));
}

function isHasChild(node: ts.CallExpression): boolean {
  return node.arguments && node.arguments[0] && ts.isObjectLiteralExpression(node.arguments[0]) &&
    node.arguments[0].properties && node.arguments[0].properties.length > 0;
}

function isToChange(item: ts.PropertyAssignment, name: string): boolean {
  const builderParamName: Set<string> = builderParamObjectCollection.get(name);
  if (item.initializer && ts.isCallExpression(item.initializer) && builderParamName &&
    builderParamName.has(item.name.getText()) &&
    !/\.(bind|call|apply)/.test(item.initializer.getText())) {
    return true;
  }
  return false;
}

function changeNodeFromCallToArrow(node: ts.CallExpression): ts.ConditionalExpression {
  let builderBindThis: ts.ExpressionStatement = ts.factory.createExpressionStatement(node);
  if (ts.isCallExpression(node) && node.expression && ts.isIdentifier(node.expression) &&
    GLOBAL_CUSTOM_BUILDER_METHOD.has(node.expression.escapedText.toString())) {
    builderBindThis = transferBuilderCall(ts.factory.createExpressionStatement(node), node.expression.escapedText.toString());
  }
  return changeNodeFromCallToArrowDetermine(node, builderBindThis);
}

function changeNodeFromCallToArrowDetermine(node: ts.CallExpression, builderBindThis: ts.ExpressionStatement): ts.ConditionalExpression {
  if (ts.isCallExpression(node)) {
    return ts.factory.createConditionalExpression(
      ts.factory.createBinaryExpression(
        ts.factory.createTypeOfExpression(node),
        ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
        ts.factory.createStringLiteral(FUNCTION)
      ),
      ts.factory.createToken(ts.SyntaxKind.QuestionToken),
      node,
      ts.factory.createToken(ts.SyntaxKind.ColonToken),
      ts.factory.createArrowFunction(
        undefined,
        undefined,
        [],
        undefined,
        ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
        ts.factory.createBlock(
          [builderBindThis],
          true
        )
      )
    );
  }
  return undefined;
}

function addCustomComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[],
  newNode: ts.NewExpression, log: LogInfo[], name: string, componentNode: ts.CallExpression,
  isBuilder: boolean, isGlobalBuilder: boolean, isRecycleComponent: boolean,
  componentAttrInfo: ComponentAttrInfo, builderParamsResult: BuilderParamsResult,
  isReuseComponentInV2: boolean): void {
  if (ts.isNewExpression(newNode)) {
    const propertyArray: ts.ObjectLiteralElementLike[] = [];
    validateCustomComponentPrams(componentNode, name, propertyArray, log, isBuilder);
    addCustomComponentStatements(node, newStatements, newNode, name, propertyArray, componentNode,
      isBuilder, isGlobalBuilder, isRecycleComponent, componentAttrInfo, builderParamsResult, log, isReuseComponentInV2);
  }
}

function addCustomComponentStatements(node: ts.ExpressionStatement, newStatements: ts.Statement[],
  newNode: ts.NewExpression, name: string, props: ts.ObjectLiteralElementLike[],
  componentNode: ts.CallExpression, isBuilder: boolean, isGlobalBuilder: boolean,
  isRecycleComponent: boolean, componentAttrInfo: ComponentAttrInfo,
  builderParamsResult: BuilderParamsResult, log: LogInfo[], isReuseComponentInV2: boolean): void {
  if (!partialUpdateConfig.partialUpdateMode) {
    const id: string = componentInfo.id.toString();
    newStatements.push(createFindChildById(id, name, isBuilder), createCustomComponentIfStatement(id,
      ts.factory.updateExpressionStatement(node, createViewCreate(newNode)),
      ts.factory.createObjectLiteralExpression(props, true), name));
  } else {
    newStatements.push(createCustomComponent(newNode, name, componentNode, isGlobalBuilder, isBuilder,
      isRecycleComponent, componentAttrInfo, builderParamsResult, log, isReuseComponentInV2));
  }
}

function createChildElmtId(node: ts.CallExpression, name: string, log: LogInfo[]): ts.PropertyAssignment[] {
  const childParam: ts.PropertyAssignment[] = [];
  const propsAndObjectLinks: string[] = [];
  if (propCollection.get(name)) {
    propsAndObjectLinks.push(...propCollection.get(name));
  }
  if (objectLinkCollection.get(name)) {
    propsAndObjectLinks.push(...objectLinkCollection.get(name));
  }
  if (projectConfig.optLazyForEach && storedFileInfo.processLazyForEach && stateCollection.get(name)) {
    propsAndObjectLinks.push(...stateCollection.get(name));
  }
  parseChildProperties(name, node, childParam, propsAndObjectLinks, log);
  return childParam;
}

class ChildAndParentComponentInfo {
  childStructInfo: StructInfo;
  parentStructInfo: StructInfo;
  paramDecoratorMap: Map<string, ParamDecoratorInfo>;
  updatePropsDecoratorsV2: string[];
  propsAndObjectLinks: string[];
  childName: string;
  forbiddenInitPropsV2: string[];
  updatePropsForV1Parent: string[];
  updatePropsForV2Parent: string[];
  constructor(childName: string, childNode: ts.CallExpression, propsAndObjectLinks: string[]) {
    this.childName = childName;
    this.propsAndObjectLinks = propsAndObjectLinks;
    this.childStructInfo = processStructComponentV2.getAliasStructInfo(childNode) ||
      processStructComponentV2.getOrCreateStructInfo(childName);
    this.paramDecoratorMap = this.childStructInfo.paramDecoratorMap;
    this.updatePropsDecoratorsV2 = [...this.childStructInfo.eventDecoratorMap.keys(), ...this.paramDecoratorMap.keys()];
    this.parentStructInfo = componentCollection.currentClassName ?
      processStructComponentV2.getOrCreateStructInfo(componentCollection.currentClassName) :
      new StructInfo();
    this.forbiddenInitPropsV2 = [...this.childStructInfo.localDecoratorSet,
      ...this.childStructInfo.providerDecoratorSet, ...this.childStructInfo.consumerDecoratorSet,
      ...this.childStructInfo.regularSet];
    this.updatePropsForV1Parent = getUpdatePropsForV1Parent();
    this.updatePropsForV2Parent = [...this.parentStructInfo.localDecoratorSet,
      ...this.parentStructInfo.paramDecoratorMap.keys(), ...this.parentStructInfo.providerDecoratorSet,
      ...this.parentStructInfo.consumerDecoratorSet];
  }
}

function getUpdatePropsForV1Parent(): string[] {
  const propertiesMapArr: Array<Map<string, Set<string>>> = [
    stateCollection, linkCollection, propCollection,
    provideCollection, consumeCollection, objectLinkCollection,
    storagePropCollection, storageLinkCollection
  ];
  const updatePropsForParent: string[] = [];
  if (componentCollection.currentClassName) {
    const localStorageSet: Set<string> = new Set();
    getLocalStorageCollection(componentCollection.currentClassName, localStorageSet);
    updatePropsForParent.push(...localStorageSet);
    propertiesMapArr.forEach((item: Map<string, Set<string>>) => {
      const value: Set<string> = item.get(componentCollection.currentClassName);
      if (value) {
        updatePropsForParent.push(...value);
      }
    });
  }
  return updatePropsForParent;
}

function parseChildProperties(childName: string, node: ts.CallExpression,
  childParam: ts.PropertyAssignment[], propsAndObjectLinks: string[], log: LogInfo[]): void {
  const childAndParentComponentInfo: ChildAndParentComponentInfo =
    new ChildAndParentComponentInfo(childName, node, propsAndObjectLinks);
  if (node.arguments[0].properties) {
    node.arguments[0].properties.forEach((item: ts.PropertyAssignment) => {
      if (ts.isIdentifier(item.name)) {
        const itemName: string = item.name.escapedText.toString();
        validateChildProperty(item, itemName, childParam, log, childAndParentComponentInfo);
      }
    });
  }
}

function validateAssignBuilderParam(log: LogInfo[], itemName: string, item: ts.PropertyAssignment): void {
  log.push({
    type: LogType.WARN,
    message: `'@BuilderParam' attribute '${itemName}' can only be initialized by '@Builder' function or '@LocalBuilder' method in struct.`,
    pos: item.getStart()
  });
}

function checkAssignBuilderParam(isChildV2: boolean, info: ChildAndParentComponentInfo,
  item: ts.PropertyAssignment, itemName: string, log: LogInfo[]): void {
  const childBuilderParamSet: Set<string> = isChildV2 ? info.childStructInfo.builderParamDecoratorSet :
    builderParamObjectCollection.get(info.childStructInfo.structName);
  if (!childBuilderParamSet || !childBuilderParamSet.has(itemName) || !item.initializer ||
    ts.isArrowFunction(item.initializer) || checkAssignBuilderParamInBuilder(item)) {
    return;
  }
  if (!checkBuilderParamInitializer(item.initializer)) {
    validateAssignBuilderParam(log, itemName, item);
  }
}

function checkAssignBuilderParamInBuilder(item: ts.PropertyAssignment): boolean {
  let assignmentAncestor: ts.FunctionDeclaration | ts.MethodDeclaration =
    ts.findAncestor(item, ts.isFunctionDeclaration);
  if (assignmentAncestor && hasTargetDecorator(assignmentAncestor, COMPONENT_BUILDER_DECORATOR)) {
    return true;
  }
  assignmentAncestor = ts.findAncestor(item, ts.isMethodDeclaration);
  if (assignmentAncestor && hasTargetDecorator(assignmentAncestor, COMPONENT_BUILDER_DECORATOR)) {
    return true;
  }
  return false;
}

function hasTargetDecorator(node: ts.MethodDeclaration | ts.FunctionDeclaration,
  decoratorName: string): boolean {
  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
  if (!decorators || !decorators.length) {
    return false;
  }
  for (let i = 0; i < decorators.length; i++) {
    const originalDecortor: string = decorators[i].getText().replace(/\(.*\)$/, '').replace(/\s*/g, '').trim();
    if (originalDecortor === decoratorName) {
      return true;
    }
  }
  return false;
}

function checkBuilderParamInitializer(initializer: ts.Expression): boolean {
  if (ts.isIdentifier(initializer) &&
    GLOBAL_CUSTOM_BUILDER_METHOD.has(initializer.escapedText.toString())) {
    return true;
  } else if (ts.isPropertyAccessExpression(initializer) && initializer.expression &&
    initializer.expression.kind === ts.SyntaxKind.ThisKeyword &&
    ts.isIdentifier(initializer.name) &&
    [...INNER_CUSTOM_BUILDER_METHOD, ...INNER_CUSTOM_LOCALBUILDER_METHOD].includes(initializer.name.escapedText.toString())) {
    return true;
  }
  return false;
}

function getForbbidenInitPropsV2Type(itemName: string, info: ChildAndParentComponentInfo): string {
  let typeName: string = COMPONENT_NON_DECORATOR;
  if (info.childStructInfo.localDecoratorSet.has(itemName)) {
    typeName = COMPONENTV2_LOCAL_DECORATOR;
  } else if (info.childStructInfo.consumerDecoratorSet.has(itemName)) {
    typeName = COMPONENTV2_CONSUMER_DECORATOR;
  } else if (info.childStructInfo.providerDecoratorSet.has(itemName)) {
    typeName = COMPONENTV2_PROVIDER_DECORATOR;
  }
  return typeName;
}

interface AssignEnvOptions {
  isParentV2: boolean;
  isChildV2: boolean;
  info: ChildAndParentComponentInfo;
  item: ts.PropertyAssignment;
  itemName: string;
}

function checkAssignEnv(assignEnvOption: AssignEnvOptions, log: LogInfo[]): void {
  const info: ChildAndParentComponentInfo = assignEnvOption.info;
  const parentEnvSet: Set<string> = assignEnvOption.isParentV2 ? info.parentStructInfo.envDecoratorSet :
    envCollection.get(info.parentStructInfo.structName);
  const initializerName: string = getInitializerName(assignEnvOption.item);
  if (!initializerName.length || !parentEnvSet || !parentEnvSet.has(initializerName)) {
    return;
  }
  if (assignEnvOption.isChildV2) {
    if (!(info.childStructInfo.paramDecoratorMap && info.childStructInfo.paramDecoratorMap.has(assignEnvOption.itemName) &&
      !(info.childStructInfo.builderParamDecoratorSet &&
        info.childStructInfo.builderParamDecoratorSet.has(assignEnvOption.itemName)))) {
      validateAssignEnvV2(log, assignEnvOption.item);
    }
  } else {
    if (!getCollectionSet(info.childName, regularCollection).has(assignEnvOption.itemName)) {
      validateAssignEnvV1(log, assignEnvOption.item);
    }
  }
}

function validateAssignEnvV2(log: LogInfo[], item: ts.PropertyAssignment): void {
  log.push({
    type: LogType.ERROR,
    message: `Within structs decorated with '@ComponentV2', '@Env' can only initialize variables decorated with '@Param'.`,
    pos: item.getStart(),
    code: '10905252'
  });
}

function validateAssignEnvV1(log: LogInfo[], item: ts.PropertyAssignment): void {
  log.push({
    type: LogType.ERROR,
    message: `Within structs decorated with '@Component', '@Env' can only initialize regular(non-decorated) variables.`,
    pos: item.getStart(),
    code: '10905253'
  });
}

function isChildCustomEnvProperty(info: ChildAndParentComponentInfo, itemName: string): boolean {
  if (info.childStructInfo.isComponentV2) {
    return info.childStructInfo.customEnvDecoratorSet.has(itemName);
  }
  const v1CustomEnvSet: Set<string> = customEnvCollection.get(info.childName);
  return !!v1CustomEnvSet && v1CustomEnvSet.has(itemName);
}

function validateAssignCustomEnv(log: LogInfo[], item: ts.PropertyAssignment,
  childName: string, itemName: string): void {
  log.push({
    type: LogType.ERROR,
    message: `The '@CustomEnv' property '${itemName}' in the custom component '${childName}' cannot be initialized by parent component.`,
    pos: item.getStart(),
    code: '10905375'
  });
}

function validateChildProperty(item: ts.PropertyAssignment, itemName: string,
  childParam: ts.PropertyAssignment[], log: LogInfo[], info: ChildAndParentComponentInfo): void {
  if (isChildCustomEnvProperty(info, itemName)) {
    validateAssignCustomEnv(log, item, info.childName, itemName);
    return;
  }
  const isParentV2: boolean = info.parentStructInfo.isComponentV2;
  if (info.childStructInfo.isComponentV2) {
    if (isInitFromParent(item)) {
      const newAssignEnvOption: AssignEnvOptions = {
        isParentV2: isParentV2,
        isChildV2: true,
        info: info,
        item: item,
        itemName: itemName
      };
      checkAssignEnv(newAssignEnvOption, log);
    }
    if (info.forbiddenInitPropsV2.includes(itemName)) {
      const propType: string = getForbbidenInitPropsV2Type(itemName, info);
      log.push({
        type: LogType.ERROR,
        message: `The '${propType}' property '${itemName}' in the custom component '${info.childName}'` +
          ` cannot be initialized here (forbidden to specify).`,
        pos: item.getStart(),
        code: '10905324'
      });
      return;
    }
    checkAssignBuilderParam(true, info, item, itemName, log);
    if (info.paramDecoratorMap.has(itemName)) {
      childParam.push(item);
    }
    if (isForbiddenAssignToComponentV2(item, itemName, info)) {
      log.push({
        type: LogType.ERROR,
        message: `Property '${itemName}' in the '@ComponentV2' component '${info.childName}' is not allowed to be assigned value here.`,
        pos: item.getStart(),
        code: '10905323'
      });
    }
  } else {
    if (info.childStructInfo.isComponentV1 && isInitFromParent(item)) {
      const newAssignEnvOption: AssignEnvOptions = {
        isParentV2: isParentV2,
        isChildV2: false,
        info: info,
        item: item,
        itemName: itemName
      };
      checkAssignEnv(newAssignEnvOption, log);
    }
    if (info.propsAndObjectLinks.includes(itemName)) {
      childParam.push(item);
    }
    checkAssignBuilderParam(false, info, item, itemName, log);
  }
  logMessageCollection.checkIfAssignToStaticProps(item, itemName, info.childStructInfo.staticPropertySet, log);
}

function isForbiddenTypeToComponentV1(type: ts.Type): boolean {
  // @ts-ignore
  if (type.types && type.types.length) {
    // @ts-ignore
    return !type.types.some((item: ts.Type) => {
      return !isForbiddenTypeToComponentV1(item);
    });
  }
  const allowedTypes: string[] = ['Set', 'Map', 'Date', 'Array'];
  const name: string = type?.getSymbol()?.getName();
  if (name && allowedTypes.includes(name)) {
    return true;
  }
  return false;
}

function isForbiddenAssignToComponentV2(item: ts.PropertyAssignment, itemName: string,
  info: ChildAndParentComponentInfo): boolean {
    const checker: ts.TypeChecker | undefined = CurrentProcessFile.getChecker();
  if (!info.parentStructInfo.isComponentV2 && info.updatePropsDecoratorsV2.includes(itemName) &&
    isObervedProperty(item.initializer, info) && checker) {
    const type: ts.Type = checker.getTypeAtLocation(item.initializer);
    return !(isAllowedTypeToComponentV2(type) || isForbiddenTypeToComponentV1(type) || !(isObservedV2(type) || isFunctionType(type)));
  }
  return false;
}

// isFunctionType 
export function isFunctionType(type: ts.Type): boolean {
  if (type.types && type.types.length) {
    return !type.types.some((item: ts.Type) => {
      return !isFunctionType(item);
  });
  }
  const name: string = type?.getSymbol()?.getName();
  if (name && name === 'Function') {
    return true;
  }
  return false;
}

function isObervedProperty(value: ts.Expression, info: ChildAndParentComponentInfo,
  isV1Parent: boolean = true): boolean {
  if (value && ts.isPropertyAccessExpression(value) && value.expression.kind === ts.SyntaxKind.ThisKeyword &&
    ts.isIdentifier(value.name)) {
    const propertyName: string = value.name.escapedText.toString();
    return isV1Parent ? info.updatePropsForV1Parent.includes(propertyName) :
      info.updatePropsForV2Parent.includes(propertyName);
  }
  return false;
}

export function isAllowedTypeToComponentV2(type: ts.Type): boolean {
  if (type) {
    // @ts-ignore
    if (type.types && type.types.length) {
      // @ts-ignore
      return type.types.some((item: ts.Type) => {
        return isAllowedTypeToComponentV2(item);
      });
    }
    // string|number|boolean|enum|null|undefined
    if (isAllowedTypeForBasic(type.flags)) {
      return true;
    }
  }
  return false;
}

export function isAllowedTypeForBasic(flags: ts.TypeFlags): boolean {
  if (isBasicType(flags) || (flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined))) {
    return true;
  }
  return false;
}

function validateInitParam(childName: string, curChildProps: Set<string>,
  node: ts.CallExpression, log: LogInfo[], parentStructInfo: StructInfo): void {
  const childStructInfo: StructInfo = processStructComponentV2.getAliasStructInfo(node) ||
    processStructComponentV2.getOrCreateStructInfo(childName);
  const paramDecoratorMap: Map<string, ParamDecoratorInfo> = childStructInfo.paramDecoratorMap;
  if (childStructInfo.isComponentV2) {
    const needInitParam: string[] = [];
    for (const item of paramDecoratorMap) {
      if (item[1].hasRequire) {
        needInitParam.push(item[0]);
      }
    }
    needInitParam.forEach((paramName: string) => {
      if (!curChildProps.has(paramName)) {
        log.push({
          type: LogType.ERROR,
          message: `'@Require' decorated '${paramName}' must be initialized through the component constructor.`,
          pos: node.getStart(),
          code: '10905321'
        });
      }
    });
  } else if (parentStructInfo.isComponentV2 && childStructInfo.linkDecoratorsV1.length) {
    log.push({
      type: LogType.ERROR,
      message: `A V2 component cannot be used with any member property decorated by '@Link' in a V1 component.`,
      pos: node.getStart(),
      code: '10905213'
    });
  }
}

function createCustomComponent(newNode: ts.NewExpression, name: string, componentNode: ts.CallExpression,
  isGlobalBuilder: boolean, isBuilder: boolean, isRecycleComponent: boolean,
  componentAttrInfo: ComponentAttrInfo, builderParamsResult: BuilderParamsResult, log: LogInfo[], 
  isReuseComponentInV2:boolean): ts.Block {
  let componentParameter: ts.ObjectLiteralExpression;
  if (componentNode.arguments && componentNode.arguments.length) {
    componentParameter = ts.factory.createObjectLiteralExpression(createChildElmtId(componentNode, name, log), true);
  } else {
    componentParameter = ts.factory.createObjectLiteralExpression([], false);
  }
  const arrowArgArr: ts.ParameterDeclaration[] = [
    ts.factory.createParameterDeclaration(undefined, undefined,
      ts.factory.createIdentifier(ELMTID)
    ),
    ts.factory.createParameterDeclaration(undefined, undefined,
      ts.factory.createIdentifier(ISINITIALRENDER)
    )
  ];
  const arrowBolck: ts.Statement[] = [
    projectConfig.optLazyForEach && storedFileInfo.processLazyForEach ? createCollectElmtIdNode() : undefined,
    createIfCustomComponent(newNode, componentNode, componentParameter, name, isGlobalBuilder,
      isBuilder, isRecycleComponent, componentAttrInfo, log)
  ];
  if (isRecycleComponent) {
    arrowArgArr.push(ts.factory.createParameterDeclaration(
      undefined, undefined, ts.factory.createIdentifier(RECYCLE_NODE),
      undefined, undefined, ts.factory.createNull()
    ));
    // reuse_4
    isCompatibleVersionOverTarget(26) &&
      arrowArgArr.push(ts.factory.createParameterDeclaration(
        undefined, undefined, ts.factory.createIdentifier(ISPRERENDER),
        undefined, undefined, ts.factory.createFalse()
      ));
  } else if (partialUpdateConfig.optimizeComponent && isGlobalBuilder &&
    builderParamsResult && builderParamsResult.firstParam) {
    const paramName: ts.Identifier = builderParamsResult.firstParam.name as ts.Identifier;
    arrowArgArr.push(ts.factory.createParameterDeclaration(undefined, undefined,
      paramName, undefined, undefined, ts.factory.createIdentifier(`__${paramName.escapedText.toString()}__`)
    ));
  }
  if (isRecycleComponent || !partialUpdateConfig.optimizeComponent) {
    arrowBolck.unshift(createViewStackProcessorStatement(STARTGETACCESSRECORDINGFOR, ELMTID));
    arrowBolck.push(createViewStackProcessorStatement(STOPGETACCESSRECORDING));
  }
  const observeArgArr: ts.Node[] = [
    ts.factory.createArrowFunction(undefined, undefined, arrowArgArr, undefined,
      ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
      ts.factory.createBlock(arrowBolck, true))
  ];
  if (isRecycleComponent) {
    componentAttrInfo.reuseId ? observeArgArr.unshift(componentAttrInfo.reuseId) :
      observeArgArr.unshift(ts.factory.createStringLiteral(name));
    isCompatibleVersionOverTarget(26) && observeArgArr.push(ts.factory.createIdentifier(name));
  } else if (partialUpdateConfig.optimizeComponent) {
    observeArgArr.push(componentPop(name));
  }
  const reuseOrCreateArgArr: ts.ObjectLiteralExpression[] = [ts.factory.createObjectLiteralExpression(
    generateReuseOrCreateArgArr(componentNode, componentAttrInfo, name, newNode), true)];
  return ts.factory.createBlock(
    [
      // reuse_2
      (isCompatibleVersionOverTarget(26) && isRecycleComponent) ?
        ts.factory.createExpressionStatement(
          ts.factory.createCallExpression(
            ts.factory.createIdentifier('Reusable'),
            undefined,
            [
              ts.factory.createIdentifier(name)
            ]
          )
        ) : undefined,
      ts.factory.createExpressionStatement(ts.factory.createCallExpression(
        ts.factory.createPropertyAccessExpression(isGlobalBuilder ?
          ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis(),
        isRecycleComponent ?
          ts.factory.createIdentifier(OBSERVE_RECYCLE_COMPONENT_CREATION) :
          isReuseComponentInV2 ? ts.factory.createIdentifier(constantDefine.REUSE_OR_CREATE_METHOD) : 
            ts.factory.createIdentifier(partialUpdateConfig.optimizeComponent ?
              OBSERVECOMPONENTCREATION2 : OBSERVECOMPONENTCREATION)
        ),
        undefined, isReuseComponentInV2 ? reuseOrCreateArgArr as ts.Expression[] : observeArgArr as ts.Expression[]))
    ], 
    true
  );
}

function generateReuseOrCreateArgArr(componentNode: ts.CallExpression, componentAttrInfo: ComponentAttrInfo, 
  name: string, newNode: ts.NewExpression): ts.ObjectLiteralElementLike[] {
  const reuseParamsArr: ts.ObjectLiteralElementLike[] = [];
  if (componentNode.expression && ts.isIdentifier(componentNode.expression)) {
    reuseParamsArr.push(ts.factory.createPropertyAssignment(
      ts.factory.createIdentifier(constantDefine.COMPONENT_CLASS),
      componentNode.expression
    ));
  }
  reuseParamsArr.push(createReuseParameterArrowFunction(getArgumentsToPass(componentNode), constantDefine.GET_PARAMS, name));
  reuseParamsArr.push(createReuseIdArrowFunction(componentAttrInfo, name));
  if (newNode.arguments.length >= 6 && ts.isObjectLiteralExpression(newNode.arguments[5])) {
    reuseParamsArr.push(generateExtraInfo(true, newNode.arguments[5]));
  } else {
    reuseParamsArr.push(generateExtraInfo(false));
  }
  return reuseParamsArr;
}

function createReuseIdArrowFunction(componentAttrInfo: ComponentAttrInfo, componentName: string): ts.PropertyAssignment {
  return ts.factory.createPropertyAssignment(
    ts.factory.createIdentifier(constantDefine.GET_REUSE_ID),
    ts.factory.createConditionalExpression(
      createApiVersionCheck(API_VERSION_26),
      ts.factory.createToken(ts.SyntaxKind.QuestionToken),
      componentAttrInfo.reuseAttribute ?? createReuseIdValue(componentName),
      ts.factory.createToken(ts.SyntaxKind.ColonToken),
      createReuseIdValue(componentAttrInfo.reuse.length > 0 ? componentAttrInfo.reuse : componentName)
    )
  );
}

function createReuseIdValue(idName: string): ts.ArrowFunction {
  return ts.factory.createArrowFunction(
    undefined,
    undefined,
    [],
    undefined,
    ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
    ts.factory.createStringLiteral(idName)
  );
}

function getArgumentsToPass(componentNode: ts.CallExpression): ts.NodeArray<ts.ObjectLiteralElementLike> | undefined[] {
  if (componentNode.arguments && componentNode.arguments.length === 1 && 
    ts.isObjectLiteralExpression(componentNode.arguments[0]) && componentNode.arguments[0].properties &&
    componentNode.arguments[0].properties.length) {
    return componentNode.arguments[0].properties;
  }
  return [];
}

function createReuseParameterArrowFunction(propertyArray: ts.NodeArray<ts.ObjectLiteralElementLike> | undefined[],
  identifierName: string, name: string): ts.PropertyAssignment {
  return ts.factory.createPropertyAssignment(
    ts.factory.createIdentifier(identifierName),
    ts.factory.createArrowFunction(
      undefined,
      undefined,
      [],
      undefined,
      ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
      identifierName === constantDefine.GET_REUSE_ID ? 
        ts.factory.createStringLiteral(name) :
        ts.factory.createParenthesizedExpression(ts.factory.createObjectLiteralExpression(
        propertyArray.length ? propertyArray : [],
        true
      ))
    )
  );
}

function generateExtraInfo(hasPositionInfo: boolean, positionInfo?: ts.ObjectLiteralExpression): ts.PropertyAssignment {
  return ts.factory.createPropertyAssignment(
    ts.factory.createIdentifier(constantDefine.EXTRA_INFO),
    hasPositionInfo ? 
    positionInfo : 
    ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)
  );
}

function componentPop(name: string): ts.ObjectLiteralExpression {
  return ts.factory.createObjectLiteralExpression(
    [ts.factory.createPropertyAssignment(
      ts.factory.createIdentifier(NAME),
      ts.factory.createStringLiteral(name)
    )],
    false
  );
}

export function assignComponentParams(componentNode: ts.CallExpression,
  isBuilder: boolean = false,
  needDoubleUnderline: boolean = false,
  parentName?: string,
  currentName?: string): ts.VariableStatement {
  const isParamsLambda: boolean = true;
  const [keyArray, valueArray]: [ts.Node[], ts.Node[]] = splitComponentParams(componentNode, isBuilder, isParamsLambda);
  let integrateParams: boolean = false;
  if (!keyArray.length && componentNode.arguments && componentNode.arguments.length > 0 && componentNode.arguments[0]) {
    integrateParams = true;
  }
  return ts.factory.createVariableStatement(
    undefined,
    ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(
      ts.factory.createIdentifier(COMPONENT_PARAMS_LAMBDA_FUNCTION),
      undefined,
      undefined,
      ts.factory.createArrowFunction(
        undefined,
        undefined,
        [],
        undefined,
        ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
        ts.factory.createBlock(
          [ts.factory.createReturnStatement(
            integrateParams ? paramsLambdaCallBack(componentNode) : ts.factory.createObjectLiteralExpression(
              reWriteComponentParams(keyArray, valueArray, needDoubleUnderline, parentName, currentName),
              true
            )
          )],
          true
        )
      )
    )],
    ts.NodeFlags.Let
    ));
}

function paramsLambdaCallBack(componentNode: ts.CallExpression): ts.Expression {
  if (partialUpdateConfig.partialUpdateMode && componentNode.arguments.length === 1 &&
    isLocalStorageParameter(componentNode)) {
    return ts.factory.createObjectLiteralExpression([], true);
  } else {
    return componentNode.arguments[0];
  }
}

function reWriteComponentParams(keyArray: ts.Node[], valueArray: ts.Node[],
  needDoubleUnderline: boolean = false,
  parentName?: string,
  currentName?: string): (ts.PropertyAssignment |
    ts.ShorthandPropertyAssignment)[] {
  const returnProperties: (ts.PropertyAssignment | ts.ShorthandPropertyAssignment)[] = [];
  keyArray.forEach((item: ts.Identifier, index: number) => {
    if (!valueArray[index]) {
      returnProperties.push(ts.factory.createShorthandPropertyAssignment(
        item,
        undefined
      ));
    } else {
      if (needDoubleUnderline) {
        parseValueArrayIndex(keyArray, valueArray, index, currentName);
      }
      returnProperties.push(ts.factory.createPropertyAssignment(
        item,
        valueArray[index] as ts.Identifier
      ));
    }
  });
  return returnProperties;
}

function parseValueArrayIndex(keyArray: ts.Node[],
  valueArray: ts.Node[], index: number, currentName?: string): void {
  const LINK_REG: RegExp = /^\$/g;
  let initText: string;
  let newInitializer: ts.PropertyAccessExpression;
  if (!currentName) {
    return;
  }
  const currentNode: ts.Node | undefined = keyArray[index];
  if (!currentNode || !ts.isIdentifier(currentNode)) {
    return;
  }
  const keyName: string = currentNode.escapedText.toString();
  const currentLinkCollection: Set<string> | undefined = linkCollection.get(currentName);
  if (!currentLinkCollection || !currentLinkCollection.has(keyName)) {
    return;
  }
  const currentValue: ts.Node = valueArray[index];
  if (ts.isIdentifier(currentValue) &&
    currentValue.toString().match(LINK_REG)) {
    initText = currentValue.escapedText.toString().replace(LINK_REG, '');
    newInitializer = addInitializerDoubleUnderLine(initText);
  }
  if (ts.isPropertyAccessExpression(currentValue) &&
    currentValue.expression &&
    currentValue.expression.kind === ts.SyntaxKind.ThisKeyword &&
    ts.isIdentifier(currentValue.name)) {
    initText = currentValue.name.escapedText.toString().replace(LINK_REG, '');
    newInitializer = addInitializerDoubleUnderLine(initText);
  }
  if (newInitializer) {
    valueArray[index] = newInitializer;
  }
}

function addInitializerDoubleUnderLine(
  initText: string
): ts.PropertyAccessExpression {
  return ts.factory.createPropertyAccessExpression(
    ts.factory.createThis(),
    ts.factory.createIdentifier(`__${initText}`)
  );
}

function splitComponentParams(componentNode: ts.CallExpression, isBuilder: boolean, isParamsLambda: boolean): [ts.Node[], ts.Node[]] {
  const keyArray: ts.Node[] = [];
  const valueArray: ts.Node[] = [];
  if (componentNode.arguments && componentNode.arguments.length > 0 &&
    ts.isObjectLiteralExpression(componentNode.arguments[0]) && componentNode.arguments[0].properties) {
    componentNode.arguments[0].properties.forEach((propertyItem: ts.PropertyAssignment) => {
      const newPropertyItem: ts.PropertyAssignment =
        isProperty(propertyItem) ? createReference(propertyItem, [], isBuilder, isParamsLambda) : propertyItem;
      keyArray.push(newPropertyItem.name);
      valueArray.push(newPropertyItem.initializer);
    });
  }
  return [keyArray, valueArray];
}

function createIfCustomComponent(newNode: ts.NewExpression, componentNode: ts.CallExpression,
  componentParameter: ts.ObjectLiteralExpression, name: string, isGlobalBuilder: boolean, isBuilder: boolean,
  isRecycleComponent: boolean, componentAttrInfo: ComponentAttrInfo, log: LogInfo[]): ts.IfStatement {
  return ts.factory.createIfStatement(
    ts.factory.createIdentifier(ISINITIALRENDER),
    ts.factory.createBlock(
      parseIfCustomComponentBlock(
        isRecycleComponent, newNode, name, log,
        componentNode, isBuilder, componentAttrInfo
      ), true),
    ts.factory.createBlock(
      [ts.factory.createExpressionStatement(ts.factory.createCallExpression(
        ts.factory.createPropertyAccessExpression(isGlobalBuilder ?
          ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis(),
          ts.factory.createIdentifier(UPDATE_STATE_VARS_OF_CHIND_BY_ELMTID)
        ), undefined,
        [ts.factory.createIdentifier(ELMTID), componentParameter]))], true)
  );
}

function parseIfCustomComponentBlock(
  isRecycleComponent: boolean,
  newNode: ts.NewExpression,
  name: string,
  log: LogInfo[],
  componentNode: ts.CallExpression,
  isBuilder: boolean,
  componentAttrInfo: ComponentAttrInfo
): ts.Statement[] {
  const structDecl: ts.StructDeclaration = ts.findAncestor(componentNode, ts.isStructDeclaration);
  const parentName: string | undefined = structDecl?.name?.getText?.();
  if (isRecycleComponent && isCompatibleVersionOverTarget(26)) {
    return [
      componentParamDetachment(newNode, isRecycleComponent, name, log, componentNode),
      assignComponentParams(componentNode, isBuilder, true, parentName, name),
      assignmentFunction(COMPONENT_CALL),
      createRecycleFlagConditionalExpression(),
      isRecycleComponent ? createNewRecycleComponent(newNode, componentNode, name, componentAttrInfo) :
        createNewComponent(COMPONENT_CALL, name, componentNode),
    ]
  }
  return [
    componentParamDetachment(newNode, isRecycleComponent, name, log, componentNode),
    isRecycleComponent ? createNewRecycleComponent(newNode, componentNode, name, componentAttrInfo) :
      createNewComponent(COMPONENT_CALL, name, componentNode),
    isCompatibleVersionOverTarget(26) ?
      assignComponentParams(componentNode, isBuilder, true, parentName, name) :
      assignComponentParams(componentNode, isBuilder),
    assignmentFunction(COMPONENT_CALL)
  ]
}

export function assignmentFunction(componeParamName: string): ts.ExpressionStatement {
  return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
    ts.factory.createPropertyAccessExpression(
      ts.factory.createIdentifier(componeParamName),
      ts.factory.createIdentifier(COMPONENT_PARAMS_FUNCTION)
    ),
    ts.factory.createToken(ts.SyntaxKind.EqualsToken),
    ts.factory.createIdentifier(COMPONENT_PARAMS_LAMBDA_FUNCTION)
  ));
}

function traverseChildComponentArgs(childParam: ts.Expression[], name: string, log: LogInfo[],
  componentNode: ts.CallExpression): ts.Expression[] {
  const childStructInfo: StructInfo = processStructComponentV2.getAliasStructInfo(componentNode) ||
    processStructComponentV2.getOrCreateStructInfo(name);
  if (!childStructInfo.isComponentV2) {
    return childParam;
  }
  const objectLiteralIndex: number = 2;
  if (childParam.length > objectLiteralIndex && ts.isObjectLiteralExpression(childParam[1]) &&
    childParam[1].properties) {
    const newProperties: ts.PropertyAssignment[] = [];
    childParam[1].properties.forEach((item: ts.PropertyAssignment) => {
      if (item.name && ts.isIdentifier(item.name)) {
        const itemName: string = item.name.escapedText.toString();
        updatePropertyAssignment(newProperties, itemName, item, childStructInfo, log);
      }
    });
    if (newProperties.length) {
      return getNewArgsForCustomComponent(childParam, newProperties);
    }
  }
  return childParam;
}

function getNewArgsForCustomComponent(childParam: ts.Expression[],
  newProperties: ts.PropertyAssignment[]): ts.Expression[] {
  const newArr: ts.Expression[] = [];
  const newObjectLiteralNode: ts.ObjectLiteralExpression =
    ts.factory.updateObjectLiteralExpression(childParam[1], [...childParam[1].properties, ...newProperties]);
  childParam.forEach((item: ts.Expression, index: number) => {
    if (index === 1) {
      newArr.push(newObjectLiteralNode);
    } else {
      newArr.push(item);
    }
  });
  return newArr;
}

function updatePropertyAssignment(newProperties: ts.PropertyAssignment[],
  itemName: string, item: ts.PropertyAssignment, childStructInfo: StructInfo, log: LogInfo[]): void {
  if (isDoubleNonNullExpression(item.initializer) && !moreThanDoubleNonNull(item.initializer)) {
    if (isLeftHandExpression(item.initializer.expression.expression)) {
      const result: Record<string, boolean> = { hasQuestionToken: false };
      traverseExpressionNode(item.initializer.expression.expression, result);
      if (result.hasQuestionToken) {
        log.push({
          type: LogType.ERROR,
          message: `The optional character can not be used in the initial value of property '${itemName}'.`,
          pos: item.getStart(),
          code: '10905320'
        });
        return;
      }
      if (childStructInfo.paramDecoratorMap.has(itemName) &&
        childStructInfo.eventDecoratorMap.has('$' + itemName)) {
        newProperties.push(createUpdateTwoWayNode(itemName, item.initializer.expression.expression));
        return;
      }
      log.push({
        type: LogType.ERROR,
        message: 'When the two-way binding syntax is used, ' +
          `the variable '${itemName}' must be decorated with '@Param', ` +
          `and the '@Event' variable '$` + `${itemName}' ` + `must be defined in the ${childStructInfo.structName}.`,
        pos: item.getStart(),
        code: '10905319'
      });
      return;
    }
    log.push({
      type: LogType.ERROR,
      message: 'When the two-way binding syntax is used, ' +
        `the initial value of property '${itemName}' must be a variable.`,
      pos: item.getStart(),
      code: '10905318'
    });
    return;
  }
}

function validateTwoWayComputed(node: ts.PropertyAccessExpression, log: LogInfo[]): void {
  const checker: ts.TypeChecker | undefined = CurrentProcessFile.getChecker();
  if (ts.isPropertyAccessExpression(node) && node.expression && 
    node.expression.kind === ts.SyntaxKind.ThisKeyword && checker) {
    const symbol: ts.Symbol = checker.getSymbolAtLocation(node);
    logMessageCollection.checkTwoWayComputed(node, symbol, log);
  }
}

function createUpdateTwoWayNode(itemName: string, leftHandExpression: ts.Expression): ts.PropertyAssignment {
  return ts.factory.createPropertyAssignment(
    ts.factory.createIdentifier('$' + itemName),
    ts.factory.createArrowFunction(undefined, undefined,
      [createAstNodeUtils.createParameterDeclaration('value')], undefined,
      ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
      ts.factory.createBlock([
        ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
          leftHandExpression, ts.factory.createToken(ts.SyntaxKind.EqualsToken),
          ts.factory.createIdentifier('value')
        ))
      ], false)
    )
  );
}

function isDoubleNonNullExpression(node: ts.Expression): boolean {
  return node && ts.isNonNullExpression(node) && ts.isNonNullExpression(node.expression);
}

function moreThanDoubleNonNull(node: ts.Expression): boolean {
  return node && ts.isNonNullExpression(node) && ts.isNonNullExpression(node.expression) && ts.isNonNullExpression(node.expression.expression);
}

function isLeftHandExpression(node: ts.Expression): boolean {
  return node && (ts.isIdentifier(node) || ts.isPropertyAccessExpression(node));
}

function traverseExpressionNode(node: ts.Node, result: Record<string, boolean>): void {
  if (ts.isOptionalChain(node) && !ts.isNonNullExpression(node) && node.questionDotToken) {
    result.hasQuestionToken = true;
  }
  if (!result.hasQuestionToken) {
    ts.forEachChild(node, (child: ts.Node) => traverseExpressionNode(child, result));
  }
}

function componentParamDetachment(newNode: ts.NewExpression, isRecycleComponent: boolean,
  name: string, log: LogInfo[], componentNode: ts.CallExpression): ts.VariableStatement {
  const paramsArray: ts.Expression[] = newNode.arguments.length ? newNode.arguments : [];
  const updateNewNode = ts.factory.updateNewExpression(newNode, newNode.expression,
    newNode.typeArguments, traverseChildComponentArgs(paramsArray, name, log, componentNode));
  return ts.factory.createVariableStatement(
    undefined,
    ts.factory.createVariableDeclarationList(
      [ts.factory.createVariableDeclaration(
        ts.factory.createIdentifier(COMPONENT_CALL),
        undefined,
        undefined,
        isRecycleComponent ? ts.factory.createConditionalExpression(
          ts.factory.createIdentifier(RECYCLE_NODE),
          ts.factory.createToken(ts.SyntaxKind.QuestionToken),
          ts.factory.createIdentifier(RECYCLE_NODE),
          ts.factory.createToken(ts.SyntaxKind.ColonToken),
          newNode) : updateNewNode
      )],
      ts.NodeFlags.Let
    ));
}

function createRecycleFlagConditionalExpression(): ts.Statement {
  return ts.factory.createVariableStatement(
    undefined,
    ts.factory.createVariableDeclarationList(
      [
        ts.factory.createVariableDeclaration(
          ts.factory.createIdentifier(RECYCLEFLAG),
          undefined,
          undefined,
          ts.factory.createConditionalExpression(
            ts.factory.createIdentifier(ISPRERENDER),
            ts.factory.createToken(ts.SyntaxKind.QuestionToken),
            ts.factory.createBinaryExpression(
              ts.factory.createIdentifier(RECYCLE_NODE),
              ts.factory.createToken(ts.SyntaxKind.EqualsEqualsToken),
              ts.factory.createNull()
            ),
            ts.factory.createToken(ts.SyntaxKind.ColonToken),
            ts.factory.createBinaryExpression(
              ts.factory.createIdentifier(RECYCLE_NODE),
              ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken),
              ts.factory.createNull()
            )
          ),
        )
      ],
      ts.NodeFlags.Let
    )
  );
}

function createNewComponent(componeParamName: string, name: string,
  componentNode: ts.CallExpression): ts.Statement {
  const childStructInfo: StructInfo = processStructComponentV2.getAliasStructInfo(componentNode) ||
    processStructComponentV2.getOrCreateStructInfo(name);
  return ts.factory.createExpressionStatement(
    ts.factory.createCallExpression(
      ts.factory.createPropertyAccessExpression(
        ts.factory.createIdentifier(
          childStructInfo.isComponentV2 ? constantDefine.STRUCT_PARENT : BASE_COMPONENT_NAME_PU),
        ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION)
      ), undefined, [ts.factory.createIdentifier(componeParamName)]));
}

function createNewRecycleComponent(newNode: ts.NewExpression, componentNode: ts.CallExpression,
  name: string, componentAttrInfo: ComponentAttrInfo): ts.Statement {
  let argNode: ts.Expression[] = [];
  const componentParam: ts.PropertyAssignment[] = [];
  if (componentNode.arguments && componentNode.arguments.length > 0 &&
    ts.isObjectLiteralExpression(componentNode.arguments[0]) && componentNode.arguments[0].properties) {
    componentNode.arguments[0].properties.forEach((propertyItem: ts.PropertyAssignment) => {
      const newPropertyItem: ts.PropertyAssignment = isProperty(propertyItem) ?
        createReference(propertyItem, [], false, false, true) : propertyItem;
      componentParam.push(newPropertyItem);
    });
    argNode = [ts.factory.createObjectLiteralExpression(componentParam, false)];
  } else {
    argNode = [ts.factory.createObjectLiteralExpression([], false)];
  }
  const recycleNode: ts.CallExpression = ts.factory.createCallExpression(
    createRecyclePropertyNode(ABOUT_TO_REUSE), undefined, argNode);
  return ts.factory.createExpressionStatement(
    ts.factory.createCallExpression(
      ts.factory.createPropertyAccessExpression(
        ts.factory.createIdentifier(BASE_COMPONENT_NAME_PU),
        ts.factory.createIdentifier(COMPONENT_CREATE_RECYCLE)
      ), undefined,
      [
        ts.factory.createIdentifier(COMPONENT_CALL),
        // reuse_5
        isCompatibleVersionOverTarget(26) ?
          ts.factory.createIdentifier(RECYCLEFLAG) :
          ts.factory.createBinaryExpression(
            ts.factory.createIdentifier(RECYCLE_NODE),
            ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken),
            ts.factory.createNull()
          ),
        componentAttrInfo.reuseId ? componentAttrInfo.reuseId as ts.Expression :
          ts.factory.createStringLiteral(name),
        ts.factory.createArrowFunction(undefined, undefined, [], undefined,
          ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
          ts.factory.createBlock([
            isCompatibleVersionOverTarget(26) ?
              ts.factory.createIfStatement(
                ts.factory.createBinaryExpression(
                  ts.factory.createPropertyAccessExpression(
                    ts.factory.createThis(),
                    ts.factory.createIdentifier(PUSH_RECYCLE_ELMTID_TO_RENDERSTACK)
                  ),
                  ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
                  ts.factory.createBinaryExpression(
                    ts.factory.createTypeOfExpression(
                      ts.factory.createPropertyAccessExpression(
                        ts.factory.createThis(),
                        ts.factory.createIdentifier(PUSH_RECYCLE_ELMTID_TO_RENDERSTACK)
                      )
                    ),
                    ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
                    ts.factory.createStringLiteral(FUNCTION)
                  )
                ),
                ts.factory.createBlock(
                  [
                    ts.factory.createExpressionStatement(
                      ts.factory.createCallExpression(
                        ts.factory.createPropertyAccessExpression(
                          ts.factory.createThis(),
                          ts.factory.createIdentifier(PUSH_RECYCLE_ELMTID_TO_RENDERSTACK)
                        ),
                        undefined,
                        [
                          ts.factory.createCallChain(
                            ts.factory.createPropertyAccessChain(
                              ts.factory.createIdentifier(RECYCLE_NODE),
                              ts.factory.createToken(ts.SyntaxKind.QuestionDotToken),
                              ts.factory.createIdentifier(ABOUT_TO_BE_DELETE_FUNCTION_ID__)
                            ),
                            undefined,
                            undefined,
                            []
                          )
                        ]
                      )
                    )
                  ],
                  true
                ),
                undefined
              ) : undefined,
            ts.factory.createIfStatement(
              ts.factory.createBinaryExpression(
                ts.factory.createIdentifier(RECYCLE_NODE),
                ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
                ts.factory.createBinaryExpression(
                  ts.factory.createTypeOfExpression(
                    createRecyclePropertyNode(COMPONENT_ABOUTTOREUSEINTERNAL_FUNCTION)),
                  ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
                  ts.factory.createStringLiteral(FUNCTION)
                )),
              ts.factory.createBlock([
                ts.factory.createExpressionStatement(ts.factory.createCallExpression(
                  createRecyclePropertyNode(COMPONENT_ABOUTTOREUSEINTERNAL_FUNCTION),
                  undefined,
                  []
                ))
              ], true),
              ts.factory.createBlock(
                [
                  ts.factory.createIfStatement(ts.factory.createBinaryExpression(
                    createRecyclePropertyNode(ABOUT_TO_REUSE), ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
                    ts.factory.createBinaryExpression(
                      ts.factory.createTypeOfExpression(createRecyclePropertyNode(ABOUT_TO_REUSE)),
                      ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
                      ts.factory.createStringLiteral(FUNCTION)
                    )),
                  ts.factory.createBlock([ts.factory.createExpressionStatement(recycleNode)], true)),
                  ts.factory.createExpressionStatement(ts.factory.createCallExpression(
                    createRecyclePropertyNode(COMPONENT_RERENDER_FUNCTION), undefined, []
                  ))
                ],
                true
              )
            ),
            isCompatibleVersionOverTarget(20) ?
              ts.factory.createIfStatement(
                ts.factory.createBinaryExpression(
                  ts.factory.createPropertyAccessExpression(
                    ts.factory.createThis(),
                    ts.factory.createIdentifier(POP_RECYCLE_ELMTID_TO_RENDERSTACK)
                  ),
                  ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
                  ts.factory.createBinaryExpression(
                    ts.factory.createTypeOfExpression(
                      ts.factory.createPropertyAccessExpression(
                        ts.factory.createThis(),
                        ts.factory.createIdentifier(POP_RECYCLE_ELMTID_TO_RENDERSTACK)
                      )
                    ),
                    ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
                    ts.factory.createStringLiteral(FUNCTION)
                  )
                ),
                ts.factory.createBlock(
                  [
                    ts.factory.createExpressionStatement(
                      ts.factory.createCallExpression(
                        ts.factory.createPropertyAccessExpression(
                          ts.factory.createThis(),
                          ts.factory.createIdentifier(POP_RECYCLE_ELMTID_TO_RENDERSTACK)
                        ),
                        undefined,
                        []
                      )
                    )
                  ],
                  true
                ),
                undefined
              ) : undefined,
          ], true))
      ]));
}

function createRecyclePropertyNode(recycleFunctionName: string): ts.PropertyAccessExpression {
  return ts.factory.createPropertyAccessExpression(
    ts.factory.createIdentifier(RECYCLE_NODE), ts.factory.createIdentifier(recycleFunctionName));
}

function validateCustomComponentPrams(node: ts.CallExpression, name: string,
  props: ts.ObjectLiteralElementLike[], log: LogInfo[], isBuilder: boolean): void {
  const curChildProps: Set<string> = new Set([]);
  const nodeArguments: ts.NodeArray<ts.Expression> = node.arguments;
  const linkSet: Set<string> = getCollectionSet(name, linkCollection);
  if (nodeArguments && nodeArguments.length >= 1 &&
    ts.isObjectLiteralExpression(nodeArguments[0])) {
    const nodeArgument: ts.ObjectLiteralExpression = nodeArguments[0] as ts.ObjectLiteralExpression;
    const doubleExclamationCollection: string[] = [];
    const dollarPropertyCollection: string[] = [];
    nodeArgument.properties.forEach(item => {
      if (item.name && ts.isIdentifier(item.name)) {
        const propName: string = item.name.escapedText.toString();
        curChildProps.add(propName);
        logMessageCollection.checkIfAssignToStaticProps(item, propName, regularStaticCollection.get(name) || new Set(), log);
      }
      validateStateManagement(item, name, log, isBuilder, doubleExclamationCollection, dollarPropertyCollection);
      if (isNonThisProperty(item, linkSet)) {
        if (isToChange(item as ts.PropertyAssignment, name)) {
          item = ts.factory.updatePropertyAssignment(item as ts.PropertyAssignment,
            item.name, changeNodeFromCallToArrow(item.initializer));
        }
        props.push(item);
      }
    });
    logMessageCollection.checkIfNeedDollarEvent(doubleExclamationCollection, dollarPropertyCollection, node, log);
  }
  const parentStructInfo: StructInfo = componentCollection.currentClassName ?
    processStructComponentV2.getOrCreateStructInfo(componentCollection.currentClassName) :
    new StructInfo();
  validateInitDecorator(node, name, curChildProps, log);
  validateMandatoryToInitViaParam(node, name, curChildProps, log, parentStructInfo);
  validateInitParam(name, curChildProps, node, log, parentStructInfo);
}

function getCustomComponentNode(node: ts.ExpressionStatement): ts.CallExpression {
  let temp: any = node.expression;
  let child: any = null;
  let componentNode: any = null;
  while (temp) {
    if (ts.isIdentifier(temp)) {
      child = temp;
      break;
    }
    temp = temp.expression;
  }
  if (child) {
    let parent = child.parent;
    while (parent) {
      if (ts.isExpressionStatement(parent)) {
        break;
      }
      if (ts.isCallExpression(parent) || ts.isEtsComponentExpression(parent)) {
        componentNode = parent;
        break;
      }
      parent = parent.parent;
    }
  }
  return componentNode;
}

export enum ParentType {
  NormalComponentV1,
  NormalComponentV2,
  ReuseComponentV1,
  ReuseComponentV2,
  InvalidComponentType
}

function getParentComponentType(parentName: string): ParentType {
  const parentCustomComponentInfo: StructInfo = parentName ? 
    processStructComponentV2.getOrCreateStructInfo(parentName) : new StructInfo();
  let type: ParentType = ParentType.InvalidComponentType;
  if (parentCustomComponentInfo.isComponentV1 &&
    parentCustomComponentInfo.isReusable) {
    type = ParentType.ReuseComponentV1;
  } else if (parentCustomComponentInfo.isComponentV1 &&
    !parentCustomComponentInfo.isReusable) {
    type = ParentType.NormalComponentV1;
  } else if (parentCustomComponentInfo.isComponentV2 &&
    parentCustomComponentInfo.isReusableV2) {
    type = ParentType.ReuseComponentV2;
  } else if (parentCustomComponentInfo.isComponentV2 &&
    !parentCustomComponentInfo.isReusableV2) {
    type = ParentType.NormalComponentV2;
  }
  return type;
}

function getCollectionSet(name: string, collection: Map<string, Set<string>>): Set<string> {
  if (!collection) {
    return new Set([]);
  }
  return collection.get(name) || new Set([]);
}

function isThisProperty(node: ts.ObjectLiteralElementLike, propertySet: Set<string>): boolean {
  if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) &&
    propertySet.has(node.name.escapedText.toString())) {
    return true;
  }
  return false;
}

function isNonThisProperty(node: ts.ObjectLiteralElementLike, propertySet: Set<string>): boolean {
  if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) &&
    (node.initializer.escapedText && node.initializer.escapedText.includes('$') ||
    ts.isPropertyAccessExpression(node.initializer) && node.initializer.expression &&
    node.initializer.expression.kind === ts.SyntaxKind.ThisKeyword &&
    ts.isIdentifier(node.initializer.name) && node.initializer.name.escapedText.toString().includes('$'))) {
    return false;
  }
  if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) &&
    !propertySet.has(node.name.escapedText.toString())) {
    return true;
  }
  return false;
}

function validateStateManagement(node: ts.ObjectLiteralElementLike, customComponentName: string, log: LogInfo[], 
  isBuilder: boolean, doubleExclamationCollection: string[], dollarPropertyCollection: string[]): void {
  validateForbiddenToInitViaParam(node, customComponentName, log);
  if (componentCollection.currentClassName) {
    const parentStructInfo: StructInfo =
      processStructComponentV2.getOrCreateStructInfo(componentCollection.currentClassName);
    if (parentStructInfo.isComponentV2) {
      if (ts.isPropertyAssignment(node)) {
        checkDoubleExclamationPropertyAssignment(node, log, doubleExclamationCollection);
        checkDollarPropertyAssignment(node, dollarPropertyCollection);
      }
      return;
    }
  }
  checkFromParentToChild(node, customComponentName, log, isBuilder);
}

function checkDoubleExclamationPropertyAssignment(node: ts.PropertyAssignment, log: LogInfo[], doubleExclamationCollection: string[]): void {
  if (node.initializer && isDoubleNonNullExpression(node.initializer)) {
    if (node.initializer.expression && node.initializer.expression.expression && 
      isLeftHandExpression(node.initializer.expression.expression)) {
        doubleExclamationCollection.push(node.name.getText());
        validateTwoWayComputed(node.initializer.expression.expression, log);
    }
  }
}

function checkDollarPropertyAssignment(node: ts.PropertyAssignment, dollarPropertyCollection: string[]): void {
  const regex = /^\$[a-zA-Z_][a-zA-Z0-9_]*$/;
  if (node.name && ts.isIdentifier(node.name) && regex.test(node.name.escapedText.toString())) {
    dollarPropertyCollection.push(node.name.escapedText.toString());
  }
}

function checkFromParentToChild(node: ts.ObjectLiteralElementLike, customComponentName: string,
  log: LogInfo[], isBuilder: boolean): void {
  let propertyName: string;
  if (ts.isIdentifier(node.name)) {
    propertyName = node.name.escapedText.toString();
  }
  const curPropertyKind: string = getPropertyDecoratorKind(propertyName, customComponentName);
  let parentPropertyName: string;
  if (curPropertyKind) {
    if (isInitFromParent(node)) {
      parentPropertyName =
        getParentPropertyName(node as ts.PropertyAssignment, curPropertyKind, log);
      let parentPropertyKind: string = PropMapManager.find(parentPropertyName);
      if (parentPropertyKind && !isCorrectInitFormParent(parentPropertyKind, curPropertyKind)) {
        validateIllegalInitFromParent(
          node, propertyName, curPropertyKind, parentPropertyName, parentPropertyKind, log);
      }
    } else if (isInitFromLocal(node) && ts.isPropertyAssignment(node) &&
      curPropertyKind !== COMPONENT_OBJECT_LINK_DECORATOR) {
      if (!isCorrectInitFormParent(COMPONENT_NON_DECORATOR, curPropertyKind)) {
        validateIllegalInitFromParent(node, propertyName, curPropertyKind,
          node.initializer.getText(), COMPONENT_NON_DECORATOR, log);
      }
    } else if (curPropertyKind === COMPONENT_OBJECT_LINK_DECORATOR && node.initializer &&
      (ts.isPropertyAccessExpression(node.initializer) ||
        ts.isElementAccessExpression(node.initializer) || ts.isIdentifier(node.initializer))) {
      return;
    } else {
      parentPropertyName =
        getParentPropertyName(node as ts.PropertyAssignment, curPropertyKind, log) || propertyName;
      const parentPropertyKind = COMPONENT_NON_DECORATOR;
      if (!isCorrectInitFormParent(parentPropertyKind, curPropertyKind)) {
        if (isBuilder && judgeStructAssignedDollar(node)) {
          log.push({
            type: LogType.WARN,
            message: `Unrecognized property '${parentPropertyName}', make sure it can be assigned to ` +
              `'${curPropertyKind}' property '${propertyName}' by yourself.`,
            // @ts-ignore
            pos: node.initializer ? node.initializer.getStart() : node.getStart()
          });
        } else {
          validateIllegalInitFromParent(
            node, propertyName, curPropertyKind, parentPropertyName, parentPropertyKind, log, LogType.WARN);
        }
      }
    }
    if (!isBuilder && ts.isPropertyAssignment(node) && curPropertyKind === COMPONENT_LINK_DECORATOR) {
      validateLinkVariableSourceData(node, log);
    }
  }
}

function validateLinkVariableSourceData(node: ts.PropertyAssignment, log: LogInfo[]): void {
  if (isInitFromMismatchSourceData(node)) {
    log.push({
      type: LogType.ERROR,
      message: `The type of the parent component's state variable initializing the '@Link' variable ` +
      `'${node.name.getText()}' must match the '@Link' variable's declared type.`,
      pos: node.getStart(),
      code: '10905364'
    });
  }
}

function isInitFromMismatchSourceData(node: ts.PropertyAssignment): boolean {
  let curNode = node.initializer;
  let nestingLevel = 0;
  while (ts.isPropertyAccessExpression(curNode) || ts.isElementAccessExpression(curNode) ||
    ts.isNonNullExpression(curNode)) {
    if (!ts.isNonNullExpression(curNode)) {
      nestingLevel++;
    }
    curNode = curNode.expression;
  }
  return nestingLevel >= MAX_LINK_SOURCE_DATA_NESTING_LEVEL && curNode.kind === ts.SyntaxKind.ThisKeyword;
}

function judgeStructAssignedDollar(node: ts.ObjectLiteralElementLike): boolean {
  return partialUpdateConfig.partialUpdateMode && node.initializer &&
    ts.isPropertyAccessExpression(node.initializer) &&
    node.initializer.expression && ts.isIdentifier(node.initializer.expression) &&
    node.initializer.expression.escapedText.toString() === $$;
}

function isInitFromParent(node: ts.ObjectLiteralElementLike): boolean {
  if (ts.isPropertyAssignment(node) && node.initializer) {
    if (ts.isPropertyAccessExpression(node.initializer) && node.initializer.expression &&
    node.initializer.expression.kind === ts.SyntaxKind.ThisKeyword &&
    ts.isIdentifier(node.initializer.name)) {
      return true;
    } else if (ts.isIdentifier(node.initializer) &&
      matchStartWithDollar(node.initializer.getText())) {
      return true;
    }
  }
  return false;
}

function getInitializerName(node: ts.PropertyAssignment): string {
  if (ts.isPropertyAccessExpression(node.initializer) && node.initializer.expression &&
    node.initializer.expression.kind === ts.SyntaxKind.ThisKeyword &&
    ts.isIdentifier(node.initializer.name)) {
    return node.initializer.name.escapedText.toString();
  } else if (ts.isIdentifier(node.initializer) &&
    matchStartWithDollar(node.initializer.getText())) {
    return node.initializer.getText().slice(1);
  }
  return '';
}

function isInitFromLocal(node: ts.ObjectLiteralElementLike): boolean {
  if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.initializer) &&
    !matchStartWithDollar(node.initializer.getText())) {
    return true;
  }
  return false;
}

function getParentPropertyName(node: ts.PropertyAssignment, curPropertyKind: string,
  log: LogInfo[]): string {
  const initExpression: ts.Expression = node.initializer;
  const checker: ts.TypeChecker | undefined = CurrentProcessFile.getChecker();
  if (!initExpression || !checker) {
    return undefined;
  }
  let parentPropertyName: string = initExpression.getText();
  const symbol = checker?.getSymbolAtLocation(initExpression);
  if (curPropertyKind === COMPONENT_LINK_DECORATOR) {
    // @ts-ignore
    const initName: ts.Identifier = initExpression.name || initExpression;
    if (!symbol && hasDollar(initExpression)) {
      parentPropertyName = initName.getText().replace(/^\$/, '');
    } else {
      parentPropertyName = initName.getText();
    }
  } else {
    if (!symbol && hasDollar(initExpression)) {
      validateNonLinkWithDollar(node, log);
    }
    // @ts-ignore
    if (node.initializer && node.initializer.name) {
      parentPropertyName = node.initializer.name.getText();
    }
  }

  return parentPropertyName;
}

function isCorrectInitFormParent(parent: string, child: string): boolean {
  switch (child) {
    case COMPONENT_STATE_DECORATOR:
    case COMPONENT_PROP_DECORATOR:
    case COMPONENT_PROVIDE_DECORATOR:
      return true;
    case COMPONENT_NON_DECORATOR:
      if ([COMPONENT_NON_DECORATOR, COMPONENT_STATE_DECORATOR, COMPONENT_LINK_DECORATOR, COMPONENT_PROP_DECORATOR,
        COMPONENT_OBJECT_LINK_DECORATOR, COMPONENT_STORAGE_LINK_DECORATOR, COMPONENT_ENV_DECORATOR].includes(parent)) {
        return true;
      }
      break;
    case COMPONENT_LINK_DECORATOR:
    case COMPONENT_OBJECT_LINK_DECORATOR:
      return ![COMPONENT_NON_DECORATOR].includes(parent);
  }
  return false;
}

function getPropertyDecoratorKind(propertyName: string, customComponentName: string): string {
  for (const item of decoractorMap.entries()) {
    if (getCollectionSet(customComponentName, item[1]).has(propertyName)) {
      return item[0];
    }
  }
  return undefined;
}

function createFindChildById(id: string, name: string, isBuilder: boolean = false): ts.VariableStatement {
  return ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList(
    [ts.factory.createVariableDeclaration(ts.factory.createIdentifier(
      `${CUSTOM_COMPONENT_EARLIER_CREATE_CHILD}${id}`), undefined, ts.factory.createTypeReferenceNode(
      ts.factory.createIdentifier(name)),
    ts.factory.createConditionalExpression(
      ts.factory.createParenthesizedExpression(
        ts.factory.createBinaryExpression(
          createConditionParent(isBuilder),
          ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
          ts.factory.createPropertyAccessExpression(
            createConditionParent(isBuilder),
            ts.factory.createIdentifier(CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID)
          ))), ts.factory.createToken(ts.SyntaxKind.QuestionToken),
      ts.factory.createAsExpression(ts.factory.createCallExpression(
        ts.factory.createPropertyAccessExpression(createConditionParent(isBuilder),
          ts.factory.createIdentifier(`${CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID}`)), undefined,
        [isBuilder ? ts.factory.createCallExpression(ts.factory.createIdentifier(GENERATE_ID),
          undefined, []) : ts.factory.createStringLiteral(id)]),
      ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(name))),
      ts.factory.createToken(ts.SyntaxKind.ColonToken),
      ts.factory.createIdentifier(COMPONENT_IF_UNDEFINED)))], ts.NodeFlags.Let));
}

export function createConditionParent(isBuilder: boolean): ts.ParenthesizedExpression | ts.ThisExpression {
  return isBuilder ? ts.factory.createParenthesizedExpression(parentConditionalExpression()) : ts.factory.createThis();
}

function createCustomComponentIfStatement(id: string, node: ts.ExpressionStatement,
  newObjectLiteralExpression: ts.ObjectLiteralExpression, parentName: string): ts.IfStatement {
  const viewName: string = `${CUSTOM_COMPONENT_EARLIER_CREATE_CHILD}${id}`;
  return ts.factory.createIfStatement(ts.factory.createBinaryExpression(
    ts.factory.createIdentifier(viewName),
    ts.factory.createToken(ts.SyntaxKind.EqualsEqualsToken),
    ts.factory.createIdentifier(`${COMPONENT_CONSTRUCTOR_UNDEFINED}`)),
  ts.factory.createBlock([node], true),
  ts.factory.createBlock([ts.factory.createExpressionStatement(ts.factory.createCallExpression(
    ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(
      viewName), ts.factory.createIdentifier(
      `${COMPONENT_CONSTRUCTOR_UPDATE_PARAMS}`)), undefined, [newObjectLiteralExpression])),
  isStaticViewCollection.get(parentName) ? createStaticIf(viewName) : undefined,
  ts.factory.createExpressionStatement(ts.factory.createCallExpression(
    ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(`${BASE_COMPONENT_NAME}`),
      ts.factory.createIdentifier(`${COMPONENT_CREATE_FUNCTION}`)), undefined,
    [ts.factory.createIdentifier(viewName)]))], true));
}

function createStaticIf(name: string): ts.IfStatement {
  return ts.factory.createIfStatement(ts.factory.createPrefixUnaryExpression(
    ts.SyntaxKind.ExclamationToken, ts.factory.createCallExpression(
      ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name),
        ts.factory.createIdentifier(CUSTOM_COMPONENT_NEEDS_UPDATE_FUNCTION)), undefined, [])),
  ts.factory.createBlock([ts.factory.createExpressionStatement(ts.factory.createCallExpression(
    ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name),
      ts.factory.createIdentifier(CUSTOM_COMPONENT_MARK_STATIC_FUNCTION)),
    undefined, []))], true), undefined);
}

function hasDollar(initExpression: ts.Expression): boolean {
  if (ts.isPropertyAccessExpression(initExpression) &&
    matchStartWithDollar(initExpression.name.getText())) {
    return true;
  } else if (ts.isIdentifier(initExpression) && matchStartWithDollar(initExpression.getText())) {
    return true;
  } else {
    return false;
  }
}

function matchStartWithDollar(name: string): boolean {
  return /^\$/.test(name);
}

function getForbbiddenToInitViaParamType(customComponentName: string,
  node: ts.Identifier): string {
  let propType: string = COMPONENT_CONSUME_DECORATOR;
  const propName: string = node.escapedText.toString();
  if (getCollectionSet(customComponentName, storageLinkCollection).has(propName)) {
    propType = COMPONENT_STORAGE_LINK_DECORATOR;
  } else if (getCollectionSet(customComponentName, storagePropCollection).has(propName)) {
    propType = COMPONENT_STORAGE_PROP_DECORATOR;
  } else if (ifLocalStorageLink(customComponentName, propName)) {
    propType = COMPONENT_LOCAL_STORAGE_LINK_DECORATOR;
  } else if (ifLocalStorageProp(customComponentName, propName)) {
    propType = COMPONENT_LOCAL_STORAGE_PROP_DECORATOR;
  }
  return propType;
}

function ifLocalStorageProp(componentName: string, propName: string): boolean {
  if (!localStoragePropCollection.get(componentName).keys) {
    return false;
  } else {
    const collection: Set<string> = new Set();
    for (const key of localStoragePropCollection.get(componentName).keys()) {
      collection.add(key);
    }
    return collection.has(propName);
  }
}

function ifLocalStorageLink(componentName: string, propName: string): boolean {
  if (!localStorageLinkCollection.get(componentName).keys) {
    return false;
  } else {
    const collection: Set<string> = new Set();
    for (const key of localStorageLinkCollection.get(componentName).keys()) {
      collection.add(key);
    }
    return collection.has(propName);
  }
}

function validateForbiddenToInitViaParam(node: ts.ObjectLiteralElementLike,
  customComponentName: string, log: LogInfo[]): void {
  const forbiddenToInitViaParamSet: Set<string> = new Set([
    ...getCollectionSet(customComponentName, storageLinkCollection),
    ...getCollectionSet(customComponentName, storagePropCollection),
    ...getCollectionSet(customComponentName, consumeCollection)
  ]);
  const localStorageSet: Set<string> = new Set();
  getLocalStorageCollection(customComponentName, localStorageSet);
  if (isThisProperty(node, forbiddenToInitViaParamSet) || isThisProperty(node, localStorageSet)) {
    const nodeIdentifier: ts.Identifier = node.name as ts.Identifier;
    const propType: string = getForbbiddenToInitViaParamType(customComponentName, nodeIdentifier);
    log.push({
      type: LogType.ERROR,
      message: `The '${propType}' property '${node.name.getText()}' in the custom component '${customComponentName}'` +
        ` cannot be initialized here (forbidden to specify).`,
      pos: node.name.getStart(),
      code: '10905317'
    });
  }
}
  
function validateMandatoryToInitViaParam(node: ts.CallExpression, customComponentName: string,
  curChildProps: Set<string>, log: LogInfo[], parentStructInfo: StructInfo): void {
  let mandatoryToInitViaParamSet: Set<string>;
  if (projectConfig.compileMode === 'esmodule' && process.env.compileTool === 'rollup' && node.expression) {
    const overAll: string[] = [
      ...getCollectionSet(node.expression.getText(), storedFileInfo.overallObjectLinkCollection)];
    if (!parentStructInfo.isComponentV2) {
      overAll.unshift(...getCollectionSet(node.expression.getText(), storedFileInfo.overallLinkCollection));
    }
    mandatoryToInitViaParamSet = new Set(overAll);
    customComponentName = node.expression.getText();
  } else {
    const arr: string[] = [...getCollectionSet(customComponentName, objectLinkCollection)];
    if (!parentStructInfo.isComponentV2) {
      arr.unshift(...getCollectionSet(customComponentName, linkCollection));
    }
    mandatoryToInitViaParamSet = new Set(arr);
  }
  mandatoryToInitViaParamSet.forEach(item => {
    if (item && !curChildProps.has(item)) {
      log.push({
        type: LogType.ERROR,
        message: `The property '${item}' in the custom component '${customComponentName}'` +
          ` is missing (mandatory to specify).`,
        pos: node.getStart(),
        code: '10905316'
      });
    }
  });
}

function validateInitDecorator(node: ts.CallExpression, customComponentName: string,
  curChildProps: Set<string>, log: LogInfo[]): void {
  const mandatoryToInitViaParamSet: Set<string> = new Set([
    ...getCollectionSet(customComponentName, builderParamObjectCollection),
    ...getCollectionSet(customComponentName, propCollection),
    ...getCollectionSet(customComponentName, regularCollection),
    ...getCollectionSet(customComponentName, stateCollection),
    ...getCollectionSet(customComponentName, provideCollection)
  ]);
  const decoratorVariable: Set<string> = new Set([
    ...(builderParamInitialization.get(customComponentName) || []),
    ...(propInitialization.get(customComponentName) || []),
    ...(regularInitialization.get(customComponentName) || []),
    ...(stateInitialization.get(customComponentName) || []),
    ...(provideInitialization.get(customComponentName) || [])
  ]);
  mandatoryToInitViaParamSet.forEach((item: string) => {
    if (item && !curChildProps.has(item) && decoratorVariable && decoratorVariable.has(item)) {
      log.push({
        type: LogType.ERROR,
        message: `'@Require' decorated '${item}' must be initialized through the component constructor.`,
        pos: node.getStart(),
        code: '10905359'
      });
    }
  });
  const privateParamSet: Set<string> = privateCollection.get(customComponentName) || new Set([]);
  curChildProps.forEach((item: string) => {
    if (privateParamSet.has(item)) {
      log.push({
        type: LogType.WARN,
        message: `Property '${item}' is private and can not be initialized through the component constructor.`,
        pos: node.getStart()
      });
    }
  });
}

function validateIllegalInitFromParent(node: ts.ObjectLiteralElementLike, propertyName: string,
  curPropertyKind: string, parentPropertyName: string, parentPropertyKind: string,
  log: LogInfo[], inputType: LogType = undefined): void {
  let type: LogType = LogType.ERROR;
  if (inputType) {
    type = inputType;
  } else if ([COMPONENT_STATE_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR].includes(
    parentPropertyKind) && curPropertyKind === COMPONENT_OBJECT_LINK_DECORATOR) {
    type = LogType.WARN;
  }
  PropMapManager.reserveLog(parentPropertyName, parentPropertyKind, {
    type: type,
    message: `The '${parentPropertyKind}' property '${parentPropertyName}' cannot be assigned to ` +
      `the '${curPropertyKind}' property '${propertyName}'.`,
    // @ts-ignore
    pos: node.initializer ? node.initializer.getStart() : node.getStart(),
    code: type === LogType.ERROR ? '10905315' : undefined
  });
}

function validateNonLinkWithDollar(node: ts.PropertyAssignment, log: LogInfo[]): void {
  log.push({
    type: LogType.ERROR,
    message: `Property '${node.name.getText()}' cannot initialize` +
      ` using '$' to create a reference to a variable.`,
    pos: node.initializer.getStart(),
    code: '10905314'
  });
}

export function isCompatibleVersionOverTarget(apiNum: number): boolean {
  const COMPATIBLE_SDK_VERSION = apiNum;
  if (projectConfig &&
    projectConfig.compatibleSdkVersion &&
    projectConfig.compatibleSdkVersion >= COMPATIBLE_SDK_VERSION
  ) {
    return true;
  }
  return false;
}