import { Component } from "vue";
import { ZeroEditorField, ZeroEditorDisplay } from "zero/schemas";
import { Zero } from "../core";
import { ZeroEditor } from "./editor";
import { createBlueprintConfig } from "./editor-blueprint";
import { ZeroEditorCanvasBase, ZeroEditorTab } from "./editor-canvas";
import { ZeroEditorFieldConfiguration, ZeroEditorFieldFilterPreview } from "./editor-field";
import { localize } from '../services/localization';


let appliedExtensionsTo: string[] = [];

export interface ZeroCompiledEditor
{
  alias: string;
  blueprint: any;
  display: ZeroEditorDisplay;
  tabs: ZeroCompiledEditorTab[];
  origin: ZeroEditor;
}


export interface ZeroCompiledEditorTab
{
  alias: string;
  name: string;
  sort: number;
  class: string | null;
  count: (model: any) => number | null;
  disabled: (model: any) => boolean;
  hidden: (model: any) => boolean;
  fieldsets: ZeroCompiledEditorFieldset[];
  component: Component | null;
}


export interface ZeroCompiledEditorFieldset
{
  fields: ZeroCompiledEditorField[];
  hasColumns: boolean;
}


export interface ZeroCompiledEditorChangeEvent
{
  value: any;
  oldValue: any;
  model: any;
  component: Component;
}


export interface ZeroCompiledEditorField
{
  path: string;
  configuration: ZeroEditorFieldConfiguration;
  fieldType: string;
  options: any | null;
  component: Component;

  optional: (model: any) => boolean;
  readonly: (model: any) => boolean;
  hidden: (model: any) => boolean;
  label: string;
  hideLabel: boolean;
  description: string | null;
  helpText: string | null;
  classes: string | null;
  horizontal: boolean;
  sort: number;
  columns: number;
  preview?: ZeroEditorFieldFilterPreview;
  onChange: (event: ZeroCompiledEditorChangeEvent) => void;
}


export function compileField(zero: Zero, editor: ZeroEditor, field: ZeroEditorField): ZeroCompiledEditorField | undefined
{
  const component = field.customComponent || zero.getFieldTypeComponent(field.fieldType);

  if (!component)
  {
    return undefined;
  }

  let model = {
    configuration: field.configuration,
    fieldType: field.fieldType,
    options: field.options,
    path: field.path,
    component,

    optional(model: any)
    {
      if (typeof field.configuration.optional === 'boolean')
      {
        return field.configuration.optional;
      }
      if (typeof field.configuration.optional === 'function')
      {
        return field.configuration.optional(model);
      }
      return false;
    },

    readonly(model: any)
    {
      if (typeof field.configuration.readonly === 'boolean')
      {
        return field.configuration.readonly;
      }
      if (typeof field.configuration.readonly === 'function')
      {
        return field.configuration.readonly(model);
      }
      return false;
    },

    hidden(model: any)
    {
      if (typeof field.configuration.hidden === 'boolean')
      {
        return field.configuration.hidden;
      }
      if (typeof field.configuration.hidden === 'function')
      {
        return field.configuration.hidden(model);
      }
      return false;
    },

    label: editor.onLabelCreate(field),  
    description: localize(editor.onDescriptionCreate(field), { hideEmpty: true }),
    hideLabel: field.configuration.hideLabel,
    helpText: field.configuration.helpText,
    classes: field.configuration.classes,
    horizontal: field.configuration.horizontal,
    sort: field.configuration.sort,
    columns: 12,
    preview: field.configuration.preview ? {
      icon: field.configuration.preview.icon || 'fth-square',
      selected: field.configuration.preview.selected || (x => !!x),
      value: field.configuration.preview.value || (x => x ? x : null)
    } : null,

    onChange(event: ZeroCompiledEditorChangeEvent)
    {
      (field.configuration.changeHandlers || []).forEach(handler =>
      {
        handler(event);
      });
    }

  } as ZeroCompiledEditorField;

  return model;
}


export function compileEditor(zero: Zero, editor: ZeroEditor): ZeroCompiledEditor | null
{
  if (!editor)
  {
    return null;
  }

  if (appliedExtensionsTo.indexOf(editor.alias) < 0)
  {
    appliedExtensionsTo.push(editor.alias);

    let extensions = zero.getSchemaExtensions(editor.alias);

    extensions.forEach(extension =>
    {
      extension.extension(editor);
    });
  }

  let model = {
    alias: editor.alias,
    blueprint: null,
    display: editor.display,
    tabs: [],
    origin: editor,
  } as ZeroCompiledEditor;

  let tabs = [...editor.tabs.sort((a, b) => a.sort - b.sort)];

  // add default tab if necessary
  if (!tabs.length && (editor.fields.length || editor.fieldsets.length))
  {
    let tab = new ZeroEditorTab('content', '@ui.tab_content');
    tab.fieldsets.push(new ZeroEditorCanvasBase());
    tabs.push(tab);
  }

  tabs.forEach((editorTab, tabIndex) =>
  {
    let tab = {
      name: editorTab.name,
      sort: editorTab.sort,
      alias: editorTab.alias,
      class: null,
      count: (model: any) => null,
      disabled: (model: any) => false,
      hidden: (model: any) => false,
      fieldsets: [],
      component: null
    } as ZeroCompiledEditorTab;

    let fieldsets = [];

    // add fieldsets (which are not part of a tab) to the first tab
    if (tabIndex == 0 && editor.fieldsets.length)
    {
      fieldsets.push(...editor.fieldsets);
    }

    // push registered fieldsets
    fieldsets.push(...editorTab.fieldsets.sort((a, b) => a.sort - b.sort));

    // add default fieldset if none are defined
    if (!fieldsets.length)
    {
      const fakeFieldset = new ZeroEditorCanvasBase();
      fakeFieldset.isFake = false;
      fieldsets.push(fakeFieldset);
    }

    fieldsets.forEach((editorFieldset, fieldsetIndex) =>
    {
      let fieldset = {
        sort: editorFieldset.sort,
        fields: [],
        hasColumns: !editorFieldset.isFake
      } as ZeroCompiledEditorFieldset;

      let fields = [];

      // add fields (which are not part of a fieldset) to the first fieldset
      if (fieldsetIndex == fieldsets.length - 1)
      {
        if (editor.fields.length)
        {
          fields.push(...editor.fields);
        }
        if (editorTab.fields.length)
        {
          fields.push(...editorTab.fields);
        }
      }

      // push registered fields
      // @ts-ignore
      fields.push(...editorFieldset.fields.sort((a, b) => a.configuration.sort - b.configuration.sort));

      fields.forEach(editorField =>
      {
        const field = compileField(zero, editor, editorField);

        if (field == null)
        {
          console.error('Could not find registered field type [' + editorField.fieldType + ']');
        }
        else
        {
          fieldset.fields.push(field);
        }
      });


      if (fieldset.fields.length)
      {
        tab.fieldsets.push(fieldset);
      }
    });


    if (tab.fieldsets.length)
    {
      model.tabs.push(tab);
    }
  });


  model.blueprint = createBlueprintConfig(model.alias, model);

  return model;
}