// @flow
import { mapFor } from '../Utils/MapFor';
import flatten from 'lodash/flatten';
import { type SelectedTags, hasStringAllTags } from '../Utils/TagsHelper';
const gd: libGDevelop = global.gd;

export type EnumeratedObjectMetadata = {|
  extension: gdPlatformExtension,
  objectMetadata: gdObjectMetadata,
  name: string,
  fullName: string,
  description: string,
  iconFilename: string,
|};

export type ObjectWithContext = {|
  object: gdObject,
  global: boolean,
|};

export type GroupWithContext = {|
  group: gdObjectGroup,
  global: boolean,
|};

export type ObjectWithContextList = Array<ObjectWithContext>;
export type GroupWithContextList = Array<GroupWithContext>;

export const isSameObjectWithContext = (
  objectWithContext: ?ObjectWithContext
) => (other: ?ObjectWithContext) => {
  return (
    objectWithContext &&
    other &&
    objectWithContext.global === other.global &&
    objectWithContext.object === other.object
  );
};

export const enumerateObjects = (
  project: gdObjectsContainer,
  objectsContainer: gdObjectsContainer,
  type: ?string = undefined
) => {
  const filterObject = (object: gdObject): boolean => {
    return (
      !type ||
      gd.getTypeOfObject(project, objectsContainer, object.getName(), false) ===
        type
    );
  };

  const containerObjectsList: ObjectWithContextList = mapFor(
    0,
    objectsContainer.getObjectsCount(),
    i => objectsContainer.getObjectAt(i)
  )
    .filter(filterObject)
    .map((object: gdObject): ObjectWithContext => ({ object, global: false }));

  const projectObjectsList: ObjectWithContextList =
    project === objectsContainer
      ? []
      : mapFor(0, project.getObjectsCount(), i => project.getObjectAt(i))
          .filter(filterObject)
          .map(
            (object: gdObject): ObjectWithContext => ({
              object,
              global: true,
            })
          );

  const allObjectsList: ObjectWithContextList = containerObjectsList.concat(
    projectObjectsList
  );

  return {
    containerObjectsList,
    projectObjectsList,
    allObjectsList,
  };
};

export const enumerateObjectTypes = (
  project: gdProject
): Array<EnumeratedObjectMetadata> => {
  const platform = project.getCurrentPlatform();
  const extensionsList = platform.getAllPlatformExtensions();

  return flatten(
    mapFor(0, extensionsList.size(), i => {
      const extension = extensionsList.at(i);

      return extension
        .getExtensionObjectsTypes()
        .toJSArray()
        .map(objectType => extension.getObjectMetadata(objectType))
        .map(objectMetadata => ({
          extension,
          objectMetadata,
          name: objectMetadata.getName(),
          fullName: objectMetadata.getFullName(),
          description: objectMetadata.getDescription(),
          iconFilename: objectMetadata.getIconFilename(),
        }));
    })
  );
};

export type ObjectFilteringOptions = {|
  searchText: string,
  selectedTags: SelectedTags,
|};

export const filterObjectsList = (
  list: ObjectWithContextList,
  { searchText, selectedTags }: ObjectFilteringOptions
): ObjectWithContextList => {
  if (!searchText && !selectedTags.length) return list;

  const lowercaseSearchText = searchText.toLowerCase();

  return list
    .filter((objectWithContext: ObjectWithContext) => {
      if (!selectedTags.length) return true;

      const objectTags = objectWithContext.object.getTags();
      return hasStringAllTags(objectTags, selectedTags);
    })
    .filter((objectWithContext: ObjectWithContext) => {
      return (
        objectWithContext.object
          .getName()
          .toLowerCase()
          .indexOf(lowercaseSearchText) !== -1
      );
    });
};

export const filterGroupsList = (
  list: GroupWithContextList,
  searchText: string
): GroupWithContextList => {
  if (!searchText) return list;

  const lowercaseSearchText = searchText.toLowerCase();

  return list.filter((groupWithContext: GroupWithContext) => {
    return (
      groupWithContext.group
        .getName()
        .toLowerCase()
        .indexOf(lowercaseSearchText) !== -1
    );
  });
};

export const enumerateGroups = (
  objectGroups: gdObjectGroupsContainer
): Array<gdObjectGroup> => {
  return mapFor(0, objectGroups.count(), i => {
    return objectGroups.getAt(i);
  });
};

export const enumerateObjectsAndGroups = (
  globalObjectsContainer: gdObjectsContainer,
  objectsContainer: gdObjectsContainer,
  type: ?string = undefined
) => {
  const filterObject = (object: gdObject): boolean => {
    return (
      !type ||
      gd.getTypeOfObject(
        globalObjectsContainer,
        objectsContainer,
        object.getName(),
        false
      ) === type
    );
  };
  const filterGroup = (group: gdObjectGroup): boolean => {
    return (
      !type ||
      gd.getTypeOfObject(
        globalObjectsContainer,
        objectsContainer,
        group.getName(),
        true
      ) === type
    );
  };

  const containerObjectsList: ObjectWithContextList = mapFor(
    0,
    objectsContainer.getObjectsCount(),
    i => objectsContainer.getObjectAt(i)
  )
    .filter(filterObject)
    .map(object => ({ object, global: false }));

  const containerGroups = objectsContainer.getObjectGroups();
  const containerGroupsList: GroupWithContextList = enumerateGroups(
    containerGroups
  )
    .filter(filterGroup)
    .map(group => ({ group, global: false }));

  const projectObjectsList: ObjectWithContextList =
    globalObjectsContainer === objectsContainer
      ? []
      : mapFor(0, globalObjectsContainer.getObjectsCount(), i =>
          globalObjectsContainer.getObjectAt(i)
        )
          .filter(filterObject)
          .map(object => ({ object, global: true }));

  const projectGroups = globalObjectsContainer.getObjectGroups();
  const projectGroupsList: GroupWithContextList =
    globalObjectsContainer === objectsContainer
      ? []
      : enumerateGroups(projectGroups)
          .filter(filterGroup)
          .map(group => ({ group, global: true }));

  const allObjectsList: ObjectWithContextList = containerObjectsList.concat(
    projectObjectsList
  );
  const allGroupsList: GroupWithContextList = containerGroupsList.concat(
    projectGroupsList
  );

  return {
    containerObjectsList,
    projectObjectsList,
    allObjectsList,
    containerGroupsList,
    projectGroupsList,
    allGroupsList,
  };
};
