/**
 * This file is part of Anis Client.
 *
 * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

import { Component } from '@angular/core';

import { Store } from '@ngrx/store';
import { Observable, Subscription } from 'rxjs';

import { AbstractSearchComponent } from './abstract-search.component';
import { Pagination } from '../../store/models';
import { Instance, Dataset, Image, ConeSearchConfig } from 'src/app/metamodel/models';
import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector';
import * as searchActions from '../../store/actions/search.actions';
import * as searchSelector from '../../store/selectors/search.selector';
import * as sampActions from 'src/app/samp/samp.actions';
import * as sampSelector from 'src/app/samp/samp.selector';
import * as archiveActions from '../../store/actions/archive.actions';
import * as archiveSelector from '../../store/selectors/archive.selector';
import * as imageActions from 'src/app/metamodel/actions/image.actions';
import * as imageSelector from 'src/app/metamodel/selectors/image.selector';
import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector';
import * as coneSearchConfigActions from 'src/app/metamodel/actions/cone-search-config.actions';
import * as coneSearchConfigSelector from 'src/app/metamodel/selectors/cone-search-config.selector';

/**
 * @class
 * @classdesc Search result container.
 *
 * @implements OnInit
 * @implements OnDestroy
 */
@Component({
    selector: 'app-result',
    templateUrl: 'result.component.html'
})
export class ResultComponent extends AbstractSearchComponent {
    public instance: Observable<Instance>;
    public dataset: Observable<Dataset>;
    public dataLength: Observable<number>;
    public dataLengthIsLoading: Observable<boolean>;
    public dataLengthIsLoaded: Observable<boolean>;
    public data: Observable<any>;
    public dataIsLoading: Observable<boolean>;
    public dataIsLoaded: Observable<boolean>;
    public selectedData: Observable<any>;
    public sampRegistered: Observable<boolean>;
    public imageList: Observable<Image[]>;
    public imageListIsLoading: Observable<boolean>;
    public imageListIsLoaded: Observable<boolean>;
    public coneSearchConfig: Observable<ConeSearchConfig>;
    public coneSearchConfigIsLoading: Observable<boolean>;
    public coneSearchConfigIsLoaded: Observable<boolean>;
    public archiveIsCreating: Observable<boolean>;
    public pristineSubscription: Subscription;
    public criteraToolTip: string = "Criteria family list";

    constructor(protected store: Store<{ }>) {
        super(store);
        this.dataset = store.select(datasetSelector.selectDatasetByRouteName);
        this.instance = store.select(instanceSelector.selectInstanceByRouteName);
        this.dataLength = this.store.select(searchSelector.selectDataLength);
        this.dataLengthIsLoading = this.store.select(searchSelector.selectDataLengthIsLoading);
        this.dataLengthIsLoaded = this.store.select(searchSelector.selectDataLengthIsLoaded);
        this.data = this.store.select(searchSelector.selectData);
        this.dataIsLoading = this.store.select(searchSelector.selectDataIsLoading);
        this.dataIsLoaded = this.store.select(searchSelector.selectDataIsLoaded);
        this.selectedData = this.store.select(searchSelector.selectSelectedData);
        this.sampRegistered = this.store.select(sampSelector.selectRegistered);
        this.imageList = this.store.select(imageSelector.selectAllImages);
        this.imageListIsLoading = this.store.select(imageSelector.selectImageListIsLoading);
        this.imageListIsLoaded = this.store.select(imageSelector.selectImageListIsLoaded);
        this.coneSearchConfig = store.select(coneSearchConfigSelector.selectConeSearchConfig);
        this.coneSearchConfigIsLoading = store.select(coneSearchConfigSelector.selectConeSearchConfigIsLoading);
        this.coneSearchConfigIsLoaded = store.select(coneSearchConfigSelector.selectConeSearchConfigIsLoaded);
        this.archiveIsCreating = this.store.select(archiveSelector.selectArchiveIsCreating);
    }

    ngOnInit(): void {
        // Create a micro task that is processed after the current synchronous code
        // This micro task prevent the expression has changed after view init error
        Promise.resolve(null).then(() => this.store.dispatch(searchActions.changeStep({ step: 'result' })));
        Promise.resolve(null).then(() => this.store.dispatch(searchActions.checkResult()));
        super.ngOnInit();
        this.pristineSubscription = this.pristine.subscribe(pristine => {
            if (!pristine) {
                Promise.resolve(null).then(() => this.store.dispatch(searchActions.retrieveDataLength()));
            }
        });
        Promise.resolve(null).then(() => this.store.dispatch(imageActions.loadImageList()));
        Promise.resolve(null).then(() => this.store.dispatch(coneSearchConfigActions.loadConeSearchConfig()));
    }

    /**
     * Dispatches action to register to SAMP.
     */
    sampRegister(): void {
        this.store.dispatch(sampActions.register());
    }

    /**
     * Dispatches action to disconnect to SAMP.
     */
    sampUnregister(): void {
        this.store.dispatch(sampActions.unregister());
    }

    /**
     * Dispatches action to broadcast data.
     *
     * @param  {string} url - The broadcast URL.
     */
    broadcastVotable(url: string): void {
        this.store.dispatch(sampActions.broadcastVotable({ url }));
    }

    /**
     * Dispatches action to retrieve data with the given pagination.
     *
     * @param {Pagination} pagination - The pagination parameters.
     */
    retrieveData(pagination: Pagination): void {
        this.store.dispatch(searchActions.retrieveData({ pagination }));
    }

    /**
     * Dispatches action to add the given data ID to the selected data.
     *
     * @param  {number | string} id - The data ID to add to the data selection.
     */
    addSearchData(id: number | string): void {
        this.store.dispatch(searchActions.addSelectedData({ id }));
    }

    /**
     * Dispatches action to remove the given data ID to the selected data.
     *
     * @param  {number | string} id - The data ID to remove to the data selection.
     */
    deleteSearchData(id: number | string): void {
        this.store.dispatch(searchActions.deleteSelectedData({ id }));
    }

    downloadFile(download: {url: string, filename: string}): void {
        this.store.dispatch(searchActions.downloadFile(download));
    }

    /**
     * Dispatches action to starts task create archive and download
     *
     */
    startTaskCreateArchive(query: string) {
        this.store.dispatch(archiveActions.startTaskCreateArchive({ query }));
    }

    /**
     * Dispatches action to destroy search results.
     */
    ngOnDestroy(): void {
        this.store.dispatch(searchActions.destroyResults());
        this.store.dispatch(archiveActions.resetArchive());
        if (this.pristineSubscription) this.pristineSubscription.unsubscribe();
        super.ngOnDestroy();
    }
}