/**
 * @since > Mon Sep 17 2018
 * @author Kasper Hansen - Klean
 * 
 * Modified
 * @since Mon Sep 17 2018
 * @author Charles Gouldmann - Klean
 */

import { Component, OnInit }                from '@angular/core'
import { Router, NavigationEnd }            from '@angular/router'
import { Location } from '@angular/common'
import { ISubscription }                    from 'rxjs/Subscription'

// Component Services
import { ConfiguratorService }              from '../services/svc.configurator'
import { ConfiguratorProductLoaderService } from '../services/svc.configurator-product-reloader'

// Component Types
import { configProductType } from '../types/type.configProduct'

// Global / Shared Services
import { PageService }  from '@k-services/svc.page'
import { SeoService }   from '@k-services/svc.seo'
import { AppHelper }    from '@k-settings/app-helper'
import { Observable } from 'rxjs'
import { delay, map, take, tap } from 'rxjs/operators'
import { forkJoin } from 'rxjs/observable/forkJoin'
import { ICurtain } from '@k-core/interfaces/configurator/interface.curtain'

@Component({
    moduleId: module.id+ '',
    selector: 'configurator-partial',
    templateUrl: './templates/t--configuratorPartial.pug',
    styleUrls: ['sty.configurator-partial.scss']
})

export class ConfiguratorPartial implements OnInit {

    // ---- Variables ---- \\
    parents: any
    parent: string
    children: any
    specs: any

    product: Map<string, any> = new Map
    values: any
    price: any = undefined
    timeout: any

    // Subscriptions
    private reset_sub: ISubscription
    private router_events_sub: ISubscription
    private spec_selector_sub: ISubscription

    // By default type id selected on configuraton page
    selectedSpecId: string = 'type_id' 
    belowTheFold: boolean = false
    loadCustom: boolean = false

    constructor(
        protected _configService: ConfiguratorService,
        protected _dataCenter: ConfiguratorProductLoaderService,
        protected _page:  PageService,
        protected _seo:  SeoService,
        protected _router: Router,
        protected _location: Location
    ) { 
        // Trigger clear if reset observable is true
        this.reset_sub = this._configService.reset$.subscribe((reset) => {
            
            if(!!reset) {
                this.stealhyClear()
                this._configService.setReset(false)
            }
        })

        // ! Should be moved to relevant store
        // Specification selector subscription for now used only in suntex
        this.spec_selector_sub = this._configService.specSelector$.subscribe((type_id) => {

            this.selectedSpecId = type_id
        })


        // Set SEO
        this._page.getPageContent('konfigurator')
            .then((content) => {
                this._seo.setTitle(content.meta.title)

                if(!!content.meta && !!content.meta.description)
                    this._seo.updateTag('description', content.meta.description)
            })

        
        // Subscribe to route changes
        this.router_events_sub = _router.events.subscribe((ev) => {
            if(ev instanceof NavigationEnd) {

                // Route is base path
                if(_router.url === AppHelper.configurator) {
                    this.stealhyClear()
                }
            }
        })
    }





    // ---- Lifecycle hooks ---- \\
    ngOnInit() {

        this._dataCenter.stopPreset = false

        this._configService.getParents().then(response => {
            this.parents = response
        })
    }

    ngOnDestroy() {
        this._dataCenter.clearPreviousBuild()
        this.loadCustom = false
        // this.stealhyClear()

        // Unsubscribe on destroy
        this.reset_sub.unsubscribe()
        this.router_events_sub.unsubscribe()
        this.spec_selector_sub.unsubscribe()
    }







    // ---- Functions ---- \\
    /**
     * Sets value of product based on which category is incoming
     * Also sets Specs and requests a price if data is valid
     * 
     * @param event 
     * @param category 
     * @param selection 
     */
    setValue(event, category, product?) {

        this.price = undefined

        switch(category) {

            case 'parent':

                this.product.clear()
                this.children = undefined

                delete this.specs
                this.loadCustom = false
                this._configService.configuratorType = undefined

                this.belowTheFold = false


                this.getChildren(event).subscribe((response) => {

                    this.product.set('type_id', event)
                    

                    if(response.length === 1) {

                        // TODO: This is very very very wrong.
                        let child = [...response[0].content]
                        child['type_id'] = response[0].type_id

                        this.setValue(child, 'child')
                    }

                    this._configService.populateProduct(MapToObject(this.product))
                })


                break;

            // If child or only 1 child type
            case 'child':

                // Do a stealthy clear if product is not defined, type is not defined, and type + click is not the same, to ensure correct reload
                if(!product && !!this.product.get('type_id') && event.type_id !== this.product.get('type_id') && !this.product.get('type_id').includes('_SUPER')) {
                    this._dataCenter.clearStealthySelection()
                    this.product.clear()
                }


                setTimeout(() => {
                    this.belowTheFold = true
                }, 2000)

                // Generate specmap for translations on external elements
                this._configService.generateSpecMap(event)

                this._configService.configuratorType = this.children.find((child) => child.type_id == event.type_id).configurator_type

                if(this._configService.configuratorType == 'sunway') {
                    this.loadCustom = true
                    this.children = []
                }

                this.specs = event
                this._configService.setSpecs(event)
                this.product.set('type_id', event.type_id)

                break


            case 'dessin':

                if(event.customChild) {
                    
                    this.product.set('dessin', event.dessin)
                    this.product.set('color-family', event.family)
                    
                    
                    let initial = this.children.filter((child) => child.type_id == event.customChild)[0]
                    let postable = [...initial.content]
                    
                    postable['type_id'] = event.customChild
                    
                    this.product.set('type_id', event.customChild)
                    let storedProduct = new Map(this.product)

                    this.setValue(postable, 'child', storedProduct)

                    setTimeout(() => {
                        this.belowTheFold = true
                    }, 4000)


                } else {

                    if(event.dessin) {
                        this.product.set('dessin', event.dessin)
                    }
    
                    if(event.family) {
                        this.product.set('color-family', event.family)
                    }
                }




                break;

            default:

                this.product.set(category, event)
                break
        }




        this._configService.populateProduct(MapToObject(this.product))
        
        if(!!this.timeout) {
            clearTimeout(this.timeout)
        }
    }


    /**
     * Calculates the position of the element to display prefix numbering
     * 
     * @param element 
     */
    getPosition(element) {
        
        let position = 0
        let container = []
        if(this.children.length != 1) {
            position += 1
        }

        if(!!this.children[0] && !!this.children[0].content) {
    
            for(let elm of Object.keys(this.children[0].content)) {
    
                if(this.children[0].content[elm].id !== 'type_id') 
                    container.push(this.children[0].content[elm])
            }
    
            for (const key of container) {
    
                if(element == key.id)
                    position += container.indexOf(key) + 1
            }
        }

        return position
    }


    /**
     * Gets child info when parent is selected
     * 
     * @param parent
     */
    getChildren(parent): Observable<any> {

        return this._configService.getChildren(parent).pipe(map((response) => {
            this.parent = parent
            this.children = response

            return response
        }))
    }

    stealhyClear() {
        this.product.clear()
        this._dataCenter.clearStealthySelection()
    }

    clear() {
        this.product.clear()
        this._dataCenter.clearSelection()
        this._dataCenter.clearPreviousBuild()
    }



    /**
     * TODO Describe what is happening here
     * Handles mount height event from mountheight component.
     * 
     * @param event 
     */
    public onMountHeight(event) {

        new Observable((observer) => {

            let i = 0

            // Loop over keys in event and update product with values. If value is null delete key from product
            for(let key of Object.keys(event)) {
                if(event[key] == null) {
                    this.product.delete(key)
                }
                else {
                    this.product.set(key, event[key])
                }

                i++
            }


            // If done iterating, complete the observable
            if(i == Object.keys(event).length) {

                observer.next(this.product)
                observer.complete()
            }
        })
        .pipe(

            // Flatten Map to be a static object
            map((productMap: Map<string, any>) => {

                let object = {}


                productMap.forEach((value, key) => {
                    object[key] = value
                })


                return JSON.parse(JSON.stringify(object))
            })
        )
        .subscribe({
            next: (product: any) => {
                this._configService.populateProduct(product)
            }
        })
    }
}


function MapToObject(aMap) {
    const obj = {};
    aMap.forEach ((v,k) => { obj[k] = v });
    return obj;
}