import Layer from 'ol/layer/Layer';
import LayerGroup from 'ol/layer/Group';
import Geometry from 'ol/geom/Geometry';
import LayerUtils from '../utils/LayerUtils';
import VectorLayer from 'ol/layer/Vector';
import TemporalImageLayer from '../model/TemporalImageLayer';

const iconColorDefault = 'white';
const iconColorActive = 'primary';

export class DemapLayerActionBase {
    public actionId = '';
    public label = '';
    public icon = ''
    public iconColor = iconColorDefault;
    public tooltip = '';
    public isGroup = false;

    /**
     * predicate whether this action is allowed/valid for the given input
     */
    public predicate!: undefined | ((actionId: string, layer: Layer, group: LayerGroup) => boolean);

    public isAllowed(layer: Layer, group: LayerGroup): boolean {
        if (this.predicate) {
            return this.predicate(this.actionId, layer, group);
        }

        return true;
    }
}

export class DemapLayerActionGroup extends DemapLayerActionBase {
    public actions: DemapLayerAction[] = [];
    public attribution: string = undefined;

    public constructor() {
        super();
        this.isGroup = true;
    }
}

export class DemapLayerAction extends DemapLayerActionBase {
    public tooltipDefault = '';
    public tooltipActive = '';

    /** 
     * executed before the action 
     */
    public beforeAction!: undefined | ((actionId: string, layer: Layer, group: LayerGroup) => void);
    public action!: (action: DemapLayerAction, layer: Layer, group: LayerGroup) => void;
    /**
     * executed after the action
     */
    public afterAction!: undefined | ((actionId: string, layer: Layer, group: LayerGroup) => void);

    public trigger(layer: Layer, group: LayerGroup): void {
        if (this.beforeAction) {
            this.beforeAction(this.actionId, layer, group);
        }
        if (this.action) {
            this.action(this, layer, group);
        }
        if (this.afterAction) {
            this.afterAction(this.actionId, layer, group);
        }
    }


}

let setActionProperty = (action: DemapLayerAction, layer: Layer, group: LayerGroup): void => {
    if (layer.get(action.actionId)) {
        // unset
        layer.set(action.actionId, false);
        action.iconColor = iconColorDefault;
        action.tooltip = action.tooltipDefault;
    } else {
        // set
        layer.set(action.actionId, true);
        action.iconColor = iconColorActive;
        action.tooltip = action.tooltipActive;
    }
}


export function createRoiAction(
    beforeAction?: (actionId: string, layer: Layer, group: LayerGroup) => void,
    afterAction?: (actionId: string, layer: Layer, group: LayerGroup) => void): DemapLayerAction {
    let roiAction: DemapLayerAction = new DemapLayerAction();
    roiAction.actionId = 'Roi';
    roiAction.label = 'Roi';
    roiAction.icon = 'public';
    roiAction.tooltipDefault = 'Set as Region of Interest';
    roiAction.tooltip = roiAction.tooltipDefault;
    roiAction.tooltipActive = 'Unset as Region of Interest';
    roiAction.beforeAction = beforeAction;
    roiAction.action = setActionProperty;
    roiAction.afterAction = afterAction;
    roiAction.predicate = (actionId: string, layer: Layer, group: LayerGroup): boolean => {
        return LayerUtils.containsGeometry(layer);
    }

    return roiAction;
}



export function createDefaultRoiAction(setRoi: (roiGeometry: Geometry) => void, unsetRoi: () => void): DemapLayerAction {
    return createRoiAction(
        // before action
        undefined,
        // after action
        (actionId: string, layer: Layer, group: LayerGroup): void => {
            if (layer.get(actionId)) {
                let roiGeometry: Geometry = LayerUtils.extractGeometryFromLayer(layer);
                window['roi'] = roiGeometry;
                setRoi(roiGeometry);
            } else {
                window['roi'] = undefined;
                unsetRoi();
            }
        }
    );
}

export function createEditAction(
    beforeAction?: (actionId: string, layer: Layer, group: LayerGroup) => void,
    afterAction?: (actionId: string, layer: Layer, group: LayerGroup) => void): DemapLayerAction {
    let roiAction: DemapLayerAction = new DemapLayerAction();
    roiAction.actionId = 'Edit';
    roiAction.label = 'Edit';
    roiAction.icon = 'edit';
    roiAction.tooltipDefault = 'Draw & Modify';
    roiAction.tooltip = roiAction.tooltipDefault;
    roiAction.tooltipActive = 'Stop Draw & Modify';
    roiAction.beforeAction = beforeAction;
    roiAction.action = setActionProperty;
    roiAction.afterAction = afterAction;

    return roiAction;
}


export function createDefaultEditAction(startEditLayer: (layer: Layer) => void, stopEditLayer: (layer: Layer) => void): DemapLayerAction {
    return createEditAction(
        // before action
        undefined,
        // after action
        (actionId: string, layer: Layer, group: LayerGroup): void => {
            if (layer.get(actionId)) {
                startEditLayer(layer);
            } else {
                stopEditLayer(layer);

                if (layer.get('Roi')) {
                    // this layer is a roi layer, since we just edited this layer, update the roi as well
                    // extract the roi layer action from the layer and trigger the after action
                    let actions: DemapLayerAction[] = layer.get('actions');
                    for (let action of actions) {
                        if (action.actionId == 'Roi') {
                            action.afterAction(action.actionId, layer, group);
                        }
                    }
                }
            }
        }
    );
}


export function createRemoveAction(
    beforeAction?: (actionId: string, layer: Layer, group: LayerGroup) => void,
    afterAction?: (actionId: string, layer: Layer, group: LayerGroup) => void): DemapLayerAction {
    let removeAction: DemapLayerAction = new DemapLayerAction();
    removeAction.actionId = 'Remove';
    removeAction.label = 'Remove';
    removeAction.icon = 'delete';
    removeAction.tooltip = 'Remove Layer';
    removeAction.beforeAction = beforeAction;
    removeAction.action = (action: DemapLayerAction, layer: Layer, group: LayerGroup): void => {
        group.getLayers().remove(layer);
    }
    removeAction.afterAction = afterAction;

    return removeAction;
}

export function createAddLayerAction(
    onAddLayer: (group: LayerGroup) => void): DemapLayerAction {
    let addLayerAction: DemapLayerAction = new DemapLayerAction();
    addLayerAction.actionId = 'AddLayer';
    addLayerAction.label = 'AddLayer';
    addLayerAction.icon = 'add_box';
    addLayerAction.tooltip = 'Add new Layer';
    addLayerAction.action = (action: DemapLayerAction, layer: Layer, group: LayerGroup): void => {
        onAddLayer(group);
    }

    return addLayerAction;
}


export function createSplitAction(onSplitLayer: (layer: Layer, group: LayerGroup) => void): DemapLayerAction {
    let splitLayerAction: DemapLayerAction = new DemapLayerAction();
    splitLayerAction.actionId = 'Split';
    splitLayerAction.label = 'Split';
    splitLayerAction.icon = 'call_split';
    splitLayerAction.tooltip = 'Split layer properties into individual layers';
    splitLayerAction.action = (action: DemapLayerAction, layer: Layer, group: LayerGroup): void => {
        onSplitLayer(layer, group);
    }

    return splitLayerAction;
}

export function createDefaultSplitAction(): DemapLayerAction {
    return createSplitAction(LayerUtils.splitIntoPropertyLayers);
}


export function createDownloadAction(onDownloadLayerAction: (layer: Layer) => void): DemapLayerAction {
    let downloadAction: DemapLayerAction = new DemapLayerAction();
    downloadAction.actionId = 'Download';
    downloadAction.label = 'Download';
    downloadAction.icon = 'download';
    downloadAction.tooltip = 'Download Layer';
    downloadAction.action = (action: DemapLayerAction, layer: Layer, group: LayerGroup): void => {
        onDownloadLayerAction(layer);
    }
    downloadAction.predicate = (actionId: string, layer: Layer, group: LayerGroup): boolean => {
        return layer instanceof VectorLayer || layer instanceof TemporalImageLayer;
    }

    return downloadAction;
}

export function createDefaultDownloadAction(): DemapLayerAction {
    return createDownloadAction(LayerUtils.downloadLayer);
}