import Action from "../domain/Action";
import Question from "../domain/Question";
import Interface4ViewManager from "../application/Interface4ViewManager";
import BackgroundService from "./BackgroundService";
import Comment from "./Comment";
import EventListener from "./EventListener";
import ExplorationEvaluation from "./ExplorationEvaluation";
import PageMutationHandler from "./PageMutationHandler";
import RuleService from "./RuleService";
import State from "./State";
import StyleManager from "./StyleManager";
import ActionState from "./ActionState";

export default class TabScript {

    private _backgroundService : BackgroundService;
    private _shadowMode : boolean;
    private _ruleService : RuleService;
    private _eventListener : EventListener;
    private _styleManager : StyleManager;
    private _pageMutationHandler : PageMutationHandler;
    private _actionState: ActionState;


    constructor(backgroundService : BackgroundService, viewManagerService: Interface4ViewManager, shadowMode : boolean = false) {
        this._backgroundService = backgroundService;
        this._shadowMode = shadowMode
        this._ruleService = new RuleService();

        this._eventListener = new EventListener(this._ruleService, this._backgroundService);
        this._eventListener.addActionPushedToBackgroundListener(this.onActionPushedToBackground.bind(this));
        this._styleManager = new StyleManager(viewManagerService);

        this._pageMutationHandler = new PageMutationHandler(this.onMutation.bind(this));
        this._pageMutationHandler.init();

    }



    synchronizeWithBackground() : Promise<void> {
        return this._backgroundService.getState()
            .then(state => {
                const rules = state.webSite.mappingList.map(ru => this._ruleService.createRule(ru));
                this._ruleService.loadRules(rules);
                this._ruleService.mapRulesToElements();
                if (state.isActive) {
                    this.explorationStarted();
                }
            })
    }

    refresh(): Promise<void> {
        return this._backgroundService.getState()
            .then((state: State) => {
                if (!state.isActive) {
                    return this._styleManager.hide()
                }
                return Promise.all([
                    this.fetchActionList(),
                    this.getExplorationEvaluation(),
                    this.getCommentList(),
                ]).then(([, evaluation, commentList]) => {
                    return this._styleManager.refresh(state, this._ruleService.elementListMatchedByRule, this._actionState, evaluation, commentList)
                })
            });
    }

    getState() : Promise<State> {
        return this._backgroundService.getState();
    }

    getCommentList(): Promise<Comment[]> {
        return this._backgroundService.getCommentList();
    }

    public fetchActionList(): Promise<void> {
        return this._backgroundService.getActionList().then((actionList) => {
            const elementToActionMap = new Map<Element, Action[]>();
            for (const action of actionList) {
                action.ruleList = this._ruleService.getRuleListByAction(action);
                action.htmlElementList = this._ruleService.getHTMLElementsMatchedByAction(action);

                for (const element of action.htmlElementList) {
                    if (elementToActionMap.has(element)) {
                        elementToActionMap.get(element).push(action);
                    } else {
                        elementToActionMap.set(element, [action]);
                    }
                }
            }
            this._actionState = new ActionState(actionList, elementToActionMap)
        });
    }

    getActionList(): Action[] {
        return this._actionState.actionList;
    }

    getRuleService() : RuleService {
        return this._ruleService;
    }

    getExplorationEvaluation(): Promise<ExplorationEvaluation | null> {
        return this._backgroundService.getExplorationEvaluation().then((evaluation) => {
            if (evaluation === null) {
                return null;
            }
            for (const action of
                [ ...evaluation.enteringInteractionList,
                 ...evaluation.continuingActionList,
                 ...evaluation.finishingInteractionList ]) {
                action.ruleList = this._ruleService.getRuleListByAction(action)
                action.htmlElementList = this._ruleService.getHTMLElementsMatchedByAction(action);
            }
            return evaluation;
        });
    }

    explorationStarted() : Promise<void> {
        this._eventListener.explorationStarted();
        if (!this._shadowMode) {
            return this.refresh()
        } else {
            return Promise.resolve();
        }
    }

    explorationStopped() : void {
        this._eventListener.explorationStopped();
        if (!this._shadowMode) {
            this.refresh();
        }
    }

    reload() : Promise<void> {
        return this._backgroundService.getState()
            .then(state => {
                const rules = state.webSite.mappingList.map(ru => this._ruleService.createRule(ru));
                this._ruleService.loadRules(rules);
            })
    }

    toggleUserView(visible: boolean) :void{
        this.refresh();
    }

    commentConfirmed(comment: Comment): Promise<void> {
        return this._backgroundService.upComment(comment);
    }

    setUserViewPosition(newPosition : {x : number, y: number}) :void{
        this._backgroundService.setUserViewPosition(newPosition);
    }

    pushAnswer(question: Question, value: boolean) :void{
        this._backgroundService.sendAnswer(question, value);
    }

    private onMutation() :void{
        this._ruleService.mapRulesToElements();
        if (!this._shadowMode) {
            this.refresh();
        }
    }

    private onActionPushedToBackground() :void{
        if (!this._shadowMode) {
            this.refresh();
        }
    }

}