/**
 * 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 java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.net.URL;
import java.util.List;

/** Defines a model class that represents a single 
 * <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a>-like object
 * named {@link #className()}. The generated class contains
 * getters and setters for properties defined via {@link #properties()} and
 * getters for other, derived properties defined by annotating methods
 * of this class by {@link ComputedProperty}. Each property
 * can be of primitive type, an {@link Enum enum type} or (in order to create 
 * nested <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a> structure)
 * of another {@link Model class generated by @Model} annotation. Each property
 * can either represent a single value or be an array of its values.
 * <p>
 * The {@link #className() generated class}'s <code>toString</code> method
 * converts the state of the object into 
 * <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a> format. One can
 * use {@link Models#parse(net.java.html.BrwsrCtx, java.lang.Class, java.io.InputStream)}
 * method to read the JSON text stored in a file or other stream back into the Java model. 
 * One can also use {@link OnReceive @OnReceive} annotation to read the model
 * asynchronously from a {@link URL}.
 * <p>
 * An example where one defines class <code>Person</code> with four
 * properties (<code>firstName</code>, <code>lastName</code>, array of <code>addresses</code> and
 * <code>fullName</code>) follows:
 * <pre>
 * {@link Model @Model}(className="Person", properties={
 *   {@link Property @Property}(name = "firstName", type=String.<b>class</b>),
 *   {@link Property @Property}(name = "lastName", type=String.<b>class</b>)
 *   {@link Property @Property}(name = "addresses", type=Address.<b>class</b>, array = <b>true</b>)
 * })
 * <b>static class</b> PersonModel {
 *   {@link ComputedProperty @ComputedProperty}
 *   <b>static</b> String fullName(String firstName, String lastName) {
 *     <b>return</b> firstName + " " + lastName;
 *   }
 * 
 *   {@link ComputedProperty @ComputedProperty}
 *   <b>static</b> String mainAddress({@link List List&lt;Address&gt;} addresses) {
 *     <b>for</b> (Address a : addresses) {
 *       <b>return</b> a.getStreet() + " " + a.getTown();
 *     }
 *     <b>return</b> "No address";
 *   }
 * 
 *   {@link Model @Model}(className="Address", properties={
 *     {@link Property @Property}(name = "street", type=String.<b>class</b>),
 *     {@link Property @Property}(name = "town", type=String.<b>class</b>)
 *   })
 *   <b>static class</b> AddressModel {
 *   }
 * }
 * </pre>
 * The generated model class has a default constructor, and also <em>quick
 * instantiation</em> constructor that accepts all non-array properties 
 * (in the order used in the {@link #properties()} attribute) and vararg list
 * for the first array property (if any). One can thus use following code
 * to create an instance of the Person and Address classes:
 * <pre>
 * Person p = <b>new</b> Person("Jaroslav", "Tulach",
 *   <b>new</b> Address("Markoušovice", "Úpice"),
 *   <b>new</b> Address("V Parku", "Praha")
 * );
 * // p.toString() then returns equivalent of following <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a> object
 * {
 *   "firstName" : "Jaroslav",
 *   "lastName" : "Tulach",
 *   "addresses" : [
 *     { "street" : "Markoušovice", "town" : "Úpice" },
 *     { "street" : "V Parku", "town" : "Praha" },
 *   ]
 * }
 * </pre>
 * <p>
 * In case you are using <a target="_blank" href="http://knockoutjs.com/">Knockout technology</a>
 * for Java then you can associate such model object with surrounding HTML page by
 * calling: <code>p.applyBindings();</code> (in case you specify {@link #targetId()}. 
 * The page can then use regular
 * <a target="_blank" href="http://knockoutjs.com/">Knockout</a> bindings to reference your
 * model and create dynamic connection between your model in Java and 
 * live DOM structure in the browser:
 * </p>
 * <pre>
 * Name: &lt;span data-bind="text: fullName"&gt;
 * &lt;div data-bind="foreach: addresses"&gt;
 *   Lives in &lt;span data-bind="text: town"/&gt;
 * &lt;/div&gt;
 * </pre>
 *
 * <p>
 * <b>Access Raw <a target="_blank" href="http://knockoutjs.com/">Knockout</a> Observables</b>
 * </p>
 *
 * <p>
 * One can obtain <em>raw</em> JavaScript object representing the 
 * instance of {@link Model model class} (with appropriate
 * <a target="_blank" href="http://knockoutjs.com/">Knockout</a> <b>observable</b> properties)
 * by calling {@link Models#toRaw(java.lang.Object) Models.toRaw(p)}. For 
 * example here is a way to obtain the value of <code>fullName</code> property
 * (inefficient as it switches between Java and JavaScript back and forth, 
 * but functional and instructive) via a JavaScript call:
 * </p>
 * <pre>
 * {@link net.java.html.js.JavaScriptBody @JavaScriptBody}(args = "raw", javacall = true, body =
 *   "return raw.fullName();" // yes, the <a target="_blank" href="http://knockoutjs.com/">Knockout</a> property is a function
 * )
 * static native String jsFullName(Object raw);
 * // and later
 * Person p = ...;
 * String fullName = jsFullName({@link Models#toRaw(java.lang.Object) Models.toRaw(p)});
 * </pre>
 * <p>
 * The above shows how to read a value from <a target="_blank" href="http://knockoutjs.com/">Knockout</a>
 * observable. There is a way to change the value too:
 * One can pass a parameter to the property-function and then
 * it acts like a setter (of course not in the case of read only <code>fullName</code> property,
 * but for <code>firstName</code> or <code>lastName</code> the setter is
 * available). Everything mentioned in this paragraph applies only when 
 * <a target="_blank" href="http://knockoutjs.com/">Knockout</a> technology is active
 * other technologies may behave differently.
 * </p>
 *
 * <p>
 * <b>Copy to Plain JSON</b>
 * </p>
 *
 * <p>
 * There is a way to pass a value of a Java {@link Model model class} instance 
 * by copy and convert 
 * the {@link Model the whole object} into plain 
 * <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a>. Just
 * print it as a string and parse it in JavaScript:
 * </p>
 * <pre>
 * {@link net.java.html.js.JavaScriptBody @JavaScriptBody}(args = { "txt" }, body =
 *   "return JSON.parse(txt);"
 * )
 * private static native Object parseJSON(String txt);
 * 
 * public static Object toPlainJSON(Object model) {
 *   return parseJSON(model.toString());
 * }
 * </pre>
 * <p>
 * The newly returned instance is a one time copy of the original model and is no longer
 * connected to it. The copy based behavior is independent on any 
 * particular technology and should work
 * in <a target="_blank" href="http://knockoutjs.com/">Knockout</a> as well as other
 * technology implementations.
 * </p>
 * 
 * <p>
 * <b>References</b>
 * </p>
 * 
 * Visit an <a target="_blank" href="http://dew.apidesign.org/dew/#7510833">on-line demo</a>
 * to see a histogram driven by the {@link Model} annotation or try 
 * a little <a target="_blank" href="http://dew.apidesign.org/dew/#7263102">math test</a>.
 *
 * @author Jaroslav Tulach
 */
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface Model {
    /** Name of the model class.
     * @return valid Java identifier to use as a name of the model class
     */
    String className();
    /** List of properties in the model.
     * @return array of property definitions
     */
    Property[] properties();
    
    /** The id of an element to bind this model too. If this
     * property is specified an <code>applyBindings</code> method
     * in the model class is going to be generated which later calls
     * {@link Models#applyBindings(java.lang.Object, java.lang.String)}
     * with appropriate <code>targetId</code>. If the <code>targetId</code>
     * is specified as empty string, <code>null</code> value is passed
     * to {@link Models#applyBindings(java.lang.Object, java.lang.String)} method.
     * If the <code>targetId</code> is not specified at all, no public
     * <code>applyBindings</code> method is generated at all (a change compared
     * to previous versions of this API).
     * 
     * @return an empty string (means apply globally), or ID of a (usually DOM)
     *    element to apply this model after calling its generated
     *    <code>applyBindings()</code> method to
     * @since 1.1
     */
    String targetId() default "";

    /** Controls whether builder-like setters shall be generated. Once this
     * attribute is set, all {@link #properties()} will get a builder like
     * setter (takes value of the property and returns <code>this</code>
     * so invocations can be chained). When this attribute is specified,
     * the non-default constructor isn't generated at all.
     * <p>
     * Specifying <code>builder="assign"</code>
     * and having {@link #properties() properties} <code>name</code> and
     * <code>age</code> will generate method: <pre>
     * <b>public</b> MyModel assignName(String name) { ... }
     * <b>public</b> MyModel assignAge(int age) { ... }
     * </pre>
     * These methods can then be chained as <pre>
     * MyModel m = <b>new</b> MyModel().assignName("name").assignAge(3);
     * </pre>
     * The <code>builder</code> attribute can be set to empty string <code>""</code> -
     * then it is possible that some property names clash with Java keywords.
     * It's responsibility of the user to specify valid builder prefix,
     * so the generated methods are compilable.
     *
     * @return the prefix to put before {@link Property property} names when
     *   generating their builder methods
     * @since 1.3
     */
    String builder() default "";
    
    /** Controls keeping of per-instance private state. Sometimes
     * the class generated by the {@link Model @Model annotation} needs to
     * keep non-public, or non-JSON like state. One can achieve that by
     * specifying <code>instance=true</code> when using the annotation. Then
     * the generated class gets a pointer to the instance of the annotated
     * class (there needs to be default constructor) and all the {@link ModelOperation @ModelOperation},
     * {@link Function @Function}, {@link OnPropertyChange @OnPropertyChange}
     * and {@link OnReceive @OnReceive} methods may be non-static. The
     * instance of the implementation class isn't accessible directly, just
     * through calls to above defined (non-static) methods. Example:
     * <pre>
     * {@link Model @Model}(className="Data", instance=<b>true</b>, properties={
     *   {@link Property @Property}(name="message", type=String.<b>class</b>)
     * })
     * <b>final class</b> DataPrivate {
     *   <b>private int</b> count;
     * 
     *   {@link ModelOperation @ModelOperation} <b>void</b> increment(Data model) {
     *     count++;
     *   }
     * 
     *   {@link ModelOperation @ModelOperation} <b>void</b> hello(Data model) {
     *     model.setMessage("Hello " + count + " times!");
     *   }
     * }
     * Data data = <b>new</b> Data();
     * data.increment();
     * data.increment();
     * data.increment();
     * data.hello();
     * <b>assert</b> data.getMessage().equals("Hello 3 times!");
     * </pre>
     * <p>
     * The methods annotated by {@link ComputedProperty} need to remain static, as 
     * they are supposed to be <em>pure</em> functions (e.g. depend only on their parameters)
     * and shouldn't use any internal state.
     * </p>
     * <p><b>How do I initialize private values?</b>
     * The implementation class (the one annotated by {@link Model @Model} annotation) 
     * needs to have accessible default constructor. That constructor is used to 
     * create the instance. Obviously such constructor does not have 
     * any parameters, so no initialization is possible.
     * </p>
     * <p>
     * Later one can, however, call any {@link ModelOperation @ModelOperation}
     * method and pass in additional configuration parameters. In the above 
     * example it should be possible add
     * </p>
     * <pre>
     * {@link ModelOperation @ModelOperation} <b>void</b> init(Data model, int count) {
     *   <b>this</b>.count = count;
     * }
     * </pre><p>
     * and then one can initialize the model using the <code>init</code> as:
     * </p>
     * <pre>
     * Data data = <b>new</b> Data();
     * data.init(2);
     * data.increment();
     * data.hello();
     * <b>assert</b> data.getMessage().equals("Hello 3 times!");
     * </pre><p>
     * Why there has to be default constructor? Because instances of 
     * classes generated by {@link Model @Model annotation} may be constructed
     * by the system as 
     * {@link Models#fromRaw(net.java.html.BrwsrCtx, java.lang.Class, java.lang.Object) wrappers around existing JavaScript objects}
     * - then
     * there is nobody to provide additional parameters at construction time.
     * </p>
     * <p><b>How do I read private values?</b>
     * The methods annotated by {@link ModelOperation} must return <code>void</code>
     * (as they can run asynchronously) and as such they aren't suitable for
     * returning values back to the caller. In case something like that is
     * needed, one can use the approach of the <code>hello</code> method - e.g.
     * set value of some {@link Property property} that has a getter:
     * </p>
     * <pre>
     * data.hello();
     * <b>assert</b> data.getMessage().equals("Hello 3 times!");
     * </pre><p>
     * Or one can use actor-like callbacks. Define callback interface and
     * use it in a {@link ModelOperation @ModelOperation} method:
     * </p>
     * <pre>
     * <b>public interface</b> ReadCount {
     *   <b>public void</b> notifyCount(int count);
     * }
     * {@link ModelOperation @ModelOperation} <b>void</b> readCount(Data model, ReadCount callback) {
     *   callback.readCount(<b>this</b>.count);
     * }
     * Data data = <b>new</b> Data();
     * data.init(2);
     * data.increment();
     * data.readCount(count -&gt; System.out.println("count should be 3: " + count));
     * </pre><p>
     * The provided lambda-function callback may be invoked immediately 
     * or asynchronously as documentation for {@link ModelOperation} 
     * annotation describes.
     * </p>
     * 
     * @return <code>true</code> if the model class should keep pointer to
     *   instance of the implementation class
     * @since 1.3
     */
    boolean instance() default false;
}
