/**
 * 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 net.java.html.json;

import net.java.html.BrwsrCtx;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;
import org.netbeans.html.json.impl.JSON;
import org.netbeans.html.json.impl.SimpleList;
import org.netbeans.html.json.spi.Technology;

/** Information about and 
 * operations for classes generated by the {@link Model @Model}
 * annotation.
 *
 * @author Jaroslav Tulach
 */
public final class Models {
    private Models() {
    }
   
    /** Finds out whether given class is a model class - e.g. has been
     * generated by {@link Model @Model} annotation.
     * 
     * @param clazz the class to test
     * @return true, if <code>clazz</code> was generated by {@link Model} annotation
     * @since 0.2
     */
    public static boolean isModel(Class<?> clazz) {
        return JSON.isModel(clazz);
    }
    
    /** Binds given model to another context. 
     * 
     * @param <Model> class defined by {@link net.java.html.json.Model} annotation
     * @param model instance of a model defined by {@link net.java.html.json.Model} annotation
     * @param context context to which the model should be bound
     * @return new instance of model bound to new <code>context</code>
     * @throws IllegalArgumentException in case the instance is not generated by model interface
     * @since 0.4
     */
    public static <Model> Model bind(Model model, BrwsrCtx context) {
        return JSON.bindTo(model, context);
    }
    
    /** Generic method to parse content of a model class from a stream.
     * 
     * @param <M> type of the <code>model</code> class
     * @param c context of the technology to use for reading 
     * @param model the model class generated by {@link Model} annotation
     * @param is input stream with data
     * @return new instance of the model class
     * @throws java.io.IOException throw when reading from <code>is</code> faces problems
     * @since 0.2
     */
    public static <M> M parse(BrwsrCtx c, Class<M> model, InputStream is) throws IOException {
        return JSON.readStream(c, model, is, null);
    }
    
    /** Generic method to parse stream, that can possibly contain array
     * of specified objects.
     * 
     * @param <M> the type of the individal JSON object
     * @param c context of the technology to use for reading 
     * @param model the model class generated by {@link Model} annotation
     * @param is input stream with data
     * @param collectTo collection to add the individual model instances to.
     *   If the stream contains an object, one instance will be added, if
     *   it contains an array, the number of array items will be added to
     *   the collection
     * @throws IOException thrown when an I/O problem appears
     * @since 0.8.3
     */
    public static <M> void parse(
        BrwsrCtx c, Class<M> model, 
        InputStream is, Collection<? super M> collectTo
    ) throws IOException {
        collectTo.getClass();
        JSON.readStream(c, model, is, collectTo);
    }
    
    /** Converts an existing, raw, JSON object into a {@link Model model class}.
     * 
     * @param <M> the type of the model class
     * @param ctx context of the technology to use for converting
     * @param model the model class
     * @param jsonObject original instance of the JSON object
     * @return new instance of the model class
     * @since 0.7
     */
    public static <M> M fromRaw(BrwsrCtx ctx, Class<M> model, Object jsonObject) {
        M value = JSON.read(ctx, model, jsonObject);
        JSON.readBindings(ctx, value, jsonObject);
        return value;
    }
    
    /** Converts an existing {@link Model model} into its associated, raw 
     * JSON object. The object may, but does not have to, be the same instance
     * as the model object.
     * 
     * @param model the model object
     *     (can be <code>null</code> to initialize the associated {@link Technology})
     * @return the raw JSON object associated with the model
     *     (<code>null</code> if the <code>model</code> parameter was null)
     * @throws IllegalArgumentException if the <code>model</code> is 
     *    not instance of a class
     *    generated by {@link Model model annotation} processor.
     * @since 0.7
     */
    public static Object toRaw(Object model) {
        if (model == null) {
            toRaw(FakeModel.create());
            return null;
        }
        final Class<? extends Object> type = model.getClass();
        if (!isModel(type)) {
            throw new IllegalStateException("Not a model " + type);
        }
        return JSON.find(model);
    }
    
    /** Apply bindings of a model class to overall page. In <em>ko4j</em> mode,
     * it binds the model values to the currently active page. 
     * 
     * @param model instance of a {@link Model class}
     * @throws IllegalArgumentException if the <code>model</code> is not
     * instance of a class generated by {@link Model model annotation}
     * processor.
     * 
     * @since 0.7
     */
    public static void applyBindings(Object model) {
        JSON.applyBindings(model, null);
    }
    
    
    /** Apply bindings of a model class. In <em>ko4j</em> mode,
     * it binds the model values to an element on currently active page.
     * 
     * @param model instance of a {@link Model class}
     * @param targetId the id of the element to apply the binding to 
     * @throws IllegalArgumentException if the <code>model</code> is not
     * instance of a class generated by {@link Model model annotation}
     * processor.
     * 
     * @since 1.1
     */
    public static void applyBindings(Object model, String targetId) {
        JSON.applyBindings(model, targetId);
    }

    /** Wrap provided values into mutable list.
     *
     * @param <T> type of the values and resulting list
     * @param values the values, if any
     * @return full features implementation of mutable and extendable list
     * @since 1.5
     */
    public static <T> List<T> asList(T... values) {
        return SimpleList.asList(values);
    }
}
