import viewModule = require("ui/core/view");
import observable = require("ui/core/dependency-observable");
import styleProperty = require("ui/styling/style-property");
import cssSelector = require("ui/styling/css-selector");
import * as visualStateConstants from "ui/styling/visual-state-constants";

export class VisualState {
    private _setters: {}; // use css selector instead
    private _animatedSelectors: cssSelector.CssVisualStateSelector[];

    constructor() {
        this._setters = {};
        this._animatedSelectors = [];
    }

    get setters(): {} {
        return this._setters;
    }

    get animatedSelectors(): cssSelector.CssVisualStateSelector[] {
        return this._animatedSelectors;
    }
}

/**
 *
 */
export function goToState(view: viewModule.View, state: string): string {
    let root = <any>view.page;
    if (!root) {
        return undefined;
    }

    //TODO: this of optimization
    let allStates = root._getStyleScope().getVisualStates(view);
    if (!allStates) {
        return undefined;
    }

    // logic here is:
    // 1. Verify the new state, whether we have setters for it and rollback to Normal(default) if not.
    // 2. If the new visual state is the same as the current one, do nothing
    // 3. Else, remove the visual state value for all the properties from the current state that are not present in the new one
    // 4. Apply all the properties from the new state.

    // Step 1
    if (!(state in allStates)) {
        // TODO: Directly go to normal?
        state = visualStateConstants.Normal;
    }

    // Step 2
    if (state !== view.visualState) {
        let newState: VisualState = allStates[state];
        let oldState: VisualState = allStates[view.visualState];

        // Step 3
        resetProperties(view, oldState, newState);

        // Step 4
        applyProperties(view, newState);
    }

    return state;
}

function resetProperties(view: viewModule.View, oldState: VisualState, newState: VisualState) {
    if (!oldState) {
        return;
    }

    let property: styleProperty.Property;

    for (let name in oldState.setters) {
        if (newState && (name in newState.setters)) {
            // Property will be altered by the new state, no need to reset it.
            continue;
        }

        property = styleProperty.getPropertyByName(name);
        if (property) {
            view.style._resetValue(property, observable.ValueSource.VisualState);
        }
    }

    view._unregisterAllAnimations();

    for (let selector of oldState.animatedSelectors) {
        for (let animationInfo of selector.animations) {
            for (let keyframe of animationInfo.keyframes) {
                for (let declaration of keyframe.declarations) {
                    property = styleProperty.getPropertyByName(declaration.property);
                    if (property) {
                        view.style._resetValue(property, observable.ValueSource.VisualState);
                    }
                }
            }
        }
    }
}

function applyProperties(view: viewModule.View, state: VisualState) {
    if (!state) {
        return;
    }

    let property: styleProperty.Property;

    for (let name in state.setters) {
        property = styleProperty.getPropertyByName(name);
        if (property) {
            view.style._setValue(property, state.setters[name], observable.ValueSource.VisualState);
        }
    }

    for (let selector of state.animatedSelectors) {
        selector.apply(view, observable.ValueSource.VisualState);
    }
}
