import * as React from 'react';
import './App.scss';
import { MainMenu } from './Components/MainMenu/MainMenu';
import { ContainerModel, findContainerById, IContainerModel, MakeIterator } from './Components/SVG/Elements/ContainerModel';
import Editor, { IEditorState } from './Editor';
import { AvailableContainer } from './Interfaces/AvailableContainer';
import { Configuration } from './Interfaces/Configuration';

export interface IHistoryState {
  MainContainer: IContainerModel | null,
  SelectedContainer: IContainerModel | null,
  SelectedContainerId: string,
  TypeCounters: Record<string, number>
}

interface IAppProps {
}

interface IAppState {
  configuration: Configuration,
  history: IHistoryState[],
  historyCurrentStep: number,
  isLoaded: boolean
}

export class App extends React.Component<IAppProps> {
  public state: IAppState;

  constructor(props: IAppProps) {
    super(props);
    this.state = {
      configuration: {
        AvailableContainers: [],
        AvailableSymbols: [],
        MainContainer: {} as AvailableContainer
      },
      history: [],
      historyCurrentStep: 0,
      isLoaded: false
    };
  }

  public NewEditor() {
    // Fetch the configuration from the API
    fetchConfiguration().then((configuration: Configuration) => {
      // Set the main container from the given properties of the API
      const MainContainer = new ContainerModel(
        null,
        {
          id: 'main',
          parentId: 'null',
          x: 0,
          y: 0,
          width: configuration.MainContainer.Width,
          height: configuration.MainContainer.Height,
          fillOpacity: 0,
          stroke: 'black'
        }
      );

      // Save the configuration and the new MainContainer
      // and default the selected container to it
      this.setState({
        configuration,
        history:
          [
            {
              MainContainer,
              SelectedContainer: MainContainer,
              TypeCounters: {}
            }
          ],
        historyCurrentStep: 0,
        isLoaded: true
      } as IAppState);
    });
  }

  public LoadEditor(files: FileList | null) {
    if (files === null) {
      return;
    }
    const file = files[0];
    const reader = new FileReader();
    reader.addEventListener('load', () => {
      const result = reader.result as string;
      const editorState: IEditorState = JSON.parse(result);

      Revive(editorState);

      this.setState({
        configuration: editorState.configuration,
        history: editorState.history,
        historyCurrentStep: editorState.historyCurrentStep,
        isLoaded: true
      } as IAppState);
    });
    reader.readAsText(file);
  }

  public render() {
    if (this.state.isLoaded) {
      return (
        <div>
          <Editor
            configuration={this.state.configuration}
            history={this.state.history}
            historyCurrentStep={this.state.historyCurrentStep}
          />
        </div>
      );
    } else {
      return (
        <div className='bg-blue-100 h-full w-full'>
          <MainMenu
            newEditor={() => this.NewEditor()}
            loadEditor={(files: FileList | null) => this.LoadEditor(files)}
          />
        </div>
      );
    }
  }
}

/**
 * Fetch the configuration from the API
 * @returns {Configation} The model of the configuration for the application
 */
export async function fetchConfiguration(): Promise<Configuration> {
  const url = `${import.meta.env.VITE_API_URL}`;
  // The test library cannot use the Fetch API
  // @ts-ignore
  if (window.fetch) {
    return await fetch(url, {
      method: 'POST'
    })
      .then((response) =>
        response.json()
      ) as Configuration;
  }
  return new Promise((resolve) => {
    const xhr = new XMLHttpRequest();
    xhr.open('POST', url, true);
    xhr.onreadystatechange = function() { // Call a function when the state changes.
      if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
        resolve(JSON.parse(this.responseText));
      }
    };
    xhr.send();
  });
}

/**
 * Revive the Editor state
 * by setting the containers references to their parent
 * @param editorState Editor state
 */
function Revive(editorState: IEditorState): void {
  const history = editorState.history;
  for (const state of history) {
    if (state.MainContainer === null || state.MainContainer === undefined) {
      continue;
    }

    const it = MakeIterator(state.MainContainer);
    for (const container of it) {
      const parentId = container.properties.parentId;
      if (parentId === null) {
        container.parent = null;
        continue;
      }
      const parent = findContainerById(state.MainContainer, parentId);
      if (parent === undefined) {
        continue;
      }
      container.parent = parent;
    }

    const selected = findContainerById(state.MainContainer, state.SelectedContainerId);
    if (selected === undefined) {
      state.SelectedContainer = null;
      continue;
    }
    state.SelectedContainer = selected;
  }
}
