<template>
  <div v-resize="onResize">
    <v-overlay
      :value="rendering || !formState.ready || formState.loading"
      z-index="999"
    >
      <v-progress-circular indeterminate size="64"></v-progress-circular>
    </v-overlay>
    <v-row v-show="formState.ready" dense>
      <gz-error :error-box-message="formState.errorBoxMessage"></gz-error>
      <v-col cols="12" class="d-sm-none accent--text">
        <h4>{{ $ay.t("ReportEditorMobileWarning") }}</h4>
      </v-col>
      <v-col cols="12" class="py-2">
        <v-btn-toggle
          v-model="activeTab"
          tile
          color="primary"
          group
          mandatory
          data-cy="viewChange"
          @change="onViewChange"
        >
          <v-btn value="properties">
            {{ $ay.t("ReportEditorProperties") }}
          </v-btn>

          <v-btn value="pdfoptions">
            {{ $ay.t("ReportPdfOptions") }}
          </v-btn>

          <v-btn value="template">
            {{ $ay.t("ReportTemplate") }}
          </v-btn>

          <v-btn value="style">
            CSS
          </v-btn>

          <v-btn value="jsPrerender">
            Prepare
          </v-btn>

          <v-btn value="jsHelpers">
            Helpers
          </v-btn>

          <v-btn v-if="reportData != null" value="rawData">
            {{ $ay.t("ReportEditorData") }}
          </v-btn>
        </v-btn-toggle>
      </v-col>
      <v-col v-show="view == 'edit'" cols="12">
        <div id="editContainer"></div>
      </v-col>

      <v-col v-show="view == 'properties'" cols="12">
        <v-form ref="form">
          <v-row dense>
            <v-col cols="12" sm="6" lg="4" xl="3">
              <v-text-field
                ref="name"
                v-model="obj.name"
                dense
                :readonly="formState.readOnly"
                :label="$ay.t('ReportName')"
                :rules="[form().required(this, 'name')]"
                :error-messages="form().serverErrors(this, 'name')"
                data-cy="name"
                @input="fieldValueChanged('name')"
              ></v-text-field>
            </v-col>

            <v-col cols="12" sm="6" lg="4" xl="3">
              <gz-role-picker
                ref="roles"
                v-model="obj.roles"
                :label="$ay.t('SelectRoles')"
                :readonly="formState.readOnly"
                data-cy="roles"
                :error-messages="form().serverErrors(this, 'roles')"
                limit-selection-to="inside"
                @input="fieldValueChanged('roles')"
              ></gz-role-picker>
            </v-col>

            <v-col v-if="showIncludeDescendants" cols="12" sm="6" lg="4" xl="3">
              <v-checkbox
                ref="includeWoItemDescendants"
                v-model="obj.includeWoItemDescendants"
                dense
                :readonly="formState.readOnly"
                :label="$ay.t('ReportIncludeAllWorkOrderItemDescendants')"
                data-cy="includeWoItemDescendants"
                :error-messages="
                  form().serverErrors(this, 'includeWoItemDescendants')
                "
                @change="fieldValueChanged('includeWoItemDescendants')"
              ></v-checkbox>
            </v-col>
            <v-col cols="12" sm="6" lg="4" xl="3">
              <v-select
                ref="ayaType"
                v-model="obj.aType"
                dense
                :items="selectLists.coreAyaTypes"
                item-text="name"
                item-value="id"
                :readonly="formState.readOnly"
                :label="$ay.t('AyaType')"
                data-cy="ayaType"
                :rules="[form().integerValid(this, 'aType')]"
                :error-messages="form().serverErrors(this, 'aType')"
                @input="fieldValueChanged('aType')"
              ></v-select>
            </v-col>

            <v-col cols="12" sm="6" lg="4" xl="3">
              <v-checkbox
                ref="active"
                v-model="obj.active"
                dense
                :readonly="formState.readOnly"
                :label="$ay.t('Active')"
                data-cy="active"
                :error-messages="form().serverErrors(this, 'active')"
                @change="fieldValueChanged('active')"
              ></v-checkbox>
            </v-col>

            <v-col cols="12">
              <v-textarea
                ref="notes"
                v-model="obj.notes"
                dense
                :readonly="formState.readOnly"
                :label="$ay.t('ReportNotes')"
                :error-messages="form().serverErrors(this, 'notes')"
                data-cy="notes"
                auto-grow
                @input="fieldValueChanged('notes')"
              ></v-textarea>
            </v-col>
          </v-row>
        </v-form>
      </v-col>

      <v-col v-show="view == 'pdfoptions'" cols="12">
        <v-form ref="formPDF">
          <v-row dense>
            <v-col cols="12" sm="6" lg="4" xl="3">
              <v-checkbox
                ref="displayHeaderFooter"
                v-model="obj.displayHeaderFooter"
                dense
                :readonly="formState.readOnly"
                :label="$ay.t('ReportDisplayHeaderFooter')"
                data-cy="displayHeaderFooter"
                :error-messages="
                  form().serverErrors(this, 'displayHeaderFooter')
                "
                @change="fieldValueChanged('displayHeaderFooter', 'formPDF')"
              ></v-checkbox>
            </v-col>

            <v-col cols="12">
              <v-textarea
                ref="headerTemplate"
                v-model="obj.headerTemplate"
                dense
                :readonly="formState.readOnly"
                :label="$ay.t('ReportHeaderTemplate')"
                :error-messages="form().serverErrors(this, 'headerTemplate')"
                data-cy="headerTemplate"
                auto-grow
                @input="fieldValueChanged('headerTemplate', 'formPDF')"
              ></v-textarea>
            </v-col>

            <v-col cols="12">
              <v-textarea
                ref="footerTemplate"
                v-model="obj.footerTemplate"
                dense
                :readonly="formState.readOnly"
                :label="$ay.t('ReportFooterTemplate')"
                :error-messages="form().serverErrors(this, 'footerTemplate')"
                data-cy="footerTemplate"
                auto-grow
                @input="fieldValueChanged('footerTemplate', 'formPDF')"
              ></v-textarea>
            </v-col>

            <v-col cols="12" sm="6" lg="4" xl="3">
              <v-select
                ref="paperFormat"
                v-model="obj.paperFormat"
                dense
                :items="selectLists.PaperFormats"
                item-text="name"
                item-value="id"
                :readonly="formState.readOnly"
                :label="$ay.t('ReportPaperFormat')"
                data-cy="paperFormat"
                :error-messages="form().serverErrors(this, 'paperFormat')"
                @input="fieldValueChanged('paperFormat', 'formPDF')"
              ></v-select>
            </v-col>

            <v-col cols="12" sm="6" lg="4" xl="3">
              <v-checkbox
                ref="landscape"
                v-model="obj.landscape"
                dense
                :readonly="formState.readOnly"
                :label="$ay.t('ReportLandscape')"
                data-cy="landscape"
                :error-messages="form().serverErrors(this, 'landscape')"
                @change="fieldValueChanged('landscape', 'formPDF')"
              ></v-checkbox>
            </v-col>

            <v-col cols="12" sm="6" lg="4" xl="3">
              <v-text-field
                ref="marginOptionsBottom"
                v-model="obj.marginOptionsBottom"
                dense
                :readonly="formState.readOnly"
                :label="$ay.t('ReportMarginOptionsBottom')"
                :error-messages="
                  form().serverErrors(this, 'marginOptionsBottom')
                "
                data-cy="marginOptionsBottom"
                @input="fieldValueChanged('marginOptionsBottom', 'formPDF')"
              ></v-text-field>
            </v-col>

            <v-col cols="12" sm="6" lg="4" xl="3">
              <v-text-field
                ref="marginOptionsLeft"
                v-model="obj.marginOptionsLeft"
                dense
                :readonly="formState.readOnly"
                :label="$ay.t('ReportMarginOptionsLeft')"
                :error-messages="form().serverErrors(this, 'marginOptionsLeft')"
                data-cy="marginOptionsLeft"
                @input="fieldValueChanged('marginOptionsLeft', 'formPDF')"
              ></v-text-field>
            </v-col>

            <v-col cols="12" sm="6" lg="4" xl="3">
              <v-text-field
                ref="marginOptionsRight"
                v-model="obj.marginOptionsRight"
                dense
                :readonly="formState.readOnly"
                :label="$ay.t('ReportMarginOptionsRight')"
                :error-messages="
                  form().serverErrors(this, 'marginOptionsRight')
                "
                data-cy="marginOptionsRight"
                @input="fieldValueChanged('marginOptionsRight', 'formPDF')"
              ></v-text-field>
            </v-col>

            <v-col cols="12" sm="6" lg="4" xl="3">
              <v-text-field
                ref="marginOptionsTop"
                v-model="obj.marginOptionsTop"
                dense
                :readonly="formState.readOnly"
                :label="$ay.t('ReportMarginOptionsTop')"
                :error-messages="form().serverErrors(this, 'marginOptionsTop')"
                data-cy="marginOptionsTop"
                @input="fieldValueChanged('marginOptionsTop', 'formPDF')"
              ></v-text-field>
            </v-col>

            <v-col cols="12" sm="6" lg="4" xl="3">
              <v-checkbox
                ref="preferCSSPageSize"
                v-model="obj.preferCSSPageSize"
                dense
                :readonly="formState.readOnly"
                :label="$ay.t('ReportPreferCSSPageSize')"
                data-cy="preferCSSPageSize"
                :error-messages="form().serverErrors(this, 'preferCSSPageSize')"
                @change="fieldValueChanged('preferCSSPageSize', 'formPDF')"
              ></v-checkbox>
            </v-col>

            <v-col cols="12" sm="6" lg="4" xl="3">
              <v-checkbox
                ref="printBackground"
                v-model="obj.printBackground"
                dense
                :readonly="formState.readOnly"
                :label="$ay.t('ReportPrintBackground')"
                data-cy="printBackground"
                :error-messages="form().serverErrors(this, 'printBackground')"
                @change="fieldValueChanged('printBackground', 'formPDF')"
              ></v-checkbox>
            </v-col>

            <v-col cols="12" sm="6" lg="4" xl="3">
              <gz-decimal
                ref="scale"
                v-model="obj.scale"
                :readonly="formState.readOnly"
                :label="$ay.t('ReportScale')"
                :rules="[
                  form().decimalValid(this, 'scale'),
                  form().required(this, 'scale'),
                  form().minValueValid(this, 'scale', 0.1),
                  form().maxValueValid(this, 'scale', 2)
                ]"
                :error-messages="form().serverErrors(this, 'scale')"
                data-cy="scale"
                @input="fieldValueChanged('scale', 'formPDF')"
              ></gz-decimal>
            </v-col>
          </v-row>
        </v-form>
      </v-col>
    </v-row>
    <gz-report-selector ref="reportSelector"></gz-report-selector>
  </div>
</template>
<script>
import * as monaco from "monaco-editor";
//Monaco editor info page with links
//https://microsoft.github.io/monaco-editor/
const FORM_KEY = "ay-report-edit";
let JUST_DELETED = false;
let editor = null;
export default {
  data() {
    return {
      reportData: null,
      editAreaHeight: 300,
      activeTab: "properties",
      lastTab: "properties",
      view: "edit",
      editData: {
        template: {
          model: null,
          state: null
        },
        style: {
          model: null,
          state: null
        },
        jsPrerender: {
          model: null,
          state: null
        },
        jsHelpers: {
          model: null,
          state: null
        },
        rawData: {
          model: null,
          state: null
        }
      },
      obj: {
        id: 0,
        concurrency: 0,
        name: "report",
        includeWoItemDescendants: false,
        active: true,
        notes: "",
        roles: 124927, //all except customers
        aType: 0,
        template: `<html>

<body>
 {{#each ayReportData}}

 <!-- Your template here see Help for details -->
 <h2>{{ Name }}</h2>
 
 {{/each}}
</body>

</html>`,
        style: "",
        jsPrerender: `async function ayPrepareData(ayData){         
    return ayData;
}`,
        jsHelpers: "",
        renderType: 0,
        headerTemplate: null,
        footerTemplate: null,
        displayHeaderFooter: false,
        paperFormat: 10,
        landscape: false,
        marginOptionsBottom: null,
        marginOptionsLeft: null,
        marginOptionsRight: null,
        marginOptionsTop: null,
        pageRanges: null,
        preferCSSPageSize: false,
        printBackground: false,
        scale: 1 //Defaults to 1. Scale amount must be between 0.1 and 2.
      },
      formState: {
        ready: false,
        dirty: false,
        valid: true,
        readOnly: false,
        loading: true,
        errorBoxMessage: null,
        appError: null,
        serverError: {}
      },
      rights: window.$gz.role.defaultRightsObject(),
      ayaType: window.$gz.type.Report,
      rendering: false,
      selectLists: {
        PaperFormats: [
          { name: "-", id: 0 },
          { name: "A0", id: 1 },
          { name: "A1", id: 2 },
          { name: "A2", id: 3 },
          { name: "A3", id: 4 },
          { name: "A4", id: 5 },
          { name: "A5", id: 6 },
          { name: "A6", id: 7 },
          { name: "Ledger", id: 8 },
          { name: "Legal", id: 9 },
          { name: "Letter", id: 10 },
          { name: "Tabloid", id: 11 }
        ],
        coreAyaTypes: []
      }
    };
  },
  computed: {
    showIncludeDescendants: function() {
      switch (this.obj.aType) {
        case window.$gz.type.WorkOrderItem:
        case window.$gz.type.WorkOrderItemExpense:
        case window.$gz.type.WorkOrderItemLabor:
        case window.$gz.type.WorkOrderItemLoan:
        case window.$gz.type.WorkOrderItemPart:
        case window.$gz.type.WorkOrderItemPartRequest:
        case window.$gz.type.WorkOrderItemScheduledUser:
        case window.$gz.type.WorkOrderItemTask:
        case window.$gz.type.WorkOrderItemTravel:
        case window.$gz.type.WorkOrderItemUnit:
        case window.$gz.type.WorkOrderItemOutsideService:
        case window.$gz.type.QuoteItem:
        case window.$gz.type.QuoteItemExpense:
        case window.$gz.type.QuoteItemLabor:
        case window.$gz.type.QuoteItemLoan:
        case window.$gz.type.QuoteItemPart:
        case window.$gz.type.QuoteItemScheduledUser:
        case window.$gz.type.QuoteItemTask:
        case window.$gz.type.QuoteItemTravel:
        case window.$gz.type.QuoteItemUnit:
        case window.$gz.type.QuoteItemOutsideService:
        case window.$gz.type.PMItem:
        case window.$gz.type.PMItemExpense:
        case window.$gz.type.PMItemLabor:
        case window.$gz.type.PMItemLoan:
        case window.$gz.type.PMItemPart:
        case window.$gz.type.PMItemScheduledUser:
        case window.$gz.type.PMItemTask:
        case window.$gz.type.PMItemTravel:
        case window.$gz.type.PMItemUnit:
        case window.$gz.type.PMItemOutsideService:
          return true;
        default:
          return false;
      }
    }
  },
  watch: {
    formState: {
      handler: function(val) {
        if (this.formState.loading) {
          return;
        }

        if (val.dirty && val.valid && !val.readOnly) {
          window.$gz.eventBus.$emit("menu-enable-item", FORM_KEY + ":save");
        } else {
          window.$gz.eventBus.$emit("menu-disable-item", FORM_KEY + ":save");
        }
        //enable / disable RENDER button if it can be rendered (has data)
        if (this.reportData != null) {
          if (!val.dirty) {
            //not disabling if invalid because that sometimes just means it needs to be re-run due to server error
            //maybe more of an issue during development but for now it's annoying
            window.$gz.eventBus.$emit("menu-enable-item", FORM_KEY + ":render");
          } else {
            window.$gz.eventBus.$emit(
              "menu-disable-item",
              FORM_KEY + ":render"
            );
          }
        }
        if (!val.dirty && val.valid && !val.readOnly) {
          window.$gz.eventBus.$emit(
            "menu-enable-item",
            FORM_KEY + ":duplicate"
          );
          window.$gz.eventBus.$emit("menu-enable-item", FORM_KEY + ":new");
        } else {
          window.$gz.eventBus.$emit(
            "menu-disable-item",
            FORM_KEY + ":duplicate"
          );
          window.$gz.eventBus.$emit("menu-disable-item", FORM_KEY + ":new");
        }
      },
      deep: true
    }
  },
  async created() {
    const vm = this;
    try {
      await initForm(vm);
      vm.rights = window.$gz.role.getRights(window.$gz.type.Report);
      vm.formState.readOnly = !vm.rights.change;
      window.$gz.eventBus.$on("menu-click", clickHandler);
      let setDirty = false;
      //id 0 means create or duplicate to new
      if (vm.$route.params.recordid != 0) {
        //is there already an obj from a prior operation?
        if (this.$route.params.obj) {
          //yes, no need to fetch it
          this.obj = this.$route.params.obj;
        } else {
          //FETCH EXISTING
          await vm.getDataFromApi(vm.$route.params.recordid);
        }
      } else {
        //Might be a duplicate and contain another record
        if (this.$route.params.obj) {
          this.obj = this.$route.params.obj;
          this.obj.concurrency = undefined;
          this.obj.id = 0;
          this.obj.name = `${this.obj.name} - ${window.$gz.translation.get(
            "Copy"
          )}`;
          setDirty = true;
        } else {
          //brand new report, not a duplicate or previous data
          //Not valid without report data options
          if (vm.$route.params.reportDataOptions == null) {
            window.$gz.eventBus.$emit(
              "notify-error",
              "ay-report-edit::created - missing report data options cannot proceed"
            );
            //
            vm.$router.go(-1);
            return;
          }
          vm.obj.aType = vm.$route.params.reportDataOptions.AType;
          //trigger rule breaking / validation
          this.$nextTick(() => {
            this.obj.name = null;
            this.fieldValueChanged(`name`);
          });
        }
      }
      //in any case we need the sample data so fetch it here after the setup for new or existing or duplicate is completed above
      await fetchReportData(vm);
      //---------------
      //setup the editor and models
      monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
        noSemanticValidation: false,
        noSyntaxValidation: false,
        noSuggestionDiagnostics: false,
        diagnosticCodesToIgnore: [2393, 7044] //2393 hides a weird error about ayPrepareData being declared more than once which I think is more related to it being barebones in there, 7044 hides a useless prompt about ayData parameter not being strongly typed
      });
      monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
        target: monaco.languages.typescript.ScriptTarget.ES2020,
        allowNonTsExtensions: true
      });

      //https://github.com/handlebars-lang/handlebars.js/blob/master/types/index.d.ts
      monaco.languages.typescript.javascriptDefaults.addExtraLib(
        [
          "export declare global function ayGetTranslations(keys:Array): promise<void>;",
          "export declare global function ayPrepareData(ayData:object): ayPrepareData: object;",
          "export declare global function ayGroupByKey(reportDataArray:Array, groupByKeyName:string): Array",
          "export declare global function ayGetFromAPI(route:string,token?:string): promise<object>;",
          "export declare global function ayPostToAPI(route:string,data?:object,token?:string): promise<result:object>;",
          "export declare global function ayPutToAPI(route:string,data?:object,token?:string): promise<result:object>;",
          "export declare global function ayGetTranslations(keys:Array): promise<void>;",
          "export declare global declare namespace AYMETA {export const ayClientMetaData; export const ayServerMetaData; export const ayReportMetaData; }",
          `export declare global declare namespace Handlebars {export function registerHelper(name: string, fn: HelperDelegate): void; export function registerHelper(name: HelperDeclareSpec): void;
          export function SafeString(value:string):string; export function escapeExpression(value:string):string; 
          export function createFrame(object: any): any;
          export function log(level: number, obj: any): void;
          export namespace Utils { 
            export function escapeExpression(str: string): string; export function createFrame(object: any): any; export function blockParams(obj: any[], ids: any[]): any[];
            export function isEmpty(obj: any) : boolean; export function extend(obj: any, ...source: any[]): any; export function toString(obj: any): string; export function isArray(obj: any): boolean;
            export function isFunction(obj: any): boolean;
            }
          export interface Logger {
              DEBUG: number;
              INFO: number;
              WARN: number;
              ERROR: number;
              level: number;

              methodMap: { [level: number]: string };

              log(level: number, obj: string): void;
            }
          }`
        ].join("\n"),
        "filename/facts.d.ts"
      );

      //Create editor models for each type of report element that can be edited

      vm.editData.template.model = monaco.editor.createModel(
        vm.obj.template,
        "html"
      );

      vm.editData.style.model = monaco.editor.createModel(vm.obj.style, "css");
      vm.editData.jsPrerender.model = monaco.editor.createModel(
        vm.obj.jsPrerender,
        "javascript"
      );

      if (vm.reportData != null) {
        vm.editData.rawData.model = monaco.editor.createModel(
          JSON.stringify(vm.reportData, null, "\t"),
          "json"
        );
      }

      vm.editData.jsHelpers.model = monaco.editor.createModel(
        vm.obj.jsHelpers,
        "javascript"
      );

      //Create the editor itself
      editor = monaco.editor.create(document.getElementById("editContainer"), {
        model: vm.editData.template.model
      });

      //save the initial state because we're going to move away from it immediately
      vm.editData.template.state = editor.saveViewState();

      //change subscription
      editor.onDidChangeModelContent(() => {
        const editorValue = editor.getValue();
        switch (vm.activeTab) {
          case "template":
            vm.obj.template = editorValue;
            break;
          case "style":
            vm.obj.style = editorValue;
            break;
          case "jsPrerender":
            vm.obj.jsPrerender = editorValue;
            break;
          case "jsHelpers":
            vm.obj.jsHelpers = editorValue;
            break;
        }
        vm.formState.dirty = true;
        //trigger normal form processing when edit happens
        //without this you can never save after a server error in the script returns
        //pretend the name field changed even though it didn't
        vm.fieldValueChanged("name");
      });
      vm.view = "properties";
      window.$gz.form.setFormState({
        vm: vm,
        loading: false,
        dirty: setDirty,
        valid: true
      });
      generateMenu(vm);
    } catch (error) {
      window.$gz.errorHandler.handleFormError(error, vm);
    } finally {
      vm.formState.ready = true;
    }
  },
  async beforeRouteLeave(to, from, next) {
    if (!this.formState.dirty || JUST_DELETED) {
      next();
      return;
    }
    if ((await window.$gz.dialog.confirmLeaveUnsaved()) === true) {
      next();
    } else {
      next(false);
    }
  },
  beforeDestroy() {
    window.$gz.eventBus.$off("menu-click", clickHandler);
    //dispose all models and editor
    editor && editor.dispose();
  },
  methods: {
    onViewChange() {
      const vm = this;
      const currentState = editor.saveViewState();
      //save edit state before switching
      switch (vm.lastTab) {
        case "properties":
          //no state to save here
          break;
        case "pdfoptions":
          //no state to save here
          break;
        case "template":
          vm.editData.template.state = currentState;
          break;
        case "style":
          vm.editData.style.state = currentState;
          break;
        case "jsPrerender":
          vm.editData.jsPrerender.state = currentState;
          break;
        case "jsHelpers":
          vm.editData.jsHelpers.state = currentState;
          break;
        case "rawData":
          vm.editData.rawData.state = currentState;
          break;
      }
      vm.lastTab = vm.activeTab;
      switch (vm.activeTab) {
        case "properties":
          vm.view = "properties";
          return;
        case "pdfoptions":
          vm.view = "pdfoptions";
          return;
        case "template":
          editor.setModel(vm.editData.template.model);
          editor.restoreViewState(vm.editData.template.state);
          editor.updateOptions({ readOnly: false });
          vm.view = "edit";
          break;
        case "style":
          editor.setModel(vm.editData.style.model);
          editor.restoreViewState(vm.editData.style.state);
          editor.updateOptions({ readOnly: false });
          vm.view = "edit";
          break;
        case "jsPrerender":
          editor.setModel(vm.editData.jsPrerender.model);
          editor.restoreViewState(vm.editData.jsPrerender.state);
          editor.updateOptions({ readOnly: false });
          vm.view = "edit";
          break;
        case "jsHelpers":
          editor.setModel(vm.editData.jsHelpers.model);
          editor.restoreViewState(vm.editData.jsHelpers.state);
          editor.updateOptions({ readOnly: false });
          vm.view = "edit";
          break;
        case "rawData":
          editor.setModel(vm.editData.rawData.model);
          editor.restoreViewState(vm.editData.rawData.state);
          editor.updateOptions({ readOnly: true });
          vm.view = "edit";
          break;
      }
      vm.$nextTick(() => {
        editor.layout();
        editor.focus();
      });
    },
    onResize() {
      this.$nextTick(() => {
        //resize related links:
        //https://github.com/Microsoft/monaco-editor/issues/28
        const el = document.getElementById("editContainer");
        el.style = `width:100%;height:${window.innerHeight * 0.77}px`;
        if (editor != null) {
          editor.layout();
        }
      });
    },
    canSave: function() {
      return this.formState.valid && this.formState.dirty;
    },
    canDuplicate: function() {
      return this.formState.valid && !this.formState.dirty;
    },
    ayaTypes: function() {
      return window.$gz.type;
    },
    form() {
      return window.$gz.form;
    },
    fieldValueChanged(ref, formReference) {
      if (
        this.formState.ready &&
        !this.formState.loading &&
        !this.formState.readOnly
      ) {
        window.$gz.form.fieldValueChanged(this, ref, formReference);
      }
    },
    async getDataFromApi(recordId) {
      const vm = this;
      window.$gz.form.setFormState({
        vm: vm,
        loading: true
      });
      if (!recordId) {
        throw new Error(FORM_KEY + "::getDataFromApi -> Missing recordID!");
      }
      try {
        window.$gz.form.deleteAllErrorBoxErrors(vm);
        const res = await window.$gz.api.get("report/" + recordId);
        if (res.error) {
          if (res.error.code == "2010") {
            window.$gz.form.handleObjectNotFound(vm);
          }
          vm.formState.serverError = res.error;
          window.$gz.form.setErrorBoxErrors(vm);
        } else {
          vm.obj = res.data;
          generateMenu(vm);
          window.$gz.form.setFormState({
            vm: vm,
            dirty: false,
            valid: true,
            loading: false
          });
        }
      } catch (error) {
        window.$gz.errorHandler.handleFormError(error, vm);
      } finally {
        window.$gz.form.setFormState({
          vm: vm,
          loading: false
        });
      }
    },
    async submit() {
      const vm = this;
      if (vm.canSave == false) {
        return;
      }

      try {
        window.$gz.form.setFormState({
          vm: vm,
          loading: true
        });
        window.$gz.form.deleteAllErrorBoxErrors(vm);
        const res = await window.$gz.api.upsert("report/", vm.obj);
        if (res.error) {
          vm.formState.serverError = res.error;
          window.$gz.form.setErrorBoxErrors(vm);
        } else {
          if (res.data.id) {
            //POST
            vm.obj = res.data;
            this.$router.replace({
              name: "ay-report-edit",
              params: {
                recordid: res.data.id,
                obj: res.data,
                reportDataOptions: vm.$route.params.reportDataOptions
              }
            });
          } else {
            //PUT
            vm.obj.concurrency = res.data.concurrency;
          }
          window.$gz.form.setFormState({
            vm: vm,
            dirty: false,
            valid: true
          });
        }
      } catch (ex) {
        window.$gz.errorHandler.handleFormError(ex, vm);
      } finally {
        window.$gz.form.setFormState({
          vm: vm,
          loading: false
        });
      }
    },
    async remove() {
      try {
        const dialogResult = await window.$gz.dialog.confirmDelete();
        if (dialogResult != true) {
          return;
        }
        window.$gz.form.setFormState({
          vm: this,
          loading: true
        });
        if (this.$route.params.recordid == 0) {
          JUST_DELETED = true;
          this.$router.go(-1);
        } else {
          window.$gz.form.deleteAllErrorBoxErrors(this);
          const res = await window.$gz.api.remove(
            "report/" + this.$route.params.recordid
          );
          if (res.error) {
            this.formState.serverError = res.error;
            window.$gz.form.setErrorBoxErrors(this);
          } else {
            JUST_DELETED = true;
            this.$router.go(-1);
          }
        }
      } catch (error) {
        window.$gz.errorHandler.handleFormError(error, this);
      } finally {
        window.$gz.form.setFormState({
          vm: this,
          loading: false
        });
      }
    },
    duplicate() {
      this.$router.push({
        name: "ay-report-edit",
        params: {
          recordid: 0,
          obj: this.obj,
          reportDataOptions: this.$route.params.reportDataOptions
        }
      });
    },
    async render() {
      const vm = this;
      if (vm.$route.params.recordid == 0) {
        return;
      }
      const reportDataOptions = vm.$route.params.reportDataOptions;
      if (!reportDataOptions) {
        throw new Error(
          "Missing report data: to view report must come here from an object edit form or list so data can be provided for viewing the report"
        );
      }
      window.$gz.form.deleteAllErrorBoxErrors(vm);
      reportDataOptions.ReportId = vm.obj.id;
      //Meta data from client for use by report script
      reportDataOptions.ClientMeta = window.$gz.api.reportClientMetaData();
      try {
        await vm.$refs.reportSelector.open(reportDataOptions, vm.obj.id);
      } catch (ex) {
        vm.rendering = false;
        window.$gz.errorHandler.handleFormError(ex, vm);
      }
    }
  }
};

/////////////////////////////
//
//
async function clickHandler(menuItem) {
  if (!menuItem) {
    return;
  }
  const m = window.$gz.menu.parseMenuItem(menuItem);
  if (m.owner == FORM_KEY && !m.disabled) {
    switch (m.key) {
      case "export":
        //ignore download link let it go
        break;
      case "save":
        m.vm.submit();
        break;
      case "delete":
        m.vm.remove();
        break;
      case "new":
        m.vm.$router.push({
          name: "ay-report-edit",
          params: { recordid: 0 }
        });
        break;
      case "duplicate":
        m.vm.duplicate();
        break;
      case "render":
        m.vm.render();
        break;

      default:
        window.$gz.eventBus.$emit(
          "notify-warning",
          FORM_KEY + "::context click: [" + m.key + "]"
        );
    }
  }
}

//////////////////////
//
//
function generateMenu(vm) {
  const menuOptions = {
    isMain: false,
    readOnly: vm.formState.readOnly,
    icon: "$ayiDraftingCompass",
    title: "ReportDesignReport",
    helpUrl: "ay-report-edit",
    formData: {
      ayaType: window.$gz.type.Report,
      recordId: vm.$route.params.recordid,
      recordName: vm.obj.name
    },
    menuItems: []
  };

  if (vm.rights.change) {
    menuOptions.menuItems.push({
      title: "Save",
      icon: "$ayiSave",
      surface: true,
      key: FORM_KEY + ":save",
      vm: vm
    });
  }
  if (vm.reportData != null) {
    menuOptions.menuItems.push({
      title: "Report",
      icon: "$ayiPrint",
      key: FORM_KEY + ":render",
      surface: true,
      vm: vm
    });
  }

  if (vm.rights.change) {
    menuOptions.menuItems.push({
      title: "New",
      icon: "$ayiPlus",
      key: FORM_KEY + ":new",
      vm: vm
    });
  }

  if (vm.rights.change && vm.$route.params.recordid != 0) {
    menuOptions.menuItems.push({
      title: "Duplicate",
      icon: "$ayiClone",
      key: FORM_KEY + ":duplicate",
      vm: vm
    });
  }

  if (vm.rights.delete && vm.$route.params.recordid != 0) {
    menuOptions.menuItems.push({
      title: "Delete",
      icon: "$ayiTrashAlt",
      surface: false,
      key: FORM_KEY + ":delete",
      vm: vm
    });
  }
  menuOptions.menuItems.push({ divider: true, inset: false });

  //EXPORT
  if (vm.$route.params.recordid != 0) {
    const href = window.$gz.api.genericDownloadUrl(
      "report/export/" + vm.$route.params.recordid
    );

    menuOptions.menuItems.push({
      title: "Export",
      icon: "$ayiFileDownload",
      href: href,
      target: "_blank",
      key: FORM_KEY + ":export",
      vm: vm
    });
  }
  menuOptions.menuItems.push({ divider: true, inset: false });

  window.$gz.eventBus.$emit("menu-change", menuOptions);
}
/////////////////////////////////
//
//
async function initForm(vm) {
  await fetchTranslatedText();
  await populateSelectionLists(vm);
}

//////////////////////////////////////////////////////////
//
// Ensures UI translated text is available
//
async function fetchTranslatedText() {
  await window.$gz.translation.cacheTranslations([
    "ReportDesignReport",
    "ReportName",
    "ReportEditorProperties",
    "ReportEditorData",
    "ReportEditorMobileWarning",
    "ReportNotes",
    "ReportTemplate",
    "SelectRoles",
    "ReportPdfOptions",
    "ReportDisplayHeaderFooter",
    "ReportHeaderTemplate",
    "ReportFooterTemplate",
    "ReportPaperFormat",
    "ReportLandscape",
    "ReportMarginOptionsBottom",
    "ReportMarginOptionsLeft",
    "ReportMarginOptionsRight",
    "ReportMarginOptionsTop",
    "ReportPrintBackground",
    "ReportScale",
    "ReportPreferCSSPageSize",
    "ReportIncludeAllWorkOrderItemDescendants"
  ]);
}

////////////////////
//
async function fetchReportData(vm) {
  const reportDataOptions = vm.$route.params.reportDataOptions;
  if (!reportDataOptions) {
    vm.reportData = null;
    return;
  }
  if (reportDataOptions.AType == null) {
    throw new Error(
      "ay-report-edit:fetchReportData - route parameter AType is missing or empty, unable to init report designer"
    );
  }
  vm.obj.aType = reportDataOptions.AType;
  //handle includeWoItemDescendants by adding it's value to the request for report data
  reportDataOptions["includeWoItemDescendants"] =
    vm.obj.includeWoItemDescendants;
  const res = await window.$gz.api.upsert("report/data", reportDataOptions);
  if (!Object.prototype.hasOwnProperty.call(res, "data")) {
    return Promise.reject(res);
  } else {
    vm.reportData = res.data;
  }
}

//////////////////////
//
//
async function populateSelectionLists(vm) {
  await window.$gz.enums.fetchEnumList("reportable");
  vm.selectLists.coreAyaTypes = window.$gz.enums.getSelectionList("reportable");
}
</script>
