/*
 * Copyright 2014 gitblit.com.
 *
 * Licensed 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 com.gitblit.wicket.panels;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Radio;
import org.apache.wicket.markup.html.form.RadioGroup;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;

import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.AuthorizationControl;
import com.gitblit.Keys;
import com.gitblit.models.RepositoryModel;
import com.gitblit.wicket.WicketUtils;

/**
 * A radio group panel of the 5 available authorization/access restriction combinations.
 *
 * @author James Moger
 *
 */
public class AccessPolicyPanel extends BasePanel {

	private static final long serialVersionUID = 1L;

	private final RepositoryModel repository;

	private final AjaxFormChoiceComponentUpdatingBehavior callback;

	private RadioGroup<AccessPolicy> policiesGroup;

	public AccessPolicyPanel(String wicketId, RepositoryModel repository) {
		this(wicketId, repository, null);
	}

	public AccessPolicyPanel(String wicketId, RepositoryModel repository, AjaxFormChoiceComponentUpdatingBehavior callback) {
		super(wicketId);
		this.repository = repository;
		this.callback = callback;
	}

	@Override
	protected void onInitialize() {
		super.onInitialize();

		AccessPolicy anonymousPolicy = new AccessPolicy(getString("gb.anonymousPolicy"),
				getString("gb.anonymousPolicyDescription"),
				"blank.png",
				AuthorizationControl.AUTHENTICATED,
				AccessRestrictionType.NONE);

		AccessPolicy authenticatedPushPolicy = new AccessPolicy(getString("gb.authenticatedPushPolicy"),
				getString("gb.authenticatedPushPolicyDescription"),
				"lock_go_16x16.png",
				AuthorizationControl.AUTHENTICATED,
				AccessRestrictionType.PUSH);

		AccessPolicy namedPushPolicy = new AccessPolicy(getString("gb.namedPushPolicy"),
				getString("gb.namedPushPolicyDescription"),
				"lock_go_16x16.png",
				AuthorizationControl.NAMED,
				AccessRestrictionType.PUSH);

		AccessPolicy clonePolicy = new AccessPolicy(getString("gb.clonePolicy"),
				getString("gb.clonePolicyDescription"),
				"lock_pull_16x16.png",
				AuthorizationControl.NAMED,
				AccessRestrictionType.CLONE);

		AccessPolicy viewPolicy = new AccessPolicy(getString("gb.viewPolicy"),
				getString("gb.viewPolicyDescription"),
				"shield_16x16.png",
				AuthorizationControl.NAMED,
				AccessRestrictionType.VIEW);

		List<AccessPolicy> policies = new ArrayList<AccessPolicy>();
		if (app().settings().getBoolean(Keys.git.allowAnonymousPushes, false)) {
			policies.add(anonymousPolicy);
		}
		policies.add(authenticatedPushPolicy);
		policies.add(namedPushPolicy);
		policies.add(clonePolicy);
		policies.add(viewPolicy);

		AccessRestrictionType defaultRestriction = repository.accessRestriction;
		if (defaultRestriction == null) {
			defaultRestriction = AccessRestrictionType.fromName(app().settings().getString(Keys.git.defaultAccessRestriction,
					AccessRestrictionType.PUSH.name()));
		}

		AuthorizationControl defaultControl = repository.authorizationControl;
		if (defaultControl == null) {
			defaultControl = AuthorizationControl.fromName(app().settings().getString(Keys.git.defaultAuthorizationControl,
					AuthorizationControl.NAMED.name()));
		}

		AccessPolicy defaultPolicy = namedPushPolicy;
		for (AccessPolicy policy : policies) {
			if (policy.type == defaultRestriction && policy.control == defaultControl) {
				defaultPolicy = policy;
			}
		}

		policiesGroup = new RadioGroup<>("policiesGroup", new Model<AccessPolicy>(defaultPolicy));
		ListView<AccessPolicy> policiesList = new ListView<AccessPolicy>("policies", policies) {

			private static final long serialVersionUID = 1L;

			@Override
			protected void populateItem(ListItem<AccessPolicy> item) {
				AccessPolicy p = item.getModelObject();
				item.add(new Radio<AccessPolicy>("radio", item.getModel()));
				item.add(WicketUtils.newImage("image",  p.image));
				item.add(new Label("name", p.name));
				item.add(new Label("description", p.description));
			}
		};
		policiesGroup.add(policiesList);
		if (callback != null) {
			policiesGroup.add(callback);
			policiesGroup.setOutputMarkupId(true);
		}
		add(policiesGroup);

		if (app().settings().getBoolean(Keys.web.allowForking, true)) {
			Fragment fragment = new Fragment("allowForks", "allowForksFragment", this);
			fragment.add(new BooleanOption("allowForks",
				getString("gb.allowForks"),
				getString("gb.allowForksDescription"),
				new PropertyModel<Boolean>(repository, "allowForks")));
			add(fragment);
		} else {
			add(new Label("allowForks").setVisible(false));
		}

		setOutputMarkupId(true);
	}

	public void updateModel(RepositoryModel repository) {
		AccessPolicy policy = policiesGroup.getModelObject();
		repository.authorizationControl = policy.control;
		repository.accessRestriction = policy.type;
	}

	@Override
	protected boolean getStatelessHint() {
		return false;
	}

	public static class AccessPolicy implements Serializable {

		private static final long serialVersionUID = 1L;

		final String name;
		final String description;
		final String image;
		final AuthorizationControl control;
		final AccessRestrictionType type;

		AccessPolicy(String name, String description, String img, AuthorizationControl control, AccessRestrictionType type) {
			this.name = name;
			this.description = description;
			this.image = img;
			this.control = control;
			this.type = type;
		}

		@Override
		public String toString() {
			return name;
		}
	}
}
