package online.view.model;

import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;

import core.config.Factory;
import online.view.ViewUtil;

/**
 * 表示用リスト
 *
 * @author Tadashi Nakayama
 */
final class ViewList implements List<Serializable>, Serializable {
	/** serialVersionUID */
	private static final long serialVersionUID = 1L;

	/** 値リストオブジェクト */
	private final Serializable list;
	/** サニタイズキャッシュ配列 */
	private final Serializable[] val;
	/** マッピング */
	private final String mapping;
	/** サニタイズ判断 */
	private final boolean sanitize;

	/**
	 * コンストラクタ
	 *
	 * @param arg0 表示用リスト
	 * @param arg1 マッピング
	 * @param arg2 サニタイズフラグ
	 */
	ViewList(final List<? extends Serializable> arg0,
			final String arg1, final boolean arg2) {
		this.list = Factory.cast(arg0);
		this.val = new Serializable[arg0.size()];
		this.mapping = arg1;
		this.sanitize = arg2;
	}

	/**
	 * リスト取得
	 *
	 * @return リスト
	 */
	private List<Serializable> getList() {
		return Factory.cast(this.list);
	}

	/**
	 * @see java.util.List#size()
	 */
	@Override
	public int size() {
		return getList().size();
	}

	/**
	 * @see java.util.List#clear()
	 */
	@Override
	public void clear() {
		throw new UnsupportedOperationException();
	}

	/**
	 * @see java.util.List#isEmpty()
	 */
	@Override
	public boolean isEmpty() {
		return getList().isEmpty();
	}

	/**
	 * @see java.util.List#toArray()
	 */
	@Override
	public Serializable[] toArray() {
		throw new UnsupportedOperationException();
	}

	/**
	 * @see java.util.List#get(int)
	 */
	@Override
	public Serializable get(final int index) {
		if (index < 0 || this.val.length <= index) {
			return null;
		}

		var obj = this.val[index];
		if (obj == null) {
			obj = getList().get(index);
			if (obj != null) {
				if (obj.getClass().isArray()) {
					obj = new ViewList(Arrays.asList(
						Serializable[].class.cast(obj)), this.mapping, this.sanitize);
				} else if (List.class.isInstance(obj)) {
					obj = new ViewList(Factory.cast(obj), this.mapping, this.sanitize);
				} else if (String.class.isInstance(obj)) {
					if (this.mapping != null) {
						obj = ViewUtil.sanitize(
							String.class.cast(obj), Charset.forName(this.mapping), this.sanitize);
					}
				}
				this.val[index] = obj;
			}
		}
		return obj;
	}

	/**
	 * @see java.util.List#remove(int)
	 */
	@Override
	public Serializable remove(final int index) {
		throw new UnsupportedOperationException();
	}

	/**
	 * @see java.util.List#add(int, java.lang.Object)
	 */
	@Override
	public void add(final int index, final Serializable element) {
		throw new UnsupportedOperationException();
	}

	/**
	 * @see java.util.List#indexOf(java.lang.Object)
	 */
	@Override
	public int indexOf(final Object o) {
		return getList().indexOf(Serializable.class.cast(o));
	}

	/**
	 * @see java.util.List#lastIndexOf(java.lang.Object)
	 */
	@Override
	public int lastIndexOf(final Object o) {
		return getList().lastIndexOf(Serializable.class.cast(o));
	}

	/**
	 * @see java.util.List#add(java.lang.Object)
	 */
	@Override
	public boolean add(final Serializable o) {
		throw new UnsupportedOperationException();
	}

	/**
	 * @see java.util.List#contains(java.lang.Object)
	 */
	@Override
	public boolean contains(final Object o) {
		return getList().contains(Serializable.class.cast(o));
	}

	/**
	 * @see java.util.List#remove(java.lang.Object)
	 */
	@Override
	public boolean remove(final Object o) {
		throw new UnsupportedOperationException();
	}

	/**
	 * @see java.util.List#addAll(int, java.util.Collection)
	 */
	@Override
	public boolean addAll(final int index, final Collection<? extends Serializable> c) {
		throw new UnsupportedOperationException();
	}

	/**
	 * @see java.util.List#addAll(java.util.Collection)
	 */
	@Override
	public boolean addAll(final Collection<? extends Serializable> c) {
		throw new UnsupportedOperationException();
	}

	/**
	 * @see java.util.List#containsAll(java.util.Collection)
	 */
	@Override
	public boolean containsAll(final Collection<?> c) {
		return getList().containsAll(Factory.<Collection<Serializable>>cast(c));
	}

	/**
	 * @see java.util.List#removeAll(java.util.Collection)
	 */
	@Override
	public boolean removeAll(final Collection<?> c) {
		throw new UnsupportedOperationException();
	}

	/**
	 * @see java.util.List#retainAll(java.util.Collection)
	 */
	@Override
	public boolean retainAll(final Collection<?> c) {
		throw new UnsupportedOperationException();
	}

	/**
	 * @see java.util.List#iterator()
	 */
	@Override
	public Iterator<Serializable> iterator() {
		return new ViewListIterator(this, 0);
	}

	/**
	 * @see java.util.List#subList(int, int)
	 */
	@Override
	public List<Serializable> subList(final int fromIndex, final int toIndex) {
		throw new UnsupportedOperationException();
	}

	/**
	 * @see java.util.List#listIterator()
	 */
	@Override
	public ListIterator<Serializable> listIterator() {
		return new ViewListIterator(this, 0);
	}

	/**
	 * @see java.util.List#listIterator(int)
	 */
	@Override
	public ListIterator<Serializable> listIterator(final int index) {
		return new ViewListIterator(this, index);
	}

	/**
	 * @see java.util.List#set(int, java.lang.Object)
	 */
	@Override
	public Serializable set(final int index, final Serializable element) {
		throw new UnsupportedOperationException();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public <T> T[] toArray(final T[] a) {
		throw new UnsupportedOperationException();
	}

	/**
	 * 内部イテレータ
	 *
	 * @author Tadashi Nakayama
	 */
	private static final class ViewListIterator implements ListIterator<Serializable> {
		/** 現在位置 */
		private int loc;
		/** インスタンス */
		private final List<Serializable> vl;

		/**
		 * コンストラクタ
		 *
		 * @param val インスタンス
		 * @param index 開始位置
		 */
		ViewListIterator(final List<Serializable> val, final int index) {
			this.vl = val;
			this.loc = index;
		}

		/**
		 * @see java.util.ListIterator#hasNext()
		 */
		@Override
		public boolean hasNext() {
			return 0 <= this.loc && this.loc < this.vl.size();
		}

		/**
		 * @see java.util.ListIterator#next()
		 */
		@Override
		public Serializable next() {
			if (this.vl.size() <= this.loc) {
				throw new NoSuchElementException();
			}

			return this.vl.get(this.loc++);
		}

		/**
		 * @see java.util.ListIterator#remove()
		 */
		@Override
		public void remove() {
			throw new UnsupportedOperationException();
		}

		/**
		 * @see java.util.ListIterator#hasPrevious()
		 */
		@Override
		public boolean hasPrevious() {
			return 0 < this.loc;
		}

		/**
		 * @see java.util.ListIterator#previous()
		 */
		@Override
		public Serializable previous() {
			if (this.loc <= 0) {
				throw new NoSuchElementException();
			}
			return this.vl.get(this.loc--);
		}

		/**
		 * @see java.util.ListIterator#nextIndex()
		 */
		@Override
		public int nextIndex() {
			return this.loc;
		}

		/**
		 * @see java.util.ListIterator#previousIndex()
		 */
		@Override
		public int previousIndex() {
			return this.loc - 1;
		}

		/**
		 * @see java.util.ListIterator#set(java.lang.Object)
		 */
		@Override
		public void set(final Serializable e) {
			throw new UnsupportedOperationException();
		}

		/**
		 * @see java.util.ListIterator#add(java.lang.Object)
		 */
		@Override
		public void add(final Serializable e) {
			throw new UnsupportedOperationException();
		}
	}
}
