<commit_before>/**
 * Copyright 2019 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {
  ChangeDetectorRef,
  Component,
  Inject,
  OnDestroy,
  QueryList,
  ViewChildren,
} from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { DialogService } from '../../services/dialog/dialog.service';
import { Layer } from '../../shared/models/layer.model';
import { Task } from '../../shared/models/task/task.model';
import { Subscription } from 'rxjs';
import { FieldType, Field } from '../../shared/models/task/field.model';
import { StringMap } from '../../shared/models/string-map.model';
import { List } from 'immutable';
import { MarkerColorEvent } from '../edit-style-button/edit-style-button.component';
import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { LayerService } from '../../services/layer/layer.service';
import { TaskFieldEditorComponent } from '../task-field-editor/task-field-editor.component';
import { NavigationService } from '../../services/navigation/navigation.service';

// To make ESLint happy:
/*global alert*/

@Component({
  selector: 'app-layer-dialog',
  templateUrl: './layer-dialog.component.html',
  styleUrls: ['./layer-dialog.component.scss'],
})
export class LayerDialogComponent implements OnDestroy {
  lang: string;
  layer?: Layer;
  layerName!: string;
  surveyId?: string;
  subscription: Subscription = new Subscription();
  fieldTypes = FieldType;
  fields: List<Field>;
  color!: string;
  defaultLayerColor: string;
  task?: Task;
  @ViewChildren(TaskFieldEditorComponent)
  taskFieldEditors?: QueryList<TaskFieldEditorComponent>;
  contributorsCanAddPoints = true;
  contributorsCanAddPolygons = true;

  constructor(
    @Inject(MAT_DIALOG_DATA)
    data: {
      surveyId: string;
      layer?: Layer;
      createLayer: boolean;
    },
    private dialogRef: MatDialogRef<LayerDialogComponent>,
    private dialogService: DialogService,
    private layerService: LayerService,
    private navigationService: NavigationService,
    private readonly cdr: ChangeDetectorRef
  ) {
    this.lang = 'en';
    this.defaultLayerColor = '#ff9131';
    // Disable closing on clicks outside of dialog.
    dialogRef.disableClose = true;
    this.fields = List<Field>();
    this.init(data.surveyId, data.createLayer, data.layer);
    this.dialogRef.keydownEvents().subscribe(event => {
      if (event.key === 'Escape') {
        this.close();
      }
    });
  }

  addQuestion() {
    const newField = this.layerService.createField(
      FieldType.TEXT,
      /* label= */
      '',
      /* required= */
      false,
      /* index= */
      this.fields.size
    );
    this.fields = this.fields.push(newField);
    this.markTaskFieldsTouched();
    this.focusNewQuestion();
  }

  /**
   * Delete the field of a given index.
   *
   * @param index - The index of the field
   * @returns void
   *
   */
  onFieldDelete(index: number) {
    this.dialogService
      .openConfirmationDialog(
        'Warning',
        'Are you sure you wish to delete this question? Any associated data ' +
          'will be lost. This cannot be undone.'
      )
      .afterClosed()
      .subscribe(dialogResult => {
        if (dialogResult) {
          this.fields = this.fields.splice(index, 1);
        }
      });
  }

  init(surveyId: string, createLayer: boolean, layer?: Layer) {
    if (!createLayer && !layer) {
      console.warn('User passed an invalid layer id');
    }
    this.surveyId = surveyId;
    this.layer = layer;
    this.layerName = this.layer?.name?.get(this.lang) || '';
    this.color = this.layer?.color || this.defaultLayerColor;
    if (!layer) {
      this.layer = this.layerService.createNewLayer();
      this.addQuestion();
      return;
    }
    this.contributorsCanAddPoints =
      this.layer?.contributorsCanAdd?.includes('points') || false;
    this.contributorsCanAddPolygons =
      this.layer?.contributorsCanAdd?.includes('polygons') || false;
    this.task = this.layerService.getTask(this.layer);
    if (this.task) {
      this.fields =
        this.task?.fields.toList().sortBy(field => field.index) ||
        List<Field>();
    } else {
      this.addQuestion();
    }
  }

  async onSave() {
    if (!this.surveyId) {
      throw Error('Survey not yet loaded');
    }

    if (!this.taskFieldEditors) {
      return;
    }

    this.markTaskFieldsTouched();

    for (const editor of this.taskFieldEditors) {
      if (editor.taskGroup.invalid || !this.isFieldOptionsValid(editor)) {
        return;
      }
    }
    const fields = this.layerService.convertFieldsListToMap(this.fields);
<<<<<<< HEAD
    const formId = this.form?.id;
    const forms = this.layerService.createForm(formId, fields);
    const allowedLocationOfInterestTypes: string[] = [];
=======
    const taskId = this.task?.id;
    const tasks = this.layerService.createTask(taskId, fields);
    const allowedFeatureTypes: string[] = [];
>>>>>>> 71fbacaa114bf83a170de8aaf5b4fc8b2c111d88
    if (this.contributorsCanAddPoints) {
      allowedLocationOfInterestTypes.push('points');
    }
    if (this.contributorsCanAddPolygons) {
      allowedLocationOfInterestTypes.push('polygons');
    }
    const layer = new Layer(
      this.layer?.id || '',
      /* index */ this.layer?.index || -1,
      this.color,
      // TODO: Make layerName Map
      StringMap({ [this.lang]: this.layerName.trim() }),
<<<<<<< HEAD
      forms,
      allowedLocationOfInterestTypes
=======
      tasks,
      allowedFeatureTypes
>>>>>>> 71fbacaa114bf83a170de8aaf5b4fc8b2c111d88
    );
    this.addOrUpdateLayer(this.surveyId, layer);
  }

  private isFieldOptionsValid(
    taskFieldEditor: TaskFieldEditorComponent
  ): boolean {
    if (!taskFieldEditor.optionEditors) {
      return true;
    }
    for (const editor of taskFieldEditor.optionEditors) {
      if (editor.optionGroup.invalid) {
        return false;
      }
    }
    return true;
  }

  private addOrUpdateLayer(surveyId: string, layer: Layer) {
    // TODO: Inform user layer was saved
    this.layerService
      .addOrUpdateLayer(surveyId, layer)
      .then(() => this.close())
      .catch(err => {
        console.error(err);
        alert('Layer update failed.');
      });
  }

  onCancel(): void {
    if (!this.hasUnsavedChanges()) {<commit_msg>Fix merge<commit_after>/**
 * Copyright 2019 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {
  ChangeDetectorRef,
  Component,
  Inject,
  OnDestroy,
  QueryList,
  ViewChildren,
} from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { DialogService } from '../../services/dialog/dialog.service';
import { Layer } from '../../shared/models/layer.model';
import { Task } from '../../shared/models/task/task.model';
import { Subscription } from 'rxjs';
import { FieldType, Field } from '../../shared/models/task/field.model';
import { StringMap } from '../../shared/models/string-map.model';
import { List } from 'immutable';
import { MarkerColorEvent } from '../edit-style-button/edit-style-button.component';
import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { LayerService } from '../../services/layer/layer.service';
import { TaskFieldEditorComponent } from '../task-field-editor/task-field-editor.component';
import { NavigationService } from '../../services/navigation/navigation.service';

// To make ESLint happy:
/*global alert*/

@Component({
  selector: 'app-layer-dialog',
  templateUrl: './layer-dialog.component.html',
  styleUrls: ['./layer-dialog.component.scss'],
})
export class LayerDialogComponent implements OnDestroy {
  lang: string;
  layer?: Layer;
  layerName!: string;
  surveyId?: string;
  subscription: Subscription = new Subscription();
  fieldTypes = FieldType;
  fields: List<Field>;
  color!: string;
  defaultLayerColor: string;
  task?: Task;
  @ViewChildren(TaskFieldEditorComponent)
  taskFieldEditors?: QueryList<TaskFieldEditorComponent>;
  contributorsCanAddPoints = true;
  contributorsCanAddPolygons = true;

  constructor(
    @Inject(MAT_DIALOG_DATA)
    data: {
      surveyId: string;
      layer?: Layer;
      createLayer: boolean;
    },
    private dialogRef: MatDialogRef<LayerDialogComponent>,
    private dialogService: DialogService,
    private layerService: LayerService,
    private navigationService: NavigationService,
    private readonly cdr: ChangeDetectorRef
  ) {
    this.lang = 'en';
    this.defaultLayerColor = '#ff9131';
    // Disable closing on clicks outside of dialog.
    dialogRef.disableClose = true;
    this.fields = List<Field>();
    this.init(data.surveyId, data.createLayer, data.layer);
    this.dialogRef.keydownEvents().subscribe(event => {
      if (event.key === 'Escape') {
        this.close();
      }
    });
  }

  addQuestion() {
    const newField = this.layerService.createField(
      FieldType.TEXT,
      /* label= */
      '',
      /* required= */
      false,
      /* index= */
      this.fields.size
    );
    this.fields = this.fields.push(newField);
    this.markTaskFieldsTouched();
    this.focusNewQuestion();
  }

  /**
   * Delete the field of a given index.
   *
   * @param index - The index of the field
   * @returns void
   *
   */
  onFieldDelete(index: number) {
    this.dialogService
      .openConfirmationDialog(
        'Warning',
        'Are you sure you wish to delete this question? Any associated data ' +
          'will be lost. This cannot be undone.'
      )
      .afterClosed()
      .subscribe(dialogResult => {
        if (dialogResult) {
          this.fields = this.fields.splice(index, 1);
        }
      });
  }

  init(surveyId: string, createLayer: boolean, layer?: Layer) {
    if (!createLayer && !layer) {
      console.warn('User passed an invalid layer id');
    }
    this.surveyId = surveyId;
    this.layer = layer;
    this.layerName = this.layer?.name?.get(this.lang) || '';
    this.color = this.layer?.color || this.defaultLayerColor;
    if (!layer) {
      this.layer = this.layerService.createNewLayer();
      this.addQuestion();
      return;
    }
    this.contributorsCanAddPoints =
      this.layer?.contributorsCanAdd?.includes('points') || false;
    this.contributorsCanAddPolygons =
      this.layer?.contributorsCanAdd?.includes('polygons') || false;
    this.task = this.layerService.getTask(this.layer);
    if (this.task) {
      this.fields =
        this.task?.fields.toList().sortBy(field => field.index) ||
        List<Field>();
    } else {
      this.addQuestion();
    }
  }

  async onSave() {
    if (!this.surveyId) {
      throw Error('Survey not yet loaded');
    }

    if (!this.taskFieldEditors) {
      return;
    }

    this.markTaskFieldsTouched();

    for (const editor of this.taskFieldEditors) {
      if (editor.taskGroup.invalid || !this.isFieldOptionsValid(editor)) {
        return;
      }
    }
    const fields = this.layerService.convertFieldsListToMap(this.fields);
    const taskId = this.task?.id;
    const tasks = this.layerService.createTask(taskId, fields);
    const allowedLocationOfInterestTypes: string[] = [];
    if (this.contributorsCanAddPoints) {
      allowedLocationOfInterestTypes.push('points');
    }
    if (this.contributorsCanAddPolygons) {
      allowedLocationOfInterestTypes.push('polygons');
    }
    const layer = new Layer(
      this.layer?.id || '',
      /* index */ this.layer?.index || -1,
      this.color,
      // TODO: Make layerName Map
      StringMap({ [this.lang]: this.layerName.trim() }),
      tasks,
      allowedLocationOfInterestTypes
    );
    this.addOrUpdateLayer(this.surveyId, layer);
  }

  private isFieldOptionsValid(
    taskFieldEditor: TaskFieldEditorComponent
  ): boolean {
    if (!taskFieldEditor.optionEditors) {
      return true;
    }
    for (const editor of taskFieldEditor.optionEditors) {
      if (editor.optionGroup.invalid) {
        return false;
      }
    }
    return true;
  }

  private addOrUpdateLayer(surveyId: string, layer: Layer) {
    // TODO: Inform user layer was saved
    this.layerService
      .addOrUpdateLayer(surveyId, layer)
      .then(() => this.close())
      .catch(err => {
        console.error(err);
        alert('Layer update failed.');
      });
  }

  onCancel(): void {
    if (!this.hasUnsavedChanges()) {
{'fraction_non_alphanumeric': 0.1317069520317294, 'fraction_numerical': 0.005088677692134999, 'mean_word_length': 3.0228777844671884, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 37, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7658881', 'n_tokens_mistral': 4026, 'n_tokens_neox': 3947, 'n_words': 927}