import { Container, interfaces } from 'inversify';
import * as React from 'react';
import { useEffect, useRef, useState } from 'react';
import { PipelineDispatcher, PipelineEntitiesSelector, PipelineRegistry, PipelineRenderer } from './pipeline';
import { AbleManager, EntityManager, PlaygroundContext } from '../common';
import { createDefaultPlaygroundConfig, PlaygroundConfig } from './playground-config';
import { Playground } from './playground';
import { PlaygroundContribution, PlaygroundRegistry } from './playground-contribution';
import { bindContributionProvider } from '@gedit/utils';
import {
  PLAYGROUND_ID_PREFIX,
  PlaygroundCommandRegistry,
  PlaygroundId,
  PlaygroundKeybindingRegistry,
  PlaygroundMenuRegistry
} from './playground-registries';
import { getFrontendApplicationSimpleModuleFromCache } from '@gedit/application-simple/lib/browser';
import PlaygroundFrontendModule from './playground-frontend-module';
import {
  PlaygroundConfigEntity,
  PlaygroundConfigEntityData,
  RulerConfigEntity,
  RulerConfigEntityData,
  SelectorBoxConfigData,
  SelectorBoxConfigEntity,
  SelectorConfigEntity,
  SelectorConfigEntityData,
  SnaplineConfigEntity,
  SnaplineConfigEntityData,
  ToolbarConfigEntity,
  ToolbarConfigEntityData
} from './layer/config';
import { SelectorExtendContribution, SelectorExtendContributionRegistry } from './selector-extend-contribution';

let idx = 0;
export function createPlaygroundContainer(parent?: interfaces.Container): Container {
  const child = new Container({defaultScope: 'Singleton'});
  if (parent) {
    child.parent = parent;
  } else {
    // 加载一些IDE的初始化模块, 如皮肤、右键菜单等模块, 用于非IDE场景使用Playground
    child.parent = getFrontendApplicationSimpleModuleFromCache();
    child.parent.load(PlaygroundFrontendModule);
  }
  bindContributionProvider(child, PlaygroundContribution);
  bindContributionProvider(child, SelectorExtendContribution);
  child.bind(PlaygroundConfig).toConstantValue(createDefaultPlaygroundConfig());
  child.bind(AbleManager).toSelf();
  child.bind(EntityManager).toSelf();
  child.bind(PipelineRenderer).toSelf();
  child.bind(PlaygroundRegistry).toSelf();
  child.bind(SelectorExtendContributionRegistry).toSelf();
  child.bind(Playground).toSelf();
  child.bind(PipelineEntitiesSelector).toSelf();
  child.bind(PlaygroundId).toConstantValue(PLAYGROUND_ID_PREFIX + (idx++));
  child.bind(PlaygroundContext).toConstantValue({});
  child.bind(PipelineDispatcher).toSelf();
  child.bind(PipelineRegistry).toSelf();
  child.bind(PlaygroundCommandRegistry).toSelf();
  child.bind(PlaygroundKeybindingRegistry).toSelf();
  child.bind(PlaygroundMenuRegistry).toSelf();
  return child;
}

export function createPlayground(config?: PlaygroundConfig, parentContainer?: interfaces.Container): Playground {
  const child = createPlaygroundContainer(parentContainer);
  child.rebind(PlaygroundConfig).toConstantValue({
    ...createDefaultPlaygroundConfig(),
    ...config,
  });
  return child.get<Playground>(Playground);
}

export interface PlaygroundContainerProps extends Omit<PlaygroundConfig, 'node'> {
  className?: string
  style?: React.CSSProperties
  parentContainer?: interfaces.Container,
  onReady?: (playground: Playground) => void
  rulerConfig?: Partial<RulerConfigEntityData>
  playgroundConfig?: Partial<PlaygroundConfigEntityData>
  selectorBoxConfig?: Partial<SelectorBoxConfigData>,
  selectorConfig?: Partial<SelectorConfigEntityData>,
  snaplineConfig?: Partial<SnaplineConfigEntityData>,
  toolbarConfig?: Partial<ToolbarConfigEntityData>,
}

/**
 * @param props
 * @constructor
 * @example
 *
 * function MyPlayground(props: PlaygroundContainerProps): React.JSX.Element {
 *   return (
 *     <PlaygroundContainer width={800} height={400} />
 *   )
 * }
 */
export function PlaygroundContainer(props: PlaygroundContainerProps): React.JSX.Element {
  // eslint-disable-next-line no-null/no-null
  const playgroundRef = useRef<HTMLDivElement>(null);
  const [playgroundInstance, savePlayground] = useState<Playground>();
  useEffect(() => {
    if (playgroundRef.current) {
      const playground = createPlayground({ ...props, node: playgroundRef.current }, props.parentContainer);
      savePlayground(playground);
      return () => {
        playground.dispose();
      };
    }
  }, [playgroundRef]);
  useEffect(() => {
    if (playgroundInstance) {
      playgroundInstance.resize();
    }
  }, [playgroundInstance, props.width, props.height, props.style, props.className]);
  useEffect(() => {
    if (playgroundInstance) {
      if (props.rulerConfig) playgroundInstance.entityManager.getEntity<RulerConfigEntity>(RulerConfigEntity)?.updateConfig(props.rulerConfig);
      if (props.playgroundConfig) playgroundInstance.entityManager.getEntity<PlaygroundConfigEntity>(PlaygroundConfigEntity)?.updateConfig(props.playgroundConfig);
      if (props.selectorBoxConfig) playgroundInstance.entityManager.getEntity<SelectorBoxConfigEntity>(SelectorBoxConfigEntity)?.updateConfig(props.selectorBoxConfig);
      if (props.selectorConfig) playgroundInstance.entityManager.getEntity<SelectorConfigEntity>(SelectorConfigEntity)?.updateConfig(props.selectorConfig);
      if (props.snaplineConfig) playgroundInstance.entityManager.getEntity<SnaplineConfigEntity>(SnaplineConfigEntity)?.updateConfig(props.snaplineConfig);
      if (props.toolbarConfig) playgroundInstance.entityManager.getEntity<ToolbarConfigEntity>(ToolbarConfigEntity)?.updateConfig(props.toolbarConfig);
    }
  }, [playgroundInstance, props.rulerConfig, props.playgroundConfig, props.selectorBoxConfig, props.selectorConfig, props.snaplineConfig, props.toolbarConfig]);
  useEffect(() => {
    if (playgroundInstance) {
      // Ready 要在配置项之后执行
      playgroundInstance.ready();
      if (props.onReady) props.onReady(playgroundInstance);
    }
  }, [playgroundInstance]);
  const style = {
    ...props.style,
    width: props.width !== undefined ? props.width : props.style?.width,
    height: props.height !== undefined ? props.height : props.style?.height,
  };
  return (
    <div className={'gedit-playground' + (props.className ? ` ${props.className}` : '')} ref={playgroundRef} style={style}/>
  );
}
