/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.netbeans.html.json.spi;

import net.java.html.BrwsrCtx;
import net.java.html.json.Model;
import net.java.html.json.Models;
import org.netbeans.html.context.spi.Contexts.Id;

/** *  An implementation of a binding between model classes (see {@link Model})
 * and particular technology like <a href="http://knockoutjs.com">knockout.js</a>
 * in a browser window, etc.Since introduction of {@link Id technology identifiers} one can choose between
 different background implementations to handle the conversion and
 communication requests.
 * The currently known provider is
 <code>org.netbeans.html:ko4j</code> module which registers 
 * a <a href="http://knockoutjs.com" target="_blank">knockout.js</a>
 * implementation called <b>ko4j</b>.
 *
 * @param <Data> technology internal type that keeps internal data for each
 *    instance of {@linkplain Model model class}.
 *
 * @author Jaroslav Tulach
 */
public interface Technology<Data> {
    /** Creates an object to wrap the provided model object. The model
     * has previously been generated by annotation processor associated 
     * with {@link Model} annotation.
     * 
     * @param model the model generated from {@link Model}
     * @return internal object representing the model
     */
    public Data wrapModel(Object model);
    
    /** Converts an element potentially representing a model into the model.
     * @param <M> the type of the <code>modelClass</code>
     * @param modelClass expected class to convert the data to
     * @param data the current data provided from the browser
     * @return the instance of modelClass somehow extracted from the data, may return <code>null</code>
     */
    public <M> M toModel(Class<M> modelClass, Object data);
    
    /** Binds a property between the model and the data as used by the technology.
     * 
     * @param b the description of the requested binding
     * @param model the original instance of the model
     * @param data the data to bind with the model
     */
    public void bind(PropertyBinding b, Object model, Data data);

    /** Model for given data has changed its value. The technology is
     * supposed to update its state (for example DOM nodes associated
     * with the model). The update usually happens asynchronously.
     * 
     * @param data technology's own representation of the model
     * @param propertyName name of the model property that changed
     */
    public void valueHasMutated(Data data, String propertyName);

    public void expose(FunctionBinding fb, Object model, Data d);
    
    /** Applies given data to current context (usually an HTML page).
     * @param data the data to apply
     */
    public void applyBindings(Data data);
    
    /**
     * Some technologies may require wrapping a Java array into a special
     * object. In such case they may return it from this method.
     *
     * @param arr original array
     * @return wrapped array
     */
    public Object wrapArray(Object[] arr);
    
    /** 
     * Run given runnable in a safe mode. If the runnable can be executed
     * immediately, do it. If we need to switch to some other thread, do it
     * and invoke r asynchronously immediately returning from the call to
     * runSafe method.
     * 
     * @param r the runnable to execute
     * @deprecated Use {@link BrwsrCtx#execute(java.lang.Runnable)}
     */
    @Deprecated
    public void runSafe(Runnable r);

    /** For certain rendering technologies it may be more efficient to register
     * property and function bindings for one instance of the model at once, 
     * rather then doing it incrementally via 
     * {@link Technology#expose(org.netbeans.html.json.spi.FunctionBinding, java.lang.Object, java.lang.Object) }
     * and 
     * {@link Technology#bind(org.netbeans.html.json.spi.PropertyBinding, java.lang.Object, java.lang.Object) }.
     * In such case implement the {@link #wrapModel(java.lang.Object, org.netbeans.html.json.spi.PropertyBinding[], org.netbeans.html.json.spi.FunctionBinding[]) }
     * method of this interface and it will be called instead of the 
     * previous two ones.
     * 
     * @since 0.6
     */
    public static interface BatchInit<D> extends Technology<D> {
        /** Wrap the given model into rendering technology appropriate object
         * <code>D</code> and expose given properties and functions on it.
         * 
         * @param model the {@link Models#isModel(java.lang.Class) model} in Java
         * @param propArr array of property bindings to expose
         * @param funcArr array of functions to expose
         * @return appropriate wrapper around the model
         */
        public D wrapModel(Object model, PropertyBinding[] propArr, FunctionBinding[] funcArr);
    }

    /** Some technologies are more effective when number of calls between
     * Java and JavaScript is limited - to do that when a value of property
     * is changed they should implement this additional interface.
     * 
     * @param <D> internal type of the technology
     * @since 0.7.6
     */
    public static interface ValueMutated<D> extends Technology<D> {
        /** Model for given data has changed its value. The technology is
         * supposed to update its state (for example DOM nodes associated
         * with the model). The update usually happens asynchronously.
         * <p>
         * If both <code>oldValue</code> and <code>newValue</code> are 
         * <code>null</code> then the real value of the technology is
         * not known.
         * <p>
         * If this method is present, then it is called instead of 
         * old, plain {@link #valueHasMutated(java.lang.Object, java.lang.String)}
         * which is never called by the infrastructure then.
         * 
         * @param data technology's own representation of the model
         * @param propertyName name of the model property that changed
         * @param oldValue provides previous value of the property
         * @param newValue provides new value of the property
         */
        public void valueHasMutated(D data, String propertyName, Object oldValue, Object newValue);
    }
    
    /** Apply technology bindings at selected subtree of the HTML page.
     * Can be accessed via {@link Proto#applyBindings(java.lang.String)} or
     * via method <code>applyBindings(String)</code> generated when one
     * is using the {@link Model} annotation.
     * 
     * @param <D> the internal data for the technology
     * @since 1.1
     */
    public static interface ApplyId<D> extends Technology<D> {
        /** Applies given data to current context (usually an element on an 
         * HTML page).
         * 
         * @param id the id of an element to apply the data to
         * @param data the data to apply
         */
        public void applyBindings(String id, D data);
    }

    /** Extension of {@link BatchInit} with enhanced support for
     * copying values. Technologies that support this interface provide a
     * guarantee that result {@link Models#toRaw(java.lang.Object)}
     * wrapped by {@link Models#fromRaw(net.java.html.BrwsrCtx, java.lang.Class, java.lang.Object)}
     * will share essential properties (and not just values) of the original object.
     *
     * @since 1.3
     */
    public static interface BatchCopy<D> extends Technology<D> {
        /** Wrap the given model into rendering technology appropriate object
         * <code>D</code> and expose given properties and functions on it.
         *
         * @param model the {@link Models#isModel(java.lang.Class) model} in Java
         * @param copyFrom the object to copy data from
         *      (expectably of type D, but that isn't guaranteed) or <code>null</code>
         * @param propArr array of property bindings to expose
         * @param funcArr array of functions to expose
         * @return appropriate wrapper around the model
         */
        public D wrapModel(Object model, Object copyFrom, PropertyBinding[] propArr, FunctionBinding[] funcArr);
    }

    /** Convertor of the internal data type to object suitable as a JavaScript
     * representation. Certain technologies need to keep some data in Java
     * and only part of them in JavaScript-ready object. With the help of
     * {@code ToJavaScript} interface, they can parametrize their
     * {@link Technology} with the Java type and implement
     * {@link #toJavaScript(java.lang.Object)}
     * method to extract the proper JavaScript part from that object.
     *
     * @param <D> the internal data type
     * @since 1.5.1
     */
    public static interface ToJavaScript<D> extends Technology<D> {
        /** Extracts JavaScript ready representation.
         *
         * @param data technology's internal data structure
         * @return object ready to represent the data in JavaScript
         * @since 1.5.1
         */
        public Object toJavaScript(D data);
    }
}
