/*!
 * @license
 * Copyright 2016 Alfresco Software, Ltd.
 *
 * 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
 *
 *     http://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 { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Response } from '@angular/http';
import { TranslateLoader } from '@ngx-translate/core';
import { Observable } from 'rxjs/Observable';
import { ComponentTranslationModel } from '../models/component.model';
import { ObjectUtils } from '../utils/object-utils';
import { LogService } from './log.service';
import { map } from 'rxjs/operators';
import 'rxjs/observable/forkJoin';
import 'rxjs/add/observable/forkJoin';

@Injectable()
export class TranslateLoaderService implements TranslateLoader {

    private prefix: string = 'i18n';
    private suffix: string = '.json';
    private providers: ComponentTranslationModel[] = [];
    private queue: string [][] = [];

    constructor(private http: HttpClient,
                private logService: LogService) {
    }

    registerProvider(name: string, path: string) {
        let registered = this.providers.find(provider => provider.name === name);
        if (registered) {
            registered.path = path;
        } else {
            this.providers.push(new ComponentTranslationModel({ name: name, path: path }));
        }
    }

    providerRegistered(name: string): boolean {
        return this.providers.find(x => x.name === name) ? true : false;
    }

    getComponentToFetch(lang: string) {
        let observableBatch = [];
        if (!this.queue[lang]) {
            this.queue[lang] = [];
        }
        this.providers.forEach((component) => {
            if (!this.isComponentInQueue(lang, component.name)) {
                this.queue[lang].push(component.name);

                const loader = Observable.create(observer => {
                    const translationUrl = `${component.path}/${this.prefix}/${lang}${this.suffix}?v=${Date.now()}`;

                    this.http.get(translationUrl).pipe(map((res: Response) => {
                        component.json[lang] = res;
                    })).subscribe((result) => {
                        observer.next(result);
                        observer.complete();
                    }, () => {
                        observer.next('');
                        observer.complete();
                    });
                });

                observableBatch.push(loader);
            }
        });

        return observableBatch;
    }

    init(lang: string) {
        if (this.queue[lang] === undefined) {
            this.queue[lang] = [];
        }
    }

    isComponentInQueue(lang: string, name: string) {
        return (this.queue[lang] || []).find(x => x === name) ? true : false;
    }

    getFullTranslationJSON(lang: string): any {
        let result = {};

        this.providers
            .slice(0)
            .sort((a, b) => {
                if (a.name === 'app') {
                    return 1;
                }
                if (b.name === 'app') {
                    return -1;
                }
                return a.name.localeCompare(b.name);
            })
            .forEach(model => {
                if (model.json && model.json[lang]) {
                    result = ObjectUtils.merge(result, model.json[lang]);
                }
            });

        return result;
    }

    getTranslation(lang: string): Observable<any> {
        let observableBatch = this.getComponentToFetch(lang);

        return Observable.create(observer => {
            if (observableBatch.length > 0) {
                Observable.forkJoin(observableBatch).subscribe(
                    () => {
                        let fullTranslation = this.getFullTranslationJSON(lang);
                        if (fullTranslation) {
                            observer.next(fullTranslation);
                        }
                        observer.complete();
                    },
                    (err: any) => {
                        this.logService.error(err);
                    });
            } else {
                let fullTranslation = this.getFullTranslationJSON(lang);
                if (fullTranslation) {
                    observer.next(fullTranslation);
                }
            }
        });
    }
}
