import { IActivationController } from './IActivationController';
import { IActivatable } from './IActivatable';
import { AsyncComponent } from './AsyncComponent';
import { ICancellation } from '../ICancellation';
import { EmptyCancellation } from '../EmptyCancellation';
import * as log from '@implab/core/log/trace!';

type Constructor<T = {}> = new (...args: any[]) => T;

function ActivatableMixin<TBase extends Constructor<AsyncComponent>>(Base: TBase) {
    return class extends Base implements IActivatable {
        _controller: IActivationController;

        _active: boolean;

        isActive() {
            return this._active;
        }

        getActivationController() {
            return this._controller;
        }

        setActivationController(controller: IActivationController) {
            this._controller = controller;
        }

        async onActivating(ct: ICancellation) {
            if (this._controller)
                await this._controller.activating(this, ct);
        }

        async onActivated(ct: ICancellation) {
            if (this._controller)
                await this._controller.activated(this, ct);
        }

        async activate(ct: ICancellation = EmptyCancellation.default) {
            if (this.isActive())
                return;
            ct = this.startOperation(ct);
            try {
                await this.onActivating(ct);
                this._active = true;
                try {
                    await this.onActivated(ct);
                } catch(e) {
                    log.error(e);
                    // TODO log error
                }
                this.completeSuccess();
            } catch (e) {
                this.completeFail(e);
            }
            return this.getCompletion();
        }

        async onDeactivating(ct: ICancellation) {
            if (this._controller)
                await this._controller.deactivating(this, ct);
        }

        async onDeactivated(ct: ICancellation) {
            if (this._controller)
                await this._controller.deactivated(this, ct);
        }

        async deactivate(ct: ICancellation = EmptyCancellation.default) {
            if (!this.isActive())
                return;
            ct = this.startOperation(ct);
            try {
                await this.onDeactivating(ct);
                this._active = false;
                try {
                    await this.onDeactivated(ct);
                } catch {
                    // TODO log error
                }
                this.completeSuccess();
            } catch (e) {
                this.completeFail(e);
            }
            return this.getCompletion();
        }

    }
}

namespace ActivatableMixin {

}

export = ActivatableMixin;