import {
  AfterViewChecked,
  Component,
  ChangeDetectorRef,
  ElementRef,
  EventEmitter,
  Inject,
  Input,
  OnDestroy,
  OnInit,
  Output,
  ViewChild,
  AfterViewInit,
  Renderer2
} from '@angular/core';
import { TranslateService } from "@ngx-translate/core";
import AdvancedSearchQuery from "advanced-search-query";
import * as _ from 'lodash';

import { Override, <% if ( !frontPaging ) { %>Pageable, <% } %>MagTableColumn, MagTableColumnBuilder } from 'ng-stalk/core/model';
import {
  CrudAction,
  ActionButton,
  SelectableZorroDataSource
} from 'ng-stalk/core/crud';
import { ContentAreaImpl2 } from 'ng-stalk/modal-template'
import {
  MagDialogService,
  MagSpaceService,
  MagSpaceInfo,
  MagSpaceStatus,
} from 'ng-stalk/core/service';

import { NzSafeAny } from "ng-zorro-antd/core/types";
import { NzModalRef } from 'ng-zorro-antd/modal';
import { NzResizeEvent } from "ng-zorro-antd/resizable";
import {BehaviorSubject, Subject} from 'rxjs';
import {distinctUntilChanged, takeUntil} from 'rxjs/operators';

import { <%= classify(name) %>Service } from '../service/<%= dasherize(name) %>-service/<%= dasherize(name) %>.service';
import { <%= classify(name) %>Dto } from '../sample/<%= dasherize(name) %>-dto';
import { <%= classify(pluralName) %>DialogOption } from '../<%= dasherize(pluralName) %>-dialog-option';
import { <%= classify(pluralName) %>DialogResult } from '../<%= dasherize(pluralName) %>-dialog-result';
import { <%= classify(pluralName) %>DataSource } from './<%= dasherize(pluralName) %>-data-source';
import { <%= classify(pluralName) %>DialogSearchModel } from '../<%= dasherize(pluralName) %>-dialog-search-form/<%= dasherize(pluralName) %>-dialog-search-model';
import { Constants } from 'src/app/service/constants/constants.service';
<% if ( !frontPaging ) { %>
import {Get<%= classify(name) %>PageRequest} from "../../../service/<%= dasherize(name) %>-service/get-<%= dasherize(name) %>-page-request";
<% } %>
import { Helpers } from '../sample/helpers';

@Component({
  selector: 'app-<%= dasherize(pluralName) %>-dialog-page',
  templateUrl: './<%= dasherize(pluralName) %>-dialog-page.component.html',
  styleUrls: ['./<%= dasherize(pluralName) %>-dialog-page.component.less']
})
export class <%= classify(pluralName) %>DialogPageComponent implements OnInit, OnDestroy, AfterViewChecked {

  // --------------------------------------------------------------------------
  // MAG: ContentAreaImpl 모달 공통 속성
  // --------------------------------------------------------------------------

  @Input()
  option: <%= classify(pluralName) %>DialogOption;
  dialogTitle = '<%= displayName %> 선택하기';
  actionButtons: BehaviorSubject<ActionButton[]> = new BehaviorSubject<ActionButton[]>([]);

  // --------------------------------------------------------------------------
  // 레이아웃 속성
  // --------------------------------------------------------------------------
  dataSource: SelectableZorroDataSource<<%= classify(name) %>Dto, <%= classify(pluralName) %>DialogSearchModel>;

  // --------------------------------------------------------------------------
  // MAG: ContentAreaImpl 지원 영역 : 키워드 & 테이블 표현 관련 속성
  // --------------------------------------------------------------------------

  columns: MagTableColumn[] = [];
  <% if ( sideArea ) { %>
  col = 4; // 현재 left colspan
  id = -1;
  <% } %>
  loading = false;
  @ViewChild('modalBodyWrapper', {static: true, read: ElementRef}) contentAreaElement: ElementRef;
  sidebarScrollSize = 0;
  tableScrollSize = 0;

  // --------------------------------------------------------------------------
  // 검색, 페이징 속성
  // --------------------------------------------------------------------------
  searchModel: <%= classify(pluralName) %>DialogSearchModel = {};

  <% if ( !frontPaging ) { %>
  /**
   * 요청하는 페이징 정보
   */
  pageableParam: Pageable = {
    page: 0,
    pageSize: 10,
  };

  /**
   * 검색 결과의 아이템 수
   */
  total = 0;
  <% } %>

  // --------------------------------------------------------------------------
  // 어플리케이션 속성
  // --------------------------------------------------------------------------

  // NZ Draft
  expandable = true; // 테이블의 expandable 기능 사용 여부
  isOperating = false;

  constructor(
    public dialogRef: NzModalRef,
    private renderer: Renderer2,
    private cdr: ChangeDetectorRef,
    private translateService: TranslateService,

    public constants: Constants,
    private magDialogService: MagDialogService,
    private magSpaceService: MagSpaceService,
    private <%= name %>Service: <%= classify(name) %>Service,
    ) {
    this.actionButtons.next([
      new ActionButton(CrudAction.close, '닫기', {
        type: 'button',
      }),
      new ActionButton(CrudAction.confirm, '확인', {
        color: 'primary',
        type: 'submit',
      })
    ]);
  }

  // --------------------------------------------------------------------------
  // MAG: 라이프 사이클 및 필수 기능 구현(oInit, makeDialogTitle, onAction, onDestroy)
  // --------------------------------------------------------------------------

  @Override
  ngOnInit(): void {
    this.dataSource = new <%= classify(pluralName) %>DataSource(this.option.multiple);
    this.columns = this.makeColumns();

    // 검색 폼(xxxx-search-from) 컴포넌트가 있을 경우, 컴포넌트 초기화시 검색 폼에서 검색 이벤트를 발행한다. 만약 검색 컴포넌트가 없을 경우네느 아래 주석을 해제하여, 페이지 컴포넌트가 데이터를 초기화한도록 한다.
    // this.loadPage();
  }

  @Override
  makeDataSource(): SelectableZorroDataSource<<%= classify(name) %>Dto, <%= classify(pluralName) %>DialogSearchModel> {
    return new <%= classify(pluralName) %>DataSource(this.option.multiple);
  }

  @Override
  makeColumns(): MagTableColumn[] {
    return [
      new MagTableColumnBuilder('id', 'sample.glossary.id')
        .sort((a: <%= classify(name) %>Dto, b: <%= classify(name) %>Dto) => {
          return a.id - b.id;
        }, 'descend')
        .width(this.constants.widthId).build(),
      new MagTableColumnBuilder('thumbnail', 'sample.glossary.thumbnail')
        .width(this.constants.widthThumbnail).build(),
      new MagTableColumnBuilder('fileName', 'sample.glossary.name')
        .sort((a: <%= classify(name) %>Dto, b: <%= classify(name) %>Dto) => {
          return MagUtil.compareStrings(a.name, b.name);
        })
        .build(),
      new MagTableColumnBuilder('caption', 'sample.glossary.caption').build(),
      new MagTableColumnBuilder('memo', 'sample.glossary.memo')
        .sort((a: <%= classify(name) %>Dto, b: <%= classify(name) %>Dto) => {
          return MagUtil.compareStrings(a.memo, b.memo);
        })
        .width(this.constants.widthMemo).build(),
      new MagTableColumnBuilder('user', 'sample.glossary.register_user')
        .width(this.constants.widthUser).build(),
      new MagTableColumnBuilder('status', 'sample.glossary.status')
        .filter(false, [], (value: NzSafeAny) => {
          // 데이터소스에게 필터링을 위임한다.
          this.onChangeFilter('status', value)
        })
        .width(this.constants.widthStatus)
        .visible(true).build(),
      new MagTableColumnBuilder('fileSize', 'sample.glossary.file_size')
        .sort((a: <%= classify(name) %>Dto, b: <%= classify(name) %>Dto) => {
          return a.fileSize - b.fileSize;
        }, 'descend')
        .width(this.constants.widthCount).build(),
      new MagTableColumnBuilder('createdAt', 'sample.glossary.created_datetime')
        .sort((a: <%= classify(name) %>Dto, b: <%= classify(name) %>Dto) => {
          return MagUtil.compareStrings(a.createdAt, b.createdAt);
        })
        .width(this.constants.widthDatetime2)
        .visible(true).build(),
      new MagTableColumnBuilder('action', 'Action')
        .width(this.constants.widthAction)
        .visible(true, true).build(),
    ];
  }

  /**
   * 목록에 노출되는 컬럼 목록을 리턴한다.
   */
  get displayColumns(): MagTableColumn[] {
    return this.columns.filter(column => column.visible);
  }

  /**
   * 목록 데이터가 구성된 후에, 컬럼 필터 데이터 구성이 필요한 경우 컬럼 필터 데이터를 업데이트한다.
   * @private
   */
  private updateColumnFilters(): void {
    // const idx = this.columns.findIndex(item => item.key === 'media');
    // this.columns[idx].listOfFilter= this.getDataSource().tableFilter.filters('media');
  }

  onResize({ width }: NzResizeEvent, col: MagTableColumn): void {
    this.columns = this.columns.map(e => (e.key === col.key ? { ...e, width: `${width}px` } : e));
  }

<% if ( sideArea ) { %>
  onResizeSidebar({ col }: NzResizeEvent): void {
    cancelAnimationFrame(this.id);
    this.id = requestAnimationFrame(() => {
      this.col = col!;
    });
  }

<% } %>
  @Override
  ngAfterViewChecked(): void {
    // TODO 아래 기능은 실험적인 기능으로 필요시에 활성화한다.
    // 테이블 목록의 스크롤 가능 영역을 설정한다.
    // this.magSpaceService.register(this.componentId, 'exclude-modal-table-space', new MagSpaceInfo({
    //   spaceCount: 2, // 화면내 space 로 구분되는 영역의 수: 검색 영역, 테이블 목록 영역
    //   withFrame: false, // containerElement 내에 MagFrame 표시 여부
    //   withMiddleTable: true, // 테이블 목록 포함 여부
    //   containerElement: this.contentAreaElement, // 영역 계산의 기준이 되는 상위 컨테이너
    // }));

    // this.magSpaceService.start(this.componentId);
    // this.magSpaceService.onSpaceChange.subscribe((map: Map<string, MagSpaceStatus>) => {
    //   this.tableScrollSize = map.get(this.componentId).getSize('exclude-modal-table-space');
    // });
  }

  @Override
  onAction(action: string | CrudAction): void {
    if (action === CrudAction.close) {
      this.cancel();
    } else if (action === CrudAction.confirm) {
      this.confirm();
    } else {
      throw new Error('not implemented action: ' + action);
    }
  }

  @Override
  ngOnDestroy(): void {

  }

  // --------------------------------------------------------------------------
  // MAG : 데이터 IO
  // --------------------------------------------------------------------------

   /**
    * 검색 폼으로부터 검색 키워드를 업데이트한다.
    * @param searchModel
    */
   onSubmitSearchForm(searchModel: <%= classify(pluralName) %>DialogSearchModel): void {
     this.searchModel = searchModel;

     <% if ( !frontPaging ) { %>
     this.pageableParam.page = 0;
     <% } %>

     this.loadPage();
   }

   <% if ( !frontPaging ) { %>
   /**
    * 페이지를 변경 한다.
    * @param pageNo 페이지 번호. 1부터시 시작 (Note. nz-zorro 에서는 1부터 시작한다.)
    */
   onChangePage(pageNo: number) {
     if (isNaN(pageNo)) {
       // onChangePageSize 이벤트가 발생할 경우, pageNo 가 NaN 으로 메서드가 호출된다.
       return;
     }
     this.pageableParam.page = pageNo -1;
     this.pageableParam.pageNumber = pageNo -1;
     this.loadPage();
   }

   /**
    * 페이지 사이즈를 변경한다.
    * @param pageSize
    */
   onChangePageSize(pageSize: number) {
     this.pageableParam.pageSize = pageSize;
     this.pageableParam.pageNumber = 0;
     this.pageableParam.page = 0;
     this.loadPage(); // TODO mag2 에서 서버 페이징에서 이게 맞을지 고민해보자
   }
   <% } %>

  onChangeFilter(group: string, value: any | any[]): void {
    if (group === 'keyword') {
      if (value && value !== '') {
        // 키워드 입력인 경우, 사이띄기를 기준으로 복합 키워드 검색을 지원한다.
        const parseDetail = AdvancedSearchQuery.parse(_.trim(value));

        _.forEach(parseDetail.conditionArray, (item) => {
          const prop = _.set(this.searchModel, item.keyword, item.value);
        });

        if (parseDetail.textSegments && parseDetail.textSegments.length > 0) {
          this.searchModel.keywords = parseDetail.textSegments.map(textSegment => textSegment.text);
        }
        console.log(this.searchModel);
      } else {
        this.searchModel.keywords = [];
      }
    } else if (group === 'role') {
      // 컬럼내 멀티 선택 필터링인 경우, value 가 배열일 수 있다. 필요시 속성을 변경 적용한다.
      this.searchModel['roles'] = value;
    } else {
      this.searchModel[group] = value;
    }

    // TODO 클라이언트 필터링 방식(페이징이 아님!)으로검색 조건으로 데이터를 필터링한다.
    this.dataSource.applyFilter(this.searchModel);

    // TODO <머신건_페이징_가이드> 서버 필터링(페이징이 아님!)을 사용할 경우 아래 주석을 해제하세요
    // this.pageableParam.page = 0;
    // this.loadPage();
  }

  loadPage(): void {
    <% if ( frontPaging ) { %>
    this.loading = true;
    this.<%= name %>Service.getList().then(result => {
      this.dataSource.updateData(result);
      this.dataSource.applyFilter(this.searchModel);
      this.updateColumnFilters();
      this.loading = false;
      this.cdr.markForCheck();
    }).catch((error: NzSafeAny) => {
      Helpers.handleError(error);
      this.loading = false;
    });
    <% } else { %>
    // @ts-ignore
    const request: Get<%= classify(name) %>PageRequest = _.cloneDeep(this.searchModel);
    _.merge(request, this.pageableParam);

    this.<%= name %>Service.getPage(request).then(result => {
      this.dataSource.updateData(result.content);
      this.total = result.totalElements;
      this.loading = false;
      this.cdr.markForCheck();
    }).catch((error: NzSafeAny) => {
      Helpers.handleError(error);
      this.loading = false;
    });
    <% } %>
  }

  // --------------------------------------------------------------------------
  // 비즈니스
  // --------------------------------------------------------------------------

  cancel(): void {
    const result: <%= classify(pluralName) %>DialogResult  = {
      items: [],
      multipleSelection: this.option.multiple,
      selected: false,
    };
    this.dialogRef.close(result);
  }

  confirm(): void {
    if (this.dataSource.getSelectedCount() === 0) {
      this.magDialogService.alertWarning(this.translateService.instant('sample.common.required_selection'));
      return;
    }
    const result: <%= classify(pluralName) %>DialogResult = {
      items: [],
      selected: true,
      multipleSelection: this.option.multiple,
    };
    if (this.option.multiple) {
      result.items = this.dataSource.getSelectedList();
    } else {
      result.items = [this.dataSource.getSelectedOne()];
    }
    this.dialogRef.close(result);
  }

  operateData(): void {

  }

}
