/*
 * Copyright 2011 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.pages;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.wicket.PageParameters;
import org.apache.wicket.extensions.markup.html.form.palette.Palette;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.ChoiceRenderer;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.util.CollectionModel;
import org.apache.wicket.model.util.ListModel;

import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.FederationStrategy;
import com.gitblit.GitBlit;
import com.gitblit.GitBlitException;
import com.gitblit.Keys;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.WicketUtils;

public class EditRepositoryPage extends RootSubPage {

	private final boolean isCreate;

	private boolean isAdmin;

	public EditRepositoryPage() {
		// create constructor
		super();
		isCreate = true;
		setupPage(new RepositoryModel());
	}

	public EditRepositoryPage(PageParameters params) {
		// edit constructor
		super(params);
		isCreate = false;
		String name = WicketUtils.getRepositoryName(params);
		RepositoryModel model = GitBlit.self().getRepositoryModel(name);
		setupPage(model);
	}

	protected void setupPage(final RepositoryModel repositoryModel) {
		// ensure this user can create or edit this repository
		checkPermissions(repositoryModel);

		List<String> federationSets = new ArrayList<String>();
		List<String> repositoryUsers = new ArrayList<String>();
		if (isCreate) {
			super.setupPage(getString("gb.newRepository"), "");
		} else {
			super.setupPage(getString("gb.edit"), repositoryModel.name);
			if (repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE)) {
				repositoryUsers.addAll(GitBlit.self().getRepositoryUsers(repositoryModel));
				Collections.sort(repositoryUsers);
			}
			federationSets.addAll(repositoryModel.federationSets);
		}		
		

		final String oldName = repositoryModel.name;
		// users palette
		final Palette<String> usersPalette = new Palette<String>("users", new ListModel<String>(
				repositoryUsers), new CollectionModel<String>(GitBlit.self().getAllUsernames()),
				new ChoiceRenderer<String>("", ""), 10, false);

		// federation sets palette
		List<String> sets = GitBlit.getStrings(Keys.federation.sets);
		final Palette<String> federationSetsPalette = new Palette<String>("federationSets",
				new ListModel<String>(federationSets), new CollectionModel<String>(sets),
				new ChoiceRenderer<String>("", ""), 10, false);

		CompoundPropertyModel<RepositoryModel> model = new CompoundPropertyModel<RepositoryModel>(
				repositoryModel);
		Form<RepositoryModel> form = new Form<RepositoryModel>("editForm", model) {

			private static final long serialVersionUID = 1L;

			@Override
			protected void onSubmit() {
				try {
					// confirm a repository name was entered
					if (StringUtils.isEmpty(repositoryModel.name)) {
						error("Please set repository name!");
						return;
					}

					// automatically convert backslashes to forward slashes
					repositoryModel.name = repositoryModel.name.replace('\\', '/');
					// Automatically replace // with /
					repositoryModel.name = repositoryModel.name.replace("//", "/");

					// prohibit folder paths
					if (repositoryModel.name.startsWith("/")) {
						error("Leading root folder references (/) are prohibited.");
						return;
					}
					if (repositoryModel.name.startsWith("../")) {
						error("Relative folder references (../) are prohibited.");
						return;
					}
					if (repositoryModel.name.contains("/../")) {
						error("Relative folder references (../) are prohibited.");
						return;
					}

					// confirm valid characters in repository name
					Character c = StringUtils.findInvalidCharacter(repositoryModel.name);
					if (c != null) {
						error(MessageFormat.format("Illegal character ''{0}'' in repository name!",
								c));
						return;
					}

					// confirm access restriction selection
					if (repositoryModel.accessRestriction == null) {
						error("Please select access restriction!");
						return;
					}

					// confirm federation strategy selection
					if (repositoryModel.federationStrategy == null) {
						error("Please select federation strategy!");
						return;
					}

					// save federation set preferences
					if (repositoryModel.federationStrategy.exceeds(FederationStrategy.EXCLUDE)) {
						repositoryModel.federationSets.clear();
						Iterator<String> sets = federationSetsPalette.getSelectedChoices();
						while (sets.hasNext()) {
							repositoryModel.federationSets.add(sets.next());
						}
					}

					// save the repository
					GitBlit.self().updateRepositoryModel(oldName, repositoryModel, isCreate);

					// save the repository access list
					if (repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE)) {
						Iterator<String> users = usersPalette.getSelectedChoices();
						List<String> repositoryUsers = new ArrayList<String>();
						while (users.hasNext()) {
							repositoryUsers.add(users.next());
						}
						// ensure the owner is added to the user list
						if (repositoryModel.owner != null
								&& !repositoryUsers.contains(repositoryModel.owner)) {
							repositoryUsers.add(repositoryModel.owner);
						}
						GitBlit.self().setRepositoryUsers(repositoryModel, repositoryUsers);
					}
				} catch (GitBlitException e) {
					error(e.getMessage());
					return;
				}
				setRedirect(false);
				setResponsePage(RepositoriesPage.class);
			}
		};

		// field names reflective match RepositoryModel fields
		form.add(new TextField<String>("name").setEnabled(isCreate || isAdmin));
		form.add(new TextField<String>("description"));
		form.add(new DropDownChoice<String>("owner", GitBlit.self().getAllUsernames())
				.setEnabled(GitBlitWebSession.get().canAdmin()));
		form.add(new DropDownChoice<AccessRestrictionType>("accessRestriction", Arrays
				.asList(AccessRestrictionType.values()), new AccessRestrictionRenderer()));
		form.add(new CheckBox("isFrozen"));
		// TODO enable origin definition
		form.add(new TextField<String>("origin").setEnabled(false/* isCreate */));

		// federation strategies - remove ORIGIN choice if this repository has
		// no origin.
		List<FederationStrategy> federationStrategies = new ArrayList<FederationStrategy>(
				Arrays.asList(FederationStrategy.values()));
		if (StringUtils.isEmpty(repositoryModel.origin)) {
			federationStrategies.remove(FederationStrategy.FEDERATE_ORIGIN);
		}
		form.add(new DropDownChoice<FederationStrategy>("federationStrategy", federationStrategies,
				new FederationTypeRenderer()));
		form.add(new CheckBox("useTickets"));
		form.add(new CheckBox("useDocs"));
		form.add(new CheckBox("showRemoteBranches"));
		form.add(new CheckBox("showReadme"));
		form.add(new CheckBox("skipSizeCalculation"));
		form.add(new CheckBox("skipSummaryMetrics"));
		form.add(usersPalette);
		form.add(federationSetsPalette);

		form.add(new Button("save"));
		Button cancel = new Button("cancel") {
			private static final long serialVersionUID = 1L;

			@Override
			public void onSubmit() {
				setResponsePage(RepositoriesPage.class);
			}
		};
		cancel.setDefaultFormProcessing(false);
		form.add(cancel);

		add(form);
	}

	/**
	 * Unfortunately must repeat part of AuthorizaitonStrategy here because that
	 * mechanism does not take PageParameters into consideration, only page
	 * instantiation.
	 * 
	 * Repository Owners should be able to edit their repository.
	 */
	private void checkPermissions(RepositoryModel model) {
		boolean authenticateAdmin = GitBlit.getBoolean(Keys.web.authenticateAdminPages, true);
		boolean allowAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, true);

		GitBlitWebSession session = GitBlitWebSession.get();
		UserModel user = session.getUser();

		if (allowAdmin) {
			if (authenticateAdmin) {
				if (user == null) {
					// No Login Available
					error("Administration requires a login", true);
				}
				if (isCreate) {
					// Create Repository
					if (!user.canAdmin) {
						// Only Administrators May Create
						error("Only an administrator may create a repository", true);
					}
				} else {
					// Edit Repository
					if (user.canAdmin) {
						// Admins can edit everything
						isAdmin = true;
						return;
					} else {
						if (!model.owner.equalsIgnoreCase(user.username)) {
							// User is not an Admin nor Owner
							error("Only an administrator or the owner may edit a repository", true);
						}
					}
				}
			}
		} else {
			// No Administration Permitted
			error("Administration is disabled", true);
		}
	}

	private class AccessRestrictionRenderer implements IChoiceRenderer<AccessRestrictionType> {

		private static final long serialVersionUID = 1L;

		private final Map<AccessRestrictionType, String> map;

		public AccessRestrictionRenderer() {
			map = getAccessRestrictions();
		}

		@Override
		public String getDisplayValue(AccessRestrictionType type) {
			return map.get(type);
		}

		@Override
		public String getIdValue(AccessRestrictionType type, int index) {
			return Integer.toString(index);
		}
	}

	private class FederationTypeRenderer implements IChoiceRenderer<FederationStrategy> {

		private static final long serialVersionUID = 1L;

		private final Map<FederationStrategy, String> map;

		public FederationTypeRenderer() {
			map = getFederationTypes();
		}

		@Override
		public String getDisplayValue(FederationStrategy type) {
			return map.get(type);
		}

		@Override
		public String getIdValue(FederationStrategy type, int index) {
			return Integer.toString(index);
		}
	}
}
