﻿import * as ko from "knockout";
import template from "./pictureEditor.html";
import { ChangeRateLimit } from "@paperbits/common/ko/consts";
import { Component, Event, OnMounted, Param } from "@paperbits/common/ko/decorators";
import { MediaContract, MediaService } from "@paperbits/common/media";
import * as MediaUtils from "@paperbits/common/media/mediaUtils";
import { HyperlinkModel } from "@paperbits/common/permalinks";
import { LocalStyles } from "@paperbits/common/styles";
import { BackgroundModel } from "@paperbits/common/widgets/background";
import { SizeStylePluginConfig } from "@paperbits/styles/plugins/size/sizeStylePluginConfig";
import { StyleService } from "@paperbits/styles/styleService";
import { MediaItem } from "../../workshops/media/ko";
import { PictureModel } from "../pictureModel";
import { MimeTypes } from "@paperbits/common";
import { BoxStylePluginConfig, MarginStylePluginConfig } from "@paperbits/styles/plugins";
import { StyleHelper } from "@paperbits/styles";



@Component({
    selector: "picture-editor",
    template: template
})
export class PictureEditor {
    public readonly caption: ko.Observable<string>;
    public readonly sourceKey: ko.Observable<string>;
    public readonly background: ko.Observable<BackgroundModel>;
    public readonly hyperlink: ko.Observable<HyperlinkModel>;
    public readonly hyperlinkTitle: ko.Computed<string>;
    public readonly sizeConfig: ko.Observable<SizeStylePluginConfig>;
    public readonly appearanceStyles: ko.ObservableArray<any>;
    public readonly appearanceStyle: ko.Observable<any>;
    public readonly mediaFileName: ko.Observable<string>;
    public readonly boxConfig: ko.Observable<BoxStylePluginConfig>;

    constructor(
        private readonly styleService: StyleService,
        private readonly mediaService: MediaService
    ) {
        this.caption = ko.observable<string>();
        this.sourceKey = ko.observable<string>();
        this.background = ko.observable();
        this.hyperlink = ko.observable<HyperlinkModel>();
        this.hyperlinkTitle = ko.computed<string>(() => this.hyperlink() ? this.hyperlink().title : "Add a link...");
        this.sizeConfig = ko.observable<SizeStylePluginConfig>();
        this.boxConfig = ko.observable<BoxStylePluginConfig>();
        this.appearanceStyles = ko.observableArray();
        this.appearanceStyle = ko.observable();
        this.mediaFileName = ko.observable();
    }

    @Param()
    public model: PictureModel;

    @Event()
    public onChange: (model: PictureModel) => void;

    @OnMounted()
    public async initialize(): Promise<void> {
        if (this.model.sourceKey) {
            const media = await this.mediaService.getMediaByKey(this.model.sourceKey);

            if (media) {
                const background = new BackgroundModel();
                background.sourceKey = this.model.sourceKey;
                background.sourceUrl = MediaUtils.getThumbnailUrl(media);
                this.background(background);
                this.sourceKey(this.model.sourceKey);
                this.mediaFileName(media.fileName);
            }
        }

        this.caption(this.model.caption);
        this.hyperlink(this.model.hyperlink);
        this.sizeConfig({ width: this.model.width, height: this.model.height });

        const marginConfig = <MarginStylePluginConfig>StyleHelper.getPluginConfigForLocalStyles(this.model.styles, "margin");
        this.boxConfig({ margin: marginConfig });

        const variations = await this.styleService.getComponentVariations("picture");
        this.appearanceStyles(variations.filter(x => x.category === "appearance"));
        this.appearanceStyle(this.model.styles?.appearance);

        this.caption.extend(ChangeRateLimit).subscribe(this.applyChanges);
        this.sizeConfig.extend(ChangeRateLimit).subscribe(this.applyChanges);
        this.appearanceStyle.extend(ChangeRateLimit).subscribe(this.applyChanges);
    }

    public onVariationSelected(snippet: LocalStyles): void {
        if (snippet) {
            this.appearanceStyle(snippet);
        }
    }

    public applyChanges(): void {
        this.model.caption = this.caption();
        this.model.hyperlink = this.hyperlink();
        this.model.sourceKey = this.sourceKey();

        const appearanceStyle = this.appearanceStyle();

        if (appearanceStyle) {
            this.model.styles = {
                appearance: this.appearanceStyle()
            };
        }

        const marginStyle = this.boxConfig().margin;
        StyleHelper.setPluginConfigForLocalStyles(this.model.styles, "margin", marginStyle);

        this.onChange(this.model);
    }

    public async onMediaSelected(media: MediaItem): Promise<void> {
        if (!media) {
            this.background(null);
            this.sourceKey(null);
        }
        else {
            this.sourceKey(media.key);

            const background = new BackgroundModel(); // TODO: Let's use proper model here
            background.sourceKey = media.key;
            background.sourceUrl = media.thumbnailUrl();
            background.size = "contain";
            background.position = "center center";
            this.background(background);
            this.mediaFileName(media.fileName());

            // await this.updateSizeConfigForSelectedMedia(media); // TODO: Set size to media itself on upload
        }

        this.applyChanges();
    }

    public updateSizeConfigForSelectedMedia(media: MediaContract): Promise<void> {
        if (!media.downloadUrl) {
            return;
        }

        return new Promise<void>(resolve => {
            const selectedMedia = new Image();
            selectedMedia.src = media.downloadUrl;

            selectedMedia.onload = () => {
                let width = selectedMedia.width;
                let height = selectedMedia.height;

                if (media.mimeType !== MimeTypes.imageSvg) {
                    const pixelRatio = devicePixelRatio || 1;
                    width = selectedMedia.width / pixelRatio;
                    height = selectedMedia.height / pixelRatio;
                }

                this.model.width = width;
                this.model.height = height;
                this.sizeConfig({ width: width, height: height });

                resolve();
            };
        });
    }

    public onBoxUpdate(pluginConfig: BoxStylePluginConfig): void {
        this.boxConfig(pluginConfig);
        this.applyChanges();
    }

    public onHyperlinkChange(hyperlink: HyperlinkModel): void {
        this.hyperlink(hyperlink);
        this.applyChanges();
    }

    public onSizeChange(sizeConfig: SizeStylePluginConfig): void {
        this.model.width = <number>sizeConfig.width;
        this.model.height = <number>sizeConfig.height;

        this.applyChanges();
    }
}
