/*
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*eslint no-undef: "warn"*/
import WidgetCommonProperties = JQueryUI.WidgetCommonProperties;
import {EMPTY_LAYOUT, Layout} from './domain/layout';
import {CookieHandler} from './cookie-handler';
import {Globals} from './domain/globals';
import {LayoutRenderer} from './layout-renderer';
import {UhrRenderer} from './uhr-renderer';
import {WidgetPrototype} from './widget/widget-prototype';

declare var $: JQueryStatic;

export class Uhr {
    private timer: number = null;
    private currentMinute: number = null;
    private renderer: UhrRenderer;
    private cookieHandler: CookieHandler;

    public constructor(private readonly widgetInstance: WidgetPrototype & WidgetCommonProperties) {
        const userTime = this.widgetInstance.options.time;
        if (this.widgetInstance.options.time === undefined) {
            this.widgetInstance.options.time = new Date();
        }
        this.cookieHandler = new CookieHandler(this.widgetInstance.uuid, this.widgetInstance.options.cookiePath);
        this.parseHash();
        this.renderer = new UhrRenderer(this, this.widgetInstance.element, this.widgetInstance.options, this.widgetInstance.uuid);
        this.renderer.render();
        if (userTime !== undefined) {
            this.setTime(userTime);
        }
    }

    public destroy(): void {
        if (this.timer) {
            window.clearInterval(this.timer);
            this.timer = null;
        }
        this.widgetInstance.element
            .removeAttr('style')
            .removeAttr('class')
            .empty();
        $(`#uhr-configlink${this.widgetInstance.uuid}`).remove();
        $(`#uhr-controlpanel${this.widgetInstance.uuid}`).remove();
    }

    public start(): void {
        if (!this.isOn()) {
            this.timer = window.setInterval((): void => {
                this.widgetInstance.options.time = new Date();
                this.update();
            }, 1000);
            this.update();
            this.cookieHandler.setStatus('on');
        }
    }

    public stop(): void {
        if (this.isOn()) {
            window.clearInterval(this.timer);
            this.timer = null;
            this.update();
            this.cookieHandler.setStatus('off');
        }
    }

    public toggle(): void {
        if (this.isOn()) {
            this.stop();
        } else {
            this.start();
        }
    }

    public setLayout(key: string): void {
        if (key !== this.widgetInstance.options.language) {
            this.widgetInstance.options.language = key;
            const renderer = new LayoutRenderer(this.getCurrentLayout(), this.widgetInstance.element.find('.letterarea'));
            renderer.render((): void => {
                this.currentMinute = -1;
                this.update();
            });
            this.cookieHandler.setLayout(key);
            this.update();
        }
    }

    public setTheme(styleClass: string): void {
        if (styleClass !== this.widgetInstance.options.theme) {
            this.widgetInstance.element.removeClass(this.widgetInstance.options.theme).addClass(styleClass);
            $(`#uhr-onoffswitch${this.widgetInstance.uuid}`).removeClass(this.widgetInstance.options.theme).addClass(styleClass);
            this.widgetInstance.options.theme = styleClass;
            this.cookieHandler.setTheme(styleClass);
        }
    }

    public setTime(time: Date): void {
        this.currentMinute = null;
        if (time === null) {
            this.widgetInstance.options.time = new Date();
        } else {
            if (this.timer !== null) {
                window.clearInterval(this.timer);
            }
            this.widgetInstance.options.time = time;
        }
        this.update();
    }

    public setMode(mode: string): void {
        this.widgetInstance.options.mode = mode;
        this.currentMinute = null;
        this.update();
        this.cookieHandler.setMode(mode);
    }

    public setWidth(width: string): void {
        this.renderer.setWidth(width);
    }

    private isOn(): boolean {
        return this.timer !== null;
    }

    private update(): void {
        if (this.isOn()) {
            const time = this.widgetInstance.options.time;
            if (!this.getCurrentLayout().hasOwnProperty('seconds') && this.widgetInstance.options.mode !== 'seconds') {
                if (time.getMinutes() === this.currentMinute) {
                    return;
                }
                this.currentMinute = time.getMinutes();
            }
            this.show(time);
        } else {
            this.clear();
            this.currentMinute = -1;
        }
    }

    private show(time: Date): void {
        const second = this.getSecond(time);
        const dotMinute = this.getDotMinute(time);
        const hour = this.getHour(time);
        const coarseMinute = this.getCoarseMinute(time);
        this.clear();
        if (this.widgetInstance.options.mode === 'seconds') {
            this.highlight(`second${second}`);
        } else {
            this.highlight('on');
            for (let i = 1; i <= dotMinute; i++) {
                this.highlight(`dot${i}`);
            }
            this.highlight(`minute${coarseMinute}`);
            this.highlight(`hour${hour}`);
        }
    }

    private clear(): void {
        this.widgetInstance.element.find('.item').removeClass('active');
    }

    private highlight(itemClass: string): void {
        this.widgetInstance.element.find(`.item.${itemClass}`).addClass('active');
    }

    private getSecond(time: Date): number {
        if (typeof this.getCurrentLayout().getSeconds === 'function') {
            return this.getCurrentLayout().getSeconds(time);
        }
        return time.getSeconds();
    }

    private getDotMinute(date: Date): number {
        if (typeof this.getCurrentLayout().getDotMinute === 'function') {
            return this.getCurrentLayout().getDotMinute(date);
        }
        return date.getMinutes() % 5;
    }

    private getCoarseMinute(date: Date): number {
        if (typeof this.getCurrentLayout().getCoarseMinute === 'function') {
            return this.getCurrentLayout().getCoarseMinute(date);
        }
        return date.getMinutes();
    }

    private getHour(date: Date): number {
        if (typeof this.getCurrentLayout().getHour === 'function') {
            return this.getCurrentLayout().getHour(date);
        }
        const hour = date.getHours();
        if (date.getMinutes() >= 25) {
            return (hour + 1) % 24;
        }
        return hour;
    }

    private parseHash(): void {
        let hash: string = window.location.hash;
        if (hash !== undefined && hash.charAt(0) === '#') {
            hash = hash.substring(1);
            hash = decodeURIComponent(hash);
            const params: string[] = hash.split('&');
            params.forEach((element: string): void => {
                const pair: string[] = element.split('=');
                const key = pair[0];
                const value = pair[1];
                switch (key) {
                    case 'l':
                    case 'language':
                        this.widgetInstance.options.language = value;
                        this.widgetInstance.options.force = true;
                        break;
                    case 't':
                    case 'theme':
                        this.widgetInstance.options.theme = value;
                        this.widgetInstance.options.force = true;
                        break;
                    case 'm':
                    case 'mode':
                        this.widgetInstance.options.mode = value;
                        this.widgetInstance.options.force = true;
                        break;
                    case 's':
                    case 'status':
                        this.widgetInstance.options.status = value;
                        this.widgetInstance.options.force = true;
                        break;
                }
            });
        }
    }

    private getCurrentLayout(): Layout {
        const matchingLanguages: Layout[] = Globals.getLayouts().filter((element: Layout): boolean => element.code === this.widgetInstance.options.language, this);
        if (matchingLanguages.length > 0) {
            return matchingLanguages[0];
        }
        // fallback: return empty object
        return EMPTY_LAYOUT;
    }
}