/*
 *  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.apache.isis.applib.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List;
import com.google.common.base.Function;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

/**
 * Specifies the positioning of an entity's (groups of) properties and of its collections, on a page, column by column.
 * 
 * <p>
 * The left column and middle column determine the ordering of the entity's (groups of) properties.  The
 * value of the {@link #left() left} list and {@link #middle() middle} list specify the order
 * of the property groups (inferred from each property's {@link MemberOrder#name() MemberOrder.name} attribute.
 * 
 * <p>
 * The right column is for the entity's collections.  The order of this collections is simply as
 * determined by the collection's {@link MemberOrder#sequence() MemberOrder.sequence} attribute
 * 
 * <p>
 * If both this annotation, {@link MemberGroupLayout}, and {@link MemberGroups} annotation
 * are present on an entity, then this one takes precedence.
 *
 * <p>
 *     The recommended alternative is to use the <code>Xxx.layout.xml</code> file, where <code>Xxx</code> is the domain object name.
 * </p>
 *
 * @deprecated
 */
@Deprecated
@Inherited
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface MemberGroupLayout {

    /**
     * The relative widths of the columns of members.
     */
    public static class ColumnSpans {
        
        private final int left;
        private final int middle;
        private final int right;
        private final int collections;
        
        public static ColumnSpans valueOf(String str) {
            final Iterable<String> split = Splitter.on(",").split(str);
            try {
                final List<Integer> list = Lists.newArrayList(Iterables.transform(split, new Function<String,Integer>() {
                    @Override
                    public Integer apply(String input) {
                        return Integer.parseInt(input);
                    }
                }));
                return asSpans(list);
            } catch(RuntimeException ex) {
                return null;
            }
        }
        public static ColumnSpans asSpans(int... columnSpans) {
            List<Integer> list = Lists.<Integer>newArrayList();
            for (int i : columnSpans) {
                list.add(i);
            }
            return asSpans(list);
        }
        private static ColumnSpans asSpans(List<Integer> list) {
            return new ColumnSpans(list);
        }
        private ColumnSpans(List<Integer> list) {
            this.left = getElse(list,0,4);
            this.middle = getElse(list,1,0);
            this.right = getElse(list,2,0);
            this.collections = getElse(list,3,8);
        }
        private static int getElse(List<Integer> list, int i, int dflt) {
            return list != null && list.size() > i? list.get(i): dflt;
        }

        public int getLeft() {
            return left;
        }
        public int getMiddle() {
            return middle;
        }
        public int getRight() {
            return right;
        }
        public int getCollections() {
            return collections;
        }
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + collections;
            result = prime * result + left;
            result = prime * result + middle;
            result = prime * result + right;
            return result;
        }
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            ColumnSpans other = (ColumnSpans) obj;
            if (collections != other.collections)
                return false;
            if (left != other.left)
                return false;
            if (middle != other.middle)
                return false;
            if (right != other.right)
                return false;
            return true;
        }
        public String name() {
            return String.format("[%d,%d,%d,%d]", left, middle, right, collections);
        }
        public boolean exceedsRow() {
            return getLeft() + getMiddle() + getRight() + getCollections() > 12;
        }
    }

    /**
     * Specify the spans of each of the columns.
     * 
     * <p>
     * The sum of the spans is always 12.
     */
    int[] columnSpans() default {4,0,0,8};

    /**
     * Order of groups of properties as they appear in the left-most column of a webpage,
     * grouped as they appear as the <tt>name</tt> attribute of the {@link MemberOrder} 
     * annotation.
     * 
     * <p>
     * The order in this list determines the order that the property groups will be rendered.  
     * By convention any {@link MemberOrder} that does not have a {@link MemberOrder#name() name} is considered
     * to be in the default group, whose name is hard-coded as <i>General</i>.
     * 
     * <p>
     * Equivalent to {@link MemberGroups#value()} annotation.
     */
    String[] left() default {};

    /**
     * As {@link #left()}, but for the middle column in a page.
     * 
     * <p>
     * If the value of this attribute is non-empty but the {@link #columnSpans()} specifies a zero size, then the
     * framework will not boot and will instead indicate a meta-model validation exception.
     */
    String[] middle() default {};

    /**
     * As {@link #right()}, but for the right column in a page.
     * 
     * <p>
     * If the value of this attribute is non-empty but the {@link #columnSpans()} specifies a zero size, then the
     * framework will not boot and will instead indicate a meta-model validation exception.
     */
    String[] right() default {};

}
