import {
  AfterViewInit,
  Component,
  Inject,
  OnDestroy,
  OnInit,
  ChangeDetectionStrategy,
  ChangeDetectorRef
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import AdvancedSearchQuery from "advanced-search-query";

import { Override, Page,<% if ( !frontPaging ) { %>  Pageable,<% } %> MagI18nPair, MagTableColumn, MagTableColumnBuilder } from 'ng-stalk/core/model';
import { NsCrudService } from 'ng-stalk/crud';
import {
  CrudAction,
  ActionButton,
  ActionButtonPreset,
  CrudActionResolver,
  CrudModal2Component,
  CrudPage2Component,
  MagGroupAndFilterDescription,
  SelectableZorroDataSource
} from 'ng-stalk/core/crud';
import { ContentAreaImpl2 } from 'ng-stalk/modal-template'
import {
  MagCodeCacheService,
  MagDateService,
  MagDialogService,
  MagFormService,
  MagMatchMediaService,
  MagSpaceService,
  MagSpaceInfo,
  MagSpaceStatus,
} from 'ng-stalk/core/service';
import { CodeAndName, NsConstants} from 'ng-stalk/core/types';
import { MagUtil } from 'ng-stalk/core/util';
import { NzSafeAny } from 'ng-zorro-antd/core/types';
import { NzModalService } from 'ng-zorro-antd/modal';
import { NzNotificationService } from 'ng-zorro-antd/notification';
import { NzResizeEvent } from 'ng-zorro-antd/resizable';
import { Observable, Observer, Subject, pipe } from 'rxjs';
import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators';
import * as _ from 'lodash';

import { Constants } from 'src/app/service/constants/constants.service';
import { Helpers } from '../sample/helpers';
import { <%= classify(name) %>Dto } from '../sample/<%= dasherize(name) %>-dto';
import { <%= classify(name) %>DataSource } from './<%= dasherize(name) %>-data-source';
import { <%= classify(name) %>DetailModalComponent } from '../<%= dasherize(name) %>-detail-modal/<%= dasherize(name) %>-detail-modal.component';
import { <%= classify(name) %>EditModalComponent } from '../<%= dasherize(name) %>-edit-modal/<%= dasherize(name) %>-edit-modal.component';
import { <%= classify(name) %>SearchModel } from '../<%= dasherize(name) %>-search-form/<%= dasherize(name) %>-search-model';
import { <%= classify(name) %>Service } from '../service/<%= dasherize(name) %>-service/<%= dasherize(name) %>.service';
<% if ( !frontPaging ) { %>
import {Get<%= classify(name) %>PageRequest} from "../../../service/<%= dasherize(name) %>-service/get-<%= dasherize(name) %>-page-request";
<% } %>
import { ChildrenItemData, Sampler } from './sampler';



@Component({
  selector: 'app-<%= dasherize(name) %>-page',
  templateUrl: './<%= dasherize(name) %>-page.component.html',
  styleUrls: ['./<%= dasherize(name) %>-page.component.less'],
  // changeDetection: ChangeDetectionStrategy.OnPush
})
export class <%= classify(name) %>PageComponent implements OnInit, AfterViewInit, OnDestroy {

  // --------------------------------------------------------------------------
  // 공통 속성
  // --------------------------------------------------------------------------
  destroy$: Subject<NzSafeAny>;
  searchModel: <%= classify(name) %>SearchModel = {};

  // --------------------------------------------------------------------------
  // 레이아웃 속성
  // --------------------------------------------------------------------------

  columns: MagTableColumn[] = [];
  tableScrollSize: number = 0;
  <% if ( sideArea ) { %>
  col = 4; // 현재 left colspan
  id = -1;
  <% } %>
  loading = false;
  expandable = true; // 테이블의 expandable 기능 사용 여부
  isDeleting = false;
  isDownloading = false;

  // --------------------------------------------------------------------------
  // 페이징 속성
  // --------------------------------------------------------------------------

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

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

  // --------------------------------------------------------------------------
  // 어플리케이션 속성
  // --------------------------------------------------------------------------
  // // TODO 사용자 코드는 이곳에 작성하세요

  constructor(
    router: Router,
    route: ActivatedRoute,
    nzModalService: NzModalService,
    private cdr: ChangeDetectorRef,
    public nsCrud: NsCrudService<<%= classify(name) %>Dto, <%= classify(name) %>SearchModel>,
    private translationService: TranslateService,
    private nzNotificationService: NzNotificationService,
    private magDialogService: MagDialogService,
    protected magSpaceService: MagSpaceService,
    protected magMatchMediaService: MagMatchMediaService,
    private magCodeCacheService: MagCodeCacheService,
    public constants: Constants,
    private <%= name %>Service: <%= classify(name) %>Service,
  ) {
    this.destroy$ = new Subject();
    this.nsCrud.registerDataSource(new <%= classify(name) %>DataSource(this.magCodeCacheService));
    this.nsCrud.dataSource.connect()
      .pipe(takeUntil(this.destroy$))
      .subscribe((apps)=> {
        this.cdr.markForCheck();
      });
    this.nsCrud.addModalComponent(CrudAction.openAddModal, <%= classify(name) %>EditModalComponent);
    this.nsCrud.addModalComponent(CrudAction.openEditModal, <%= classify(name) %>EditModalComponent);
    this.nsCrud.addModalComponent(CrudAction.openDetailModal, <%= classify(name) %>DetailModalComponent);
  }

  // --------------------------------------------------------------------------
  // MAG: 라이프 사이클
  // --------------------------------------------------------------------------

  @Override
  ngOnInit(): void {
    this.columns = this.makeColumns();

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

  @Override
  ngAfterViewInit(): void {
  }

  @Override
  ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
  }

  // --------------------------------------------------------------------------
  // MAG : 데이터 소스 생성 및 모달 컴포넌트 연동
  // --------------------------------------------------------------------------

  @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('name', 'sample.glossary.name')
        .sort((a: <%= classify(name) %>Dto, b: <%= classify(name) %>Dto) => {
          return MagUtil.compareStrings(a.name, b.name);
        })
        .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('fileSize', 'sample.glossary.fileSize')
        .sort((a: <%= classify(name) %>Dto, b: <%= classify(name) %>Dto) => {
          return a.fileSize - b.fileSize;
        })
        .width(this.constants.widthCount).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('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.nsCrud.dataSource.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!;
    });
  }

<% } %>

  public onSpaceResize(groupAndHeightMap: Map<string, number>): void {
    console.error('groupAndHeightMap:', groupAndHeightMap);

    // @ts-ignore
    const gutter = NsConstants.PAGE_BOTTOM_PAD * 2
      + NsConstants.AREA_GAP * 2
      + NsConstants.AREA_PAD * 2 + NsConstants.AREA_PAD * 2
      + NsConstants.TABLE_HEADER + NsConstants.TABLE_FOOTER
      + NsConstants.ELEMENT_GAP * 2;

    this.tableScrollSize = groupAndHeightMap.get('frameContentHeight') - groupAndHeightMap.get('no-table') - gutter;
    console.log("** groupAndHeightMap.get('frameContentHeight'):", groupAndHeightMap.get('frameContentHeight'),)
    console.log("** groupAndHeightMap.get('no-table'):", groupAndHeightMap.get('no-table'),)
    console.log("*** tableScrollSize:", this.tableScrollSize);
    // @ts-ignore
    // this.tableScrollSize2 = 'auto';
  }

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

  /**
   * 검색 폼으로부터 검색 키워드를 업데이트한다.
   * @param searchModel
   */
  onSubmitSearchForm(searchModel: <%= classify(name) %>SearchModel): 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.nsCrud.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.nsCrud.dataSource.updateData(this.nsCrud.sortData(result));
      this.nsCrud.dataSource.applyFilter(this.searchModel);
      this.updateColumnFilters();
      this.loading = false;
    }).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.nsCrud.dataSource.updateData(result.content);
      this.total = result.totalElements;
    }).catch((error: NzSafeAny) => {
      Helpers.handleError(error);
    });
    <% } %>
  }

  // --------------------------------------------------------------------------
  // 어플리케이션 비즈니스 로직
  // --------------------------------------------------------------------------

  downloadData(): void {
    this.isDownloading = true;
    setTimeout(() => {
      alert('준비중입니다');
      this.isDownloading = false;
    }, 1000);
  }

  deleteSelected(): void {
    const message = this.translationService.instant('sample.common.prompt_to_delete_selected_items', {
      numberOfCases: this.nsCrud.dataSource.selected().length
    });
    this.isDeleting = true;
    this.magDialogService.confirm({
      content: message
    }).subscribe(result => {
      // TODO API 로 벌크 삭제 후, 처리 결과에 따라 반영 처리 해야한다.
      // const deleteBulkRequest = {
      //   ids: this.nsCrud.dataSource.getSelected().map(item => item.id),
      // };
      // this.<%= name %>Service.deleteBulk(deleteBulkRequest).then((deleteResult) => {
      //   for (const item of deleteResult) {
      //     this.dataSource.remove(item);
      //   }
      // });
      // TODO 샘플 코드에서는 바로 삭제한다.
      for (const item of this.nsCrud.dataSource.getSelectedList()) {
        this.nsCrud.dataSource.remove(item);
      }
      this.isDeleting = false;
    });
  }

}
