/*
 * 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.wicket.examples.ajax.builtin;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.collections4.iterators.EmptyIterator;
import org.apache.commons.fileupload2.core.FileUploadException;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.extensions.ajax.AjaxFileDropBehavior;
import org.apache.wicket.extensions.ajax.markup.html.form.upload.UploadProgressBar;
import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackDefaultDataTable;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.form.upload.FileDescription;
import org.apache.wicket.markup.html.form.upload.FileUpload;
import org.apache.wicket.markup.html.form.upload.FileUploadField;
import org.apache.wicket.markup.html.form.upload.FilesSelectedBehavior;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.util.lang.Bytes;
import org.apache.wicket.validation.validator.StringValidator;

/**
 * Demos ajax handling of a multipart form
 *
 * @author igor.vaynberg
 */
public class FileUploadPage extends BasePage
{
	private static final long serialVersionUID = 1L;

	private static class SingleFileUploadSamplePanel extends Panel {

		private final FileUploadField file;
		private final TextField<String> text;
		private final Label selectedFileInfo;
		private final AjaxButton ajaxSubmit;
		private final Button submit;
		private String fileInfo;

		public SingleFileUploadSamplePanel(String id) {
			super(id);

			// create a feedback panel
			final Component feedback = new FeedbackPanel("feedback").setOutputMarkupId(true);
			add(feedback);

			// create the form
			final Form<?> form = new Form<Void>("form")
			{
				private static final long serialVersionUID = 1L;

				@Override
				protected void onSubmit()
				{
					// display uploaded info
					info("Text: " + text.getModelObject());
					FileUpload upload = file.getFileUpload();
					if (upload == null)
					{
						info("No file uploaded");
					}
					else
					{
						info("File-Name: " + upload.getClientFileName() + " File-Size: " +
								Bytes.bytes(upload.getSize()).toString());
					}
				}
			};
			form.setMaxSize(Bytes.megabytes(1));
			add(form);

			// create a textfield to demo non-file content
			form.add(text = new TextField<>("text", Model.of()));
			text.add(StringValidator.minimumLength(2));

			// create the file upload field
			form.add(file = new FileUploadField("file"));

			add(selectedFileInfo = new Label("selectedFileInfo", (IModel<String>) () -> fileInfo) {
				@Override
				protected void onAfterRender() {
					super.onAfterRender();
					fileInfo = null;
				}
			});
			selectedFileInfo.setOutputMarkupId(true);
			form.add(selectedFileInfo);

			form.add(new Label("max", form::getMaxSize));

			form.add(new UploadProgressBar("progress", form, file));

			// create a submit button
			form.add(submit = new Button("submit"));
			submit.setOutputMarkupId(true);

			// create the ajax button used to submit the form
			form.add(ajaxSubmit = new AjaxButton("ajaxSubmit")
			{
				private static final long serialVersionUID = 1L;

				/**
				 * Need to trigger submit to initiate progressbar.
				 */
				@Override
				protected boolean shouldTriggerJavaScriptSubmitEvent()
				{
					return true;
				}

				@Override
				protected void onSubmit(AjaxRequestTarget target)
				{
					info("This request was processed using AJAX");

					// ajax-update the feedback panel
					target.add(feedback);
				}

				@Override
				protected void onError(AjaxRequestTarget target)
				{
					// update feedback to display errors
					target.add(feedback);
				}

			});
			ajaxSubmit.setOutputMarkupId(true);

			file.add(FilesSelectedBehavior.onSelected(
					(AjaxRequestTarget target, List<FileDescription> fileDescriptions)->
					{
						FileDescription fileDescription = fileDescriptions.get(0);
						Bytes bytes = Bytes.bytes(fileDescription.getFileSize());
						fileInfo = "File " + fileDescription.getFileName() +
								" (with size " + bytes + ") was selected at client side. "
								+ "File was last modified at: " + fileDescription.getLastModified()
								+ " and is of type " + fileDescription.getMimeType() +
								". It has not been uploaded yet. ";
						if (bytes.greaterThan(form.getMaxSize()))
						{
							fileInfo += " File exceeds max allowed size.";
							// disable buttons as file is not valid
							submit.setEnabled(false);
							ajaxSubmit.setEnabled(false);
						}
						else
						{
							fileInfo += " You can click on buttons bellow in order to upload it.";
							// enable buttons as file is valid
							submit.setEnabled(true);
							ajaxSubmit.setEnabled(true);
						}
						target.add(selectedFileInfo, submit, ajaxSubmit);
					}));
			WebMarkupContainer drop = new WebMarkupContainer("drop");
			drop.add(new AjaxFileDropBehavior() {
				protected void onFileUpload(AjaxRequestTarget target, List<FileUpload> files) {

					// display uploaded info
					if (files == null || files.isEmpty())
					{
						info("No file uploaded");
					}
					else
					{
						for (FileUpload file : files) {
							info("File-Name: " + file.getClientFileName() + " File-Size: " +
									Bytes.bytes(file.getSize()).toString());
						}
					}

					target.add(feedback);
				}

				@Override
				protected void onError(AjaxRequestTarget target, FileUploadException fux)
				{
					info(fux.getMessage());

					target.add(feedback);
				}
			});
			add(drop);
		}
	}

	private static class MultipleFileUploadsSamplePanel extends Panel {

		private static class DataProvider extends SortableDataProvider<FileDescription, String> {

			private List<FileDescription> fileDescriptions;

			public void setFileDescriptions(List<FileDescription> fileDescriptions) {
				this.fileDescriptions = new ArrayList<>(fileDescriptions);
			}

			@Override
			public Iterator<? extends FileDescription> iterator(long first, long count) {
				if (this.fileDescriptions == null) {
					return EmptyIterator.emptyIterator();
				}
				return fileDescriptions.listIterator();
			}

			@Override
			public long size() {
				if (this.fileDescriptions == null) {
					return 0L;
				}
				return this.fileDescriptions.size();
			}

			@Override
			public IModel<FileDescription> model(FileDescription object) {
				return Model.of(object);
			}

			@Override
			public void detach() {
				super.detach();
				this.fileDescriptions = null;
			}
		}

		private final FileUploadField file;
		private final TextField<String> text;
		private DataProvider dataProvider;
		private AjaxFallbackDefaultDataTable<FileDescription, String> selectedFileInfo;
		private final AjaxButton ajaxSubmit;
		private final Button submit;

		public MultipleFileUploadsSamplePanel(String id) {
			super(id);

			// create a feedback panel
			final Component feedback = new FeedbackPanel("feedback").setOutputMarkupId(true);
			add(feedback);

			// create the form
			final Form<?> form = new Form<Void>("form")
			{
				private static final long serialVersionUID = 1L;

				@Override
				protected void onSubmit()
				{
					// display uploaded info
					info("Text: " + text.getModelObject());
					FileUpload upload = file.getFileUpload();
					if (upload == null)
					{
						info("No file uploaded");
					}
					else
					{
						info("File-Name: " + upload.getClientFileName() + " File-Size: " +
								Bytes.bytes(upload.getSize()).toString());
					}
				}
			};
			form.setMaxSize(Bytes.megabytes(1));
			add(form);

			List<IColumn<FileDescription, String>> columns = new ArrayList<>();
			columns.add(new PropertyColumn<>(Model.of("File Name"), "fileName"));
			columns.add(new PropertyColumn<>(Model.of("Size"), "fileSize"));
			columns.add(new PropertyColumn<>(Model.of("Last Modified"), "lastModified"));
			columns.add(new PropertyColumn<>(Model.of("MIME Type"), "mimeType"));
			selectedFileInfo = new AjaxFallbackDefaultDataTable<>("selectedFileInfo", columns, dataProvider = new DataProvider(), 100) {
				@Override
				protected void onConfigure() {
					super.onConfigure();
					setVisible(dataProvider.size() > 0);
				}
			};
			form.add(selectedFileInfo);
			selectedFileInfo.setOutputMarkupPlaceholderTag(true);

			// create a textfield to demo non-file content
			form.add(text = new TextField<>("text", Model.of()));
			text.add(StringValidator.minimumLength(2));

			// create the file upload field
			form.add(file = new FileUploadField("file"));

			form.add(new Label("max", form::getMaxSize));

			form.add(new UploadProgressBar("progress", form, file));

			// create a submit button
			form.add(submit = new Button("submit"));
			submit.setOutputMarkupId(true);

			// create the ajax button used to submit the form
			form.add(ajaxSubmit = new AjaxButton("ajaxSubmit")
			{
				private static final long serialVersionUID = 1L;

				/**
				 * Need to trigger submit to initiate progressbar.
				 */
				@Override
				protected boolean shouldTriggerJavaScriptSubmitEvent()
				{
					return true;
				}

				@Override
				protected void onSubmit(AjaxRequestTarget target)
				{
					info("This request was processed using AJAX");

					// ajax-update the feedback panel
					target.add(feedback);
				}

				@Override
				protected void onError(AjaxRequestTarget target)
				{
					// update feedback to display errors
					target.add(feedback);
				}

			});
			ajaxSubmit.setOutputMarkupId(true);
			file.add(FilesSelectedBehavior.onSelected(
					(AjaxRequestTarget target, List<FileDescription> fileDescriptions) ->
			{
				dataProvider.setFileDescriptions(fileDescriptions);
				Bytes bytes = Bytes.bytes(fileDescriptions.stream().mapToLong(FileDescription::getFileSize).sum());
				if (bytes.greaterThan(form.getMaxSize()))
				{
					form.error("Total file size exceeds max allowed size.");
					// disable buttons as file is not valid
					submit.setEnabled(false);
					ajaxSubmit.setEnabled(false);
				}
				else
				{
					form.info("You can click on buttons bellow in order to upload selected files.");
					// enable buttons as file is valid
					submit.setEnabled(true);
					ajaxSubmit.setEnabled(true);
				}
				target.add(selectedFileInfo, submit, ajaxSubmit, feedback);
			}));
		}
	}
	/**
	 * Constructor
	 */
	public FileUploadPage()
	{
		// sample of a single uploaded file.
		add(new SingleFileUploadSamplePanel("singleFileUpload"));
		add(new MultipleFileUploadsSamplePanel("multipleFileUpload"));


	}
}
