import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'
import { IconDefinition, library } from "@fortawesome/fontawesome-svg-core"
import { Dictionary } from "@/utils";
import {
    faBullhorn,
    faCheck,
    faCheckDouble,
    faChevronCircleUp,
    faChevronDown,
    faChevronUp,
    faClock,
    faCog,
    faExclamationTriangle,
    faHistory,
    faHourglassHalf,
    faInfoCircle,
    faMapMarkerAlt,
    faMoon,
    faQuestionCircle,
    faQuestionSquare,
    faSearch,
    faSign,
    faStar,
    faSync,
    faTimes,
    faTrashAlt
} from "@fortawesome/pro-light-svg-icons";
import {
    faClock as faClockBold,
    faCodeCommit,
    faMinus,
    faPlus,
    faSpinnerThird
} from "@fortawesome/pro-regular-svg-icons";
import { faExclamationTriangle as faSolidExclamationTriangle, faWalking } from "@fortawesome/pro-solid-svg-icons";
import { fac } from "@/icons";
import { FontAwesomeIcon, FontAwesomeLayers, FontAwesomeLayersText } from "@fortawesome/vue-fontawesome";

type IconDescription = { icon: IconDefinition, [prop: string]: any }

type SimpleIcon = {
    type: 'simple',
} & IconDescription;

type StackedIcon = {
    type: 'stacked',
    icons: IconDescription[],
}

export type Icon = SimpleIcon | StackedIcon;

const simple = (icon: IconDefinition, props: any = {}): SimpleIcon => ({
    icon, ...props, type: "simple"
});

const stack = (icons: IconDescription[]): StackedIcon => ({ type: "stacked", icons });

const lineTypeIcons = Object
    .values(fac)
    .map<[string, Icon]>(icon => [`line-${ icon.iconName }`, simple(icon)])
    .reduce((acc, [icon, definition]) => ({ ...acc, [icon]: definition }), {})

const messageTypeIcons: Dictionary<Icon> = {
    'message-breakdown': simple(faExclamationTriangle),
    'message-info': simple(faInfoCircle),
    'message-unknown': simple(faQuestionCircle),
};

const definitions = {
    'favourite': simple(faStar),
    'unknown': simple(faQuestionSquare),
    'add': simple(faCheck),
    'add-all': simple(faCheckDouble),
    'remove-stop': simple(faTimes),
    'delete': simple(faTrashAlt),
    'messages': simple(faBullhorn),
    'timetable': simple(faClock),
    'settings': simple(faCog),
    'refresh': simple(faSync),
    'chevron-down': simple(faChevronDown),
    'chevron-up': simple(faChevronUp),
    'search': simple(faSearch),
    'info': simple(faInfoCircle),
    'warning': simple(faExclamationTriangle),
    'night': simple(faMoon),
    'fast': simple(faWalking),
    'track': simple(faCodeCommit),
    'info-hide': simple(faChevronCircleUp),
    'map': simple(faMapMarkerAlt),
    'stop': simple(faSign),
    'spinner': simple(faSpinnerThird, { spin: true }),
    'increment': simple(faPlus, { "fixed-width": true }),
    'decrement': simple(faMinus, { "fixed-width": true }),
    'relative-time': simple(faHourglassHalf),
    'departure-warning': stack([
        { icon: faClockBold },
        { icon: faSolidExclamationTriangle, transform: "shrink-5 down-4 right-6" }
    ]),
    'close': simple(faTimes),
    'history': simple(faHistory),
    ...lineTypeIcons,
    ...messageTypeIcons,
};

export type PredefinedIcon = keyof typeof definitions;

const extractAllIcons = (icons: Icon[]) => icons.map(icon => {
    switch (icon.type) {
        case "simple":
            return [icon.icon];
        case "stacked":
            return icon.icons.map(stacked => stacked.icon);
    }
}).reduce((acc, cur) => [...acc, ...cur]);

library.add(...extractAllIcons(Object.values(definitions)));

@Component({
    template: require('@templates/ui/icon.html'),
    components: {
        fa: FontAwesomeIcon,
        faLayers: FontAwesomeLayers,
        faText: FontAwesomeLayersText,
    }
})
export class UiIcon extends Vue {
    @Prop({
        type: [String, Object],
        validator: value => typeof value === "object" || Object.keys(definitions).includes(value),
        required: true,
    })
    icon: PredefinedIcon | IconDefinition;

    get definition(): Icon {
        return typeof this.icon === "string"
            ? definitions[this.icon] || definitions['unknown']
            : { icon: this.icon as IconDefinition, type: "simple" };
    }

    get attrs() {
        return { ...this.definition, ...this.$attrs };
    }

    get type() {
        return this.definition.type;
    }
}

Vue.component('UiIcon', UiIcon);
